1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2
3 Copyright (C) 1999, 2000, 2001 Eazel, Inc.
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public
16 License along with this program; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19
20 Author: Pavel Cisler <pavel@eazel.com>
21 */
22
23 #include <config.h>
24 #include "caja-file-changes-queue.h"
25
26 #include "caja-directory-notify.h"
27
28 typedef enum
29 {
30 CHANGE_FILE_INITIAL,
31 CHANGE_FILE_ADDED,
32 CHANGE_FILE_CHANGED,
33 CHANGE_FILE_REMOVED,
34 CHANGE_FILE_MOVED,
35 CHANGE_POSITION_SET,
36 CHANGE_POSITION_REMOVE
37 } CajaFileChangeKind;
38
39 typedef struct
40 {
41 CajaFileChangeKind kind;
42 GFile *from;
43 GFile *to;
44 GdkPoint point;
45 int screen;
46 } CajaFileChange;
47
48 typedef struct
49 {
50 GList *head;
51 GList *tail;
52 GMutex mutex;
53 } CajaFileChangesQueue;
54
55 static CajaFileChangesQueue *
caja_file_changes_queue_new(void)56 caja_file_changes_queue_new (void)
57 {
58 CajaFileChangesQueue *result;
59
60 result = g_new0 (CajaFileChangesQueue, 1);
61
62 g_mutex_init (&result->mutex);
63
64 return result;
65 }
66
67 static CajaFileChangesQueue *
caja_file_changes_queue_get(void)68 caja_file_changes_queue_get (void)
69 {
70 static CajaFileChangesQueue *file_changes_queue;
71
72 if (file_changes_queue == NULL)
73 {
74 file_changes_queue = caja_file_changes_queue_new ();
75 }
76
77 return file_changes_queue;
78 }
79
80 static void
caja_file_changes_queue_add_common(CajaFileChangesQueue * queue,CajaFileChange * new_item)81 caja_file_changes_queue_add_common (CajaFileChangesQueue *queue,
82 CajaFileChange *new_item)
83 {
84 /* enqueue the new queue item while locking down the list */
85 g_mutex_lock (&queue->mutex);
86
87 queue->head = g_list_prepend (queue->head, new_item);
88 if (queue->tail == NULL)
89 queue->tail = queue->head;
90
91 g_mutex_unlock (&queue->mutex);
92 }
93
94 void
caja_file_changes_queue_file_added(GFile * location)95 caja_file_changes_queue_file_added (GFile *location)
96 {
97 CajaFileChange *new_item;
98 CajaFileChangesQueue *queue;
99
100 queue = caja_file_changes_queue_get();
101
102 new_item = g_new0 (CajaFileChange, 1);
103 new_item->kind = CHANGE_FILE_ADDED;
104 new_item->from = g_object_ref (location);
105 caja_file_changes_queue_add_common (queue, new_item);
106 }
107
108 void
caja_file_changes_queue_file_changed(GFile * location)109 caja_file_changes_queue_file_changed (GFile *location)
110 {
111 CajaFileChange *new_item;
112 CajaFileChangesQueue *queue;
113
114 queue = caja_file_changes_queue_get();
115
116 new_item = g_new0 (CajaFileChange, 1);
117 new_item->kind = CHANGE_FILE_CHANGED;
118 new_item->from = g_object_ref (location);
119 caja_file_changes_queue_add_common (queue, new_item);
120 }
121
122 void
caja_file_changes_queue_file_removed(GFile * location)123 caja_file_changes_queue_file_removed (GFile *location)
124 {
125 CajaFileChange *new_item;
126 CajaFileChangesQueue *queue;
127
128 queue = caja_file_changes_queue_get();
129
130 new_item = g_new0 (CajaFileChange, 1);
131 new_item->kind = CHANGE_FILE_REMOVED;
132 new_item->from = g_object_ref (location);
133 caja_file_changes_queue_add_common (queue, new_item);
134 }
135
136 void
caja_file_changes_queue_file_moved(GFile * from,GFile * to)137 caja_file_changes_queue_file_moved (GFile *from,
138 GFile *to)
139 {
140 CajaFileChange *new_item;
141 CajaFileChangesQueue *queue;
142
143 queue = caja_file_changes_queue_get ();
144
145 new_item = g_new (CajaFileChange, 1);
146 new_item->kind = CHANGE_FILE_MOVED;
147 new_item->from = g_object_ref (from);
148 new_item->to = g_object_ref (to);
149 caja_file_changes_queue_add_common (queue, new_item);
150 }
151
152 void
caja_file_changes_queue_schedule_position_set(GFile * location,GdkPoint point,int screen)153 caja_file_changes_queue_schedule_position_set (GFile *location,
154 GdkPoint point,
155 int screen)
156 {
157 CajaFileChange *new_item;
158 CajaFileChangesQueue *queue;
159
160 queue = caja_file_changes_queue_get ();
161
162 new_item = g_new (CajaFileChange, 1);
163 new_item->kind = CHANGE_POSITION_SET;
164 new_item->from = g_object_ref (location);
165 new_item->point = point;
166 new_item->screen = screen;
167 caja_file_changes_queue_add_common (queue, new_item);
168 }
169
170 void
caja_file_changes_queue_schedule_position_remove(GFile * location)171 caja_file_changes_queue_schedule_position_remove (GFile *location)
172 {
173 CajaFileChange *new_item;
174 CajaFileChangesQueue *queue;
175
176 queue = caja_file_changes_queue_get ();
177
178 new_item = g_new (CajaFileChange, 1);
179 new_item->kind = CHANGE_POSITION_REMOVE;
180 new_item->from = g_object_ref (location);
181 caja_file_changes_queue_add_common (queue, new_item);
182 }
183
184 static CajaFileChange *
caja_file_changes_queue_get_change(CajaFileChangesQueue * queue)185 caja_file_changes_queue_get_change (CajaFileChangesQueue *queue)
186 {
187 GList *new_tail;
188 CajaFileChange *result;
189
190 g_assert (queue != NULL);
191
192 /* dequeue the tail item while locking down the list */
193 g_mutex_lock (&queue->mutex);
194
195 if (queue->tail == NULL)
196 {
197 result = NULL;
198 }
199 else
200 {
201 new_tail = queue->tail->prev;
202 result = queue->tail->data;
203 queue->head = g_list_remove_link (queue->head,
204 queue->tail);
205 g_list_free_1 (queue->tail);
206 queue->tail = new_tail;
207 }
208
209 g_mutex_unlock (&queue->mutex);
210
211 return result;
212 }
213
214 enum
215 {
216 CONSUME_CHANGES_MAX_CHUNK = 20
217 };
218
219 static void
pairs_list_free(GList * pairs)220 pairs_list_free (GList *pairs)
221 {
222 GList *p;
223 GFilePair *pair = NULL;
224
225 /* deep delete the list of pairs */
226
227 for (p = pairs; p != NULL; p = p->next)
228 {
229 /* delete the strings in each pair */
230 pair = p->data;
231 g_object_unref (pair->from);
232 g_object_unref (pair->to);
233 }
234
235 /* delete the list and the now empty pair structs */
236 g_list_free_full (pairs, g_free);
237 }
238
239 static void
position_set_list_free(GList * list)240 position_set_list_free (GList *list)
241 {
242 GList *p;
243 CajaFileChangesQueuePosition *item = NULL;
244
245 for (p = list; p != NULL; p = p->next)
246 {
247 item = p->data;
248 g_object_unref (item->location);
249 }
250 /* delete the list and the now empty structs */
251 g_list_free_full (list, g_free);
252 }
253
254 /* go through changes in the change queue, send ones with the same kind
255 * in a list to the different caja_directory_notify calls
256 */
257 void
caja_file_changes_consume_changes(gboolean consume_all)258 caja_file_changes_consume_changes (gboolean consume_all)
259 {
260 CajaFileChange *change;
261 GList *additions, *changes, *deletions, *moves;
262 GList *position_set_requests;
263 GFilePair *pair;
264 CajaFileChangesQueuePosition *position_set;
265 guint chunk_count;
266 CajaFileChangesQueue *queue;
267 gboolean flush_needed;
268
269
270 additions = NULL;
271 changes = NULL;
272 deletions = NULL;
273 moves = NULL;
274 position_set_requests = NULL;
275
276 queue = caja_file_changes_queue_get();
277
278 /* Consume changes from the queue, stuffing them into one of three lists,
279 * keep doing it while the changes are of the same kind, then send them off.
280 * This is to ensure that the changes get sent off in the same order that they
281 * arrived.
282 */
283 for (chunk_count = 0; ; chunk_count++)
284 {
285 change = caja_file_changes_queue_get_change (queue);
286
287 /* figure out if we need to flush the pending changes that we collected sofar */
288
289 if (change == NULL)
290 {
291 flush_needed = TRUE;
292 /* no changes left, flush everything */
293 }
294 else
295 {
296 flush_needed = additions != NULL
297 && change->kind != CHANGE_FILE_ADDED
298 && change->kind != CHANGE_POSITION_SET
299 && change->kind != CHANGE_POSITION_REMOVE;
300
301 flush_needed |= changes != NULL
302 && change->kind != CHANGE_FILE_CHANGED;
303
304 flush_needed |= moves != NULL
305 && change->kind != CHANGE_FILE_MOVED
306 && change->kind != CHANGE_POSITION_SET
307 && change->kind != CHANGE_POSITION_REMOVE;
308
309 flush_needed |= deletions != NULL
310 && change->kind != CHANGE_FILE_REMOVED;
311
312 flush_needed |= position_set_requests != NULL
313 && change->kind != CHANGE_POSITION_SET
314 && change->kind != CHANGE_POSITION_REMOVE
315 && change->kind != CHANGE_FILE_ADDED
316 && change->kind != CHANGE_FILE_MOVED;
317
318 flush_needed |= !consume_all && chunk_count >= CONSUME_CHANGES_MAX_CHUNK;
319 /* we have reached the chunk maximum */
320 }
321
322 if (flush_needed)
323 {
324 /* Send changes we collected off.
325 * At one time we may only have one of the lists
326 * contain changes.
327 */
328
329 if (deletions != NULL)
330 {
331 deletions = g_list_reverse (deletions);
332 caja_directory_notify_files_removed (deletions);
333 g_list_free_full (deletions, g_object_unref);
334 deletions = NULL;
335 }
336 if (moves != NULL)
337 {
338 moves = g_list_reverse (moves);
339 caja_directory_notify_files_moved (moves);
340 pairs_list_free (moves);
341 moves = NULL;
342 }
343 if (additions != NULL)
344 {
345 additions = g_list_reverse (additions);
346 caja_directory_notify_files_added (additions);
347 g_list_free_full (additions, g_object_unref);
348 additions = NULL;
349 }
350 if (changes != NULL)
351 {
352 changes = g_list_reverse (changes);
353 caja_directory_notify_files_changed (changes);
354 g_list_free_full (changes, g_object_unref);
355 changes = NULL;
356 }
357 if (position_set_requests != NULL)
358 {
359 position_set_requests = g_list_reverse (position_set_requests);
360 caja_directory_schedule_position_set (position_set_requests);
361 position_set_list_free (position_set_requests);
362 position_set_requests = NULL;
363 }
364 }
365
366 if (change == NULL)
367 {
368 /* we are done */
369 return;
370 }
371
372 /* add the new change to the list */
373 switch (change->kind)
374 {
375 case CHANGE_FILE_ADDED:
376 additions = g_list_prepend (additions, change->from);
377 break;
378
379 case CHANGE_FILE_CHANGED:
380 changes = g_list_prepend (changes, change->from);
381 break;
382
383 case CHANGE_FILE_REMOVED:
384 deletions = g_list_prepend (deletions, change->from);
385 break;
386
387 case CHANGE_FILE_MOVED:
388 pair = g_new (GFilePair, 1);
389 pair->from = change->from;
390 pair->to = change->to;
391 moves = g_list_prepend (moves, pair);
392 break;
393
394 case CHANGE_POSITION_SET:
395 position_set = g_new (CajaFileChangesQueuePosition, 1);
396 position_set->location = change->from;
397 position_set->set = TRUE;
398 position_set->point = change->point;
399 position_set->screen = change->screen;
400 position_set_requests = g_list_prepend (position_set_requests,
401 position_set);
402 break;
403
404 case CHANGE_POSITION_REMOVE:
405 position_set = g_new (CajaFileChangesQueuePosition, 1);
406 position_set->location = change->from;
407 position_set->set = FALSE;
408 position_set_requests = g_list_prepend (position_set_requests,
409 position_set);
410 break;
411
412 default:
413 g_assert_not_reached ();
414 break;
415 }
416
417 g_free (change);
418 }
419 }
420