-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathgtk_filemon.c
348 lines (296 loc) · 11.2 KB
/
gtk_filemon.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
#include "gtk_filemon.h"
/* prototype from filebuf.c */
void gtkwrite_window_set_title (GtkWidget *widget, kwinst *app);
void buffer_save_file (kwinst *app, gchar *filename);
void file_get_stats (const gchar *filename, kwinst *app);
/* prototype for infobar */
void ib_foreign_mod_response (GtkInfoBar *bar, gint response_id, kwinst *app);
void ibar_foreign_mod (kwinst *app);
void buffer_reload_file (kwinst *app)
{
/* clear exising buffer, insert saved file, set modified to FALSE
* set title.
*/
gtk_text_buffer_set_text (GTK_TEXT_BUFFER(app->buffer), "", -1);
/* insert saved file into buffer */
buffer_insert_file (app, NULL);
gtk_text_buffer_set_modified (GTK_TEXT_BUFFER(app->buffer), FALSE);
gtkwrite_window_set_title (NULL, app);
}
/* For changes, rename in same dir (mv) and restore after delete,
* move and delete monitoring:
* G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
* is all that is required, as that is generated after each individual event
* takes place. Attribute changes are ignored.
* normal save operations are ignored by setting app->mfp_savecmd flag
* to TRUE on save. The flag is reset FALSE here on
* G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT.
* update file stat information on attribute change.
*/
void file_monitor_on_changed (GFileMonitor *mon,
GFile *file, GFile *other,
GFileMonitorEvent evtype,
gpointer data)
{
kwinst *app = (kwinst *)data;
#ifdef DEBUGFM
g_print ("Monitor Event: File = %s\n", g_file_get_parse_name (file));
#endif
switch (evtype)
{
case G_FILE_MONITOR_EVENT_CHANGED:
/* will fire CHANGES_DONE_HINT */
#ifdef DEBUGFM
g_print ("G_FILE_MONITOR_EVENT_CHANGED\n");
#endif
break;
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
#ifdef DEBUGFM
/* monitor for mod or create after restored from delete */
g_print ("G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT\n");
#endif
/* app->mfp_savecmd is set TRUE for normal save and reset here.
* in the event of a foreign modification after initiating save,
* the events will be triggered again after reset occurs.
*/
if (app->mfp_savecmd) { /* ignore normal saves, reset flag */
#ifdef DEBUGFM
g_print (" mfp_savecmd TRUE, normal save, resetting...\n");
#endif
app->mfp_savecmd = FALSE;
return;
}
/* set modified state and update window title */
gtk_text_buffer_set_modified (GTK_TEXT_BUFFER(app->buffer), TRUE);
gtkwrite_window_set_title (NULL, app);
/* prompt to overwrite or reload */
#ifndef USEFMONDLG
ibar_foreign_mod (app);
#else
if (dlg_yes_no_msg (app, "Current file modified by a foreign process\n\n"
"Save File Overwriting Foreign Change? -- Yes\n\n"
"Reload File Incorporating Foreign Change? -- NO",
"File Modified by Foreign Process",
TRUE))
buffer_save_file (app, NULL);
else
buffer_reload_file (app);
#endif
/* set unmodified state and update window title */
gtk_text_buffer_set_modified (GTK_TEXT_BUFFER(app->buffer), FALSE);
gtkwrite_window_set_title (NULL, app);
break;
case G_FILE_MONITOR_EVENT_DELETED:
/* monitor for file mv or delete */
/* Also fires G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT */
#ifdef DEBUGFM
g_print ("G_FILE_MONITOR_EVENT_DELETED\n");
#endif
if (app->mfp_savecmd)
return;
/* set modified state and update window title */
// gtk_text_buffer_set_modified (GTK_TEXT_BUFFER(app->buffer), TRUE);
// gtkwrite_window_set_title (NULL, app);
/* save into existing filename creating new file
* TODO: update dialog to present 'Save/Save As' or 'Ignore' options.
* on ignore, must set unmodified and update window title, handled by
* Save/Save As otherwise.
*/
// dlg_info_win (app, "Current file deleted by a foreign process\n\n"
// "Save File Under Original or New Filename.",
// "File Deleted by Foreign Process");
break;
case G_FILE_MONITOR_EVENT_CREATED:
/* will fire CHANGES_DONE_HINT */
#ifdef DEBUGFM
g_print ("G_FILE_MONITOR_EVENT_CREATED\n");
#endif
break;
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
/* update attributes */
file_get_stats (app->filename, app);
#ifdef DEBUGFM
g_print ("G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED\n");
#endif
break;
case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
#ifdef DEBUGFM
g_print ("G_FILE_MONITOR_EVENT_PRE_UNMOUNT\n");
#endif
break;
case G_FILE_MONITOR_EVENT_UNMOUNTED:
#ifdef DEBUGFM
g_print ("G_FILE_MONITOR_EVENT_UNMOUNTED\n");
#endif
break;
case G_FILE_MONITOR_EVENT_MOVED:
#ifdef DEBUGFM
g_print ("G_FILE_MONITOR_EVENT_MOVED\n");
#endif
break;
#ifdef GLIB246
case G_FILE_MONITOR_EVENT_RENAMED:
#ifdef DEBUGFM
g_print ("G_FILE_MONITOR_EVENT_RENAMED\n");
#endif
break;
case G_FILE_MONITOR_EVENT_MOVED_IN:
#ifdef DEBUGFM
g_print ("G_FILE_MONITOR_EVENT_MOVED_IN\n");
#endif
break;
case G_FILE_MONITOR_EVENT_MOVED_OUT:
#ifdef DEBUGFM
g_print ("G_FILE_MONITOR_EVENT_MOVED_OUT\n");
#endif
break;
#endif
default:
#ifdef DEBUGFM
g_print ("unknown EVENT on changed signal.\n");
#endif
break;
}
if (mon || file || other || data) {}
}
GFileMonitor *file_monitor_add (gpointer data)
{
kwinst *app = (kwinst *)data;
GFile *gfile = g_file_new_for_path (app->filename);
GCancellable *cancellable = g_cancellable_new();
GError *err=NULL;
if (app->filemon) {
/* handle error
* remove monitor, then add under new filename
*/
// return NULL;
file_monitor_cancel (data);
}
/* create monitor for app->filename */
app->filemon = g_file_monitor_file (gfile,
G_FILE_MONITOR_NONE,
cancellable, &err);
if (!app->filemon) {
err_dialog (err->message);
g_error_free (err);
g_object_unref (gfile);
return NULL;
}
/* store name of file being monitored */
app->filemonfn = g_strdup (app->filename);
/* connect changed signal to monitored file saving ID */
app->mfp_handler = g_signal_connect (G_OBJECT(app->filemon), "changed",
G_CALLBACK (file_monitor_on_changed), data);
#ifdef DEBUGFM
g_print ("adding app->mfp_handler: %lu\n", app->mfp_handler);
#endif
g_object_unref (gfile);
return app->filemon;
}
gboolean file_monitor_cancel (gpointer data)
{
kwinst *app = (kwinst *)data;
if (!app->filemon) return FALSE;
#ifdef DEBUGFM
g_print ("cancelling app->mfp_handler: %lu\n", app->mfp_handler);
#endif
if (app->mfp_handler) {
g_signal_handler_disconnect (G_OBJECT(app->filemon), app->mfp_handler);
app->mfp_handler = 0;
}
if (g_file_monitor_cancel (app->filemon)) {
g_free (app->filemonfn);
g_object_unref (app->filemon);
app->filemonfn = NULL;
app->filemon = NULL;
}
return TRUE;
}
/* block firing of changed signal */
void file_monitor_block_changed (gpointer data)
{
kwinst *app = (kwinst *)data;
if (!app->filemon || !app->mfp_handler) return;
g_print ("blocking changed (%lu)\n", app->mfp_handler);
g_signal_handler_block (app->filemon, app->mfp_handler);
}
/* unblock firing of changed signal */
void file_monitor_unblock_changed (gpointer data)
{
kwinst *app = (kwinst *)data;
if (!app->filemon || !app->mfp_handler) return;
g_print ("unblocking changed (%lu)\n", app->mfp_handler);
g_signal_handler_unblock (app->filemon, app->mfp_handler);
}
/** callback for file monitored by foreign process infobar */
void ib_foreign_mod_response (GtkInfoBar *bar, gint response_id, kwinst *app)
{
switch (response_id) {
case GTK_RESPONSE_NO:
#ifdef DEBUGFM
g_print ("ib_response: GTK_RESPONSE_NO (overwrite data on disk)\n");
#endif
buffer_save_file (app, NULL);
break;
case GTK_RESPONSE_YES:
#ifdef DEBUGFM
g_print ("ib_response: GTK_RESPONSE_YES (reload file with changes)\n");
#endif
buffer_reload_file (app);
break;
case GTK_RESPONSE_CANCEL:
#ifdef DEBUGFM
g_print ("ib_response: GTK_RESPONSE_CANCEL (File Save-As)\n");
#endif
break;
}
gtk_widget_hide (GTK_WIDGET(bar));
// set text_view sensitive TRUE
if (!gtk_widget_get_sensitive (app->view))
gtk_widget_set_sensitive (app->view, TRUE);
// grab focus for textview
gtk_widget_grab_focus (app->view);
// reset flags
app->ibflags = 0;
}
/** file monitored by foreign process infobar */
void ibar_foreign_mod (kwinst *app)
{
gchar *msg;
ibbtndef btndef[] = { { .btntext = "_Reload File", .response_id = GTK_RESPONSE_YES },
{ .btntext = "_Overwite Changes", .response_id = GTK_RESPONSE_NO },
{ .btntext = "_Cancel", .response_id = GTK_RESPONSE_CANCEL },
{ .btntext = "", .response_id = 0 } };
if (app->ibflags & IBAR_VISIBLE) /* prevent duplicate infobars */
return;
app->ibflags |= IBAR_VIEW_SENSITIVE; /* use |= to preserve other flags */
msg = g_markup_printf_escaped ("<span font_weight=\"bold\" font_size='large'>"
"Current file modified by a foreign process\n\nReload File:</span> "
"incorporating Foreign Changes, losing any unsaved changes?\n<span "
"font_weight=\"bold\">Overwrite Changes:</span> continue without "
"reloading, foreign changes will be lost on save.\n"
"<span font_weight=\"bold\">\"Cancel:\"</span> and then choose"
"<span font_weight=\"bold\">\"File Save-As\"</span> to save "
"current buffer under a new file name.\n(foreign change "
"will be preserved in original filename)");
show_info_bar_choice (msg, GTK_MESSAGE_WARNING, btndef,
ib_foreign_mod_response, app);
g_free (msg);
}
void buffer_file_insert_dlg (kwinst *app, gchar *filename)
{
GtkWidget *dialog;
/* Create a new file chooser widget */
dialog = gtk_file_chooser_dialog_new ("Select a file for editing",
// parent_window,
GTK_WINDOW (app->window),
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
NULL);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
buffer_insert_file (app, filename);
}
gtk_widget_destroy (dialog);
}