No issues found
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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20 Author: Pavel Cisler <pavel@eazel.com>
21 */
22
23 #include <config.h>
24 #include "nautilus-file-changes-queue.h"
25
26 #include "nautilus-directory-notify.h"
27
28 typedef enum {
29 CHANGE_FILE_INITIAL,
30 CHANGE_FILE_ADDED,
31 CHANGE_FILE_CHANGED,
32 CHANGE_FILE_REMOVED,
33 CHANGE_FILE_MOVED,
34 CHANGE_POSITION_SET,
35 CHANGE_POSITION_REMOVE
36 } NautilusFileChangeKind;
37
38 typedef struct {
39 NautilusFileChangeKind kind;
40 GFile *from;
41 GFile *to;
42 GdkPoint point;
43 int screen;
44 } NautilusFileChange;
45
46 typedef struct {
47 GList *head;
48 GList *tail;
49 GMutex mutex;
50 } NautilusFileChangesQueue;
51
52 static NautilusFileChangesQueue *
53 nautilus_file_changes_queue_new (void)
54 {
55 NautilusFileChangesQueue *result;
56
57 result = g_new0 (NautilusFileChangesQueue, 1);
58 g_mutex_init (&result->mutex);
59
60 return result;
61 }
62
63 static NautilusFileChangesQueue *
64 nautilus_file_changes_queue_get (void)
65 {
66 static NautilusFileChangesQueue *file_changes_queue;
67
68 if (file_changes_queue == NULL) {
69 file_changes_queue = nautilus_file_changes_queue_new ();
70 }
71
72 return file_changes_queue;
73 }
74
75 static void
76 nautilus_file_changes_queue_add_common (NautilusFileChangesQueue *queue,
77 NautilusFileChange *new_item)
78 {
79 /* enqueue the new queue item while locking down the list */
80 g_mutex_lock (&queue->mutex);
81
82 queue->head = g_list_prepend (queue->head, new_item);
83 if (queue->tail == NULL)
84 queue->tail = queue->head;
85
86 g_mutex_unlock (&queue->mutex);
87 }
88
89 void
90 nautilus_file_changes_queue_file_added (GFile *location)
91 {
92 NautilusFileChange *new_item;
93 NautilusFileChangesQueue *queue;
94
95 queue = nautilus_file_changes_queue_get();
96
97 new_item = g_new0 (NautilusFileChange, 1);
98 new_item->kind = CHANGE_FILE_ADDED;
99 new_item->from = g_object_ref (location);
100 nautilus_file_changes_queue_add_common (queue, new_item);
101 }
102
103 void
104 nautilus_file_changes_queue_file_changed (GFile *location)
105 {
106 NautilusFileChange *new_item;
107 NautilusFileChangesQueue *queue;
108
109 queue = nautilus_file_changes_queue_get();
110
111 new_item = g_new0 (NautilusFileChange, 1);
112 new_item->kind = CHANGE_FILE_CHANGED;
113 new_item->from = g_object_ref (location);
114 nautilus_file_changes_queue_add_common (queue, new_item);
115 }
116
117 void
118 nautilus_file_changes_queue_file_removed (GFile *location)
119 {
120 NautilusFileChange *new_item;
121 NautilusFileChangesQueue *queue;
122
123 queue = nautilus_file_changes_queue_get();
124
125 new_item = g_new0 (NautilusFileChange, 1);
126 new_item->kind = CHANGE_FILE_REMOVED;
127 new_item->from = g_object_ref (location);
128 nautilus_file_changes_queue_add_common (queue, new_item);
129 }
130
131 void
132 nautilus_file_changes_queue_file_moved (GFile *from,
133 GFile *to)
134 {
135 NautilusFileChange *new_item;
136 NautilusFileChangesQueue *queue;
137
138 queue = nautilus_file_changes_queue_get ();
139
140 new_item = g_new (NautilusFileChange, 1);
141 new_item->kind = CHANGE_FILE_MOVED;
142 new_item->from = g_object_ref (from);
143 new_item->to = g_object_ref (to);
144 nautilus_file_changes_queue_add_common (queue, new_item);
145 }
146
147 void
148 nautilus_file_changes_queue_schedule_position_set (GFile *location,
149 GdkPoint point,
150 int screen)
151 {
152 NautilusFileChange *new_item;
153 NautilusFileChangesQueue *queue;
154
155 queue = nautilus_file_changes_queue_get ();
156
157 new_item = g_new (NautilusFileChange, 1);
158 new_item->kind = CHANGE_POSITION_SET;
159 new_item->from = g_object_ref (location);
160 new_item->point = point;
161 new_item->screen = screen;
162 nautilus_file_changes_queue_add_common (queue, new_item);
163 }
164
165 void
166 nautilus_file_changes_queue_schedule_position_remove (GFile *location)
167 {
168 NautilusFileChange *new_item;
169 NautilusFileChangesQueue *queue;
170
171 queue = nautilus_file_changes_queue_get ();
172
173 new_item = g_new (NautilusFileChange, 1);
174 new_item->kind = CHANGE_POSITION_REMOVE;
175 new_item->from = g_object_ref (location);
176 nautilus_file_changes_queue_add_common (queue, new_item);
177 }
178
179 static NautilusFileChange *
180 nautilus_file_changes_queue_get_change (NautilusFileChangesQueue *queue)
181 {
182 GList *new_tail;
183 NautilusFileChange *result;
184
185 g_assert (queue != NULL);
186
187 /* dequeue the tail item while locking down the list */
188 g_mutex_lock (&queue->mutex);
189
190 if (queue->tail == NULL) {
191 result = NULL;
192 } else {
193 new_tail = queue->tail->prev;
194 result = queue->tail->data;
195 queue->head = g_list_remove_link (queue->head,
196 queue->tail);
197 g_list_free_1 (queue->tail);
198 queue->tail = new_tail;
199 }
200
201 g_mutex_unlock (&queue->mutex);
202
203 return result;
204 }
205
206 enum {
207 CONSUME_CHANGES_MAX_CHUNK = 20
208 };
209
210 static void
211 pairs_list_free (GList *pairs)
212 {
213 GList *p;
214 GFilePair *pair;
215
216 /* deep delete the list of pairs */
217
218 for (p = pairs; p != NULL; p = p->next) {
219 /* delete the strings in each pair */
220 pair = p->data;
221 g_object_unref (pair->from);
222 g_object_unref (pair->to);
223 }
224
225 /* delete the list and the now empty pair structs */
226 g_list_free_full (pairs, g_free);
227 }
228
229 static void
230 position_set_list_free (GList *list)
231 {
232 GList *p;
233 NautilusFileChangesQueuePosition *item;
234
235 for (p = list; p != NULL; p = p->next) {
236 item = p->data;
237 g_object_unref (item->location);
238 }
239 /* delete the list and the now empty structs */
240 g_list_free_full (list, g_free);
241 }
242
243 /* go through changes in the change queue, send ones with the same kind
244 * in a list to the different nautilus_directory_notify calls
245 */
246 void
247 nautilus_file_changes_consume_changes (gboolean consume_all)
248 {
249 NautilusFileChange *change;
250 GList *additions, *changes, *deletions, *moves;
251 GList *position_set_requests;
252 GFilePair *pair;
253 NautilusFileChangesQueuePosition *position_set;
254 guint chunk_count;
255 NautilusFileChangesQueue *queue;
256 gboolean flush_needed;
257
258
259 additions = NULL;
260 changes = NULL;
261 deletions = NULL;
262 moves = NULL;
263 position_set_requests = NULL;
264
265 queue = nautilus_file_changes_queue_get();
266
267 /* Consume changes from the queue, stuffing them into one of three lists,
268 * keep doing it while the changes are of the same kind, then send them off.
269 * This is to ensure that the changes get sent off in the same order that they
270 * arrived.
271 */
272 for (chunk_count = 0; ; chunk_count++) {
273 change = nautilus_file_changes_queue_get_change (queue);
274
275 /* figure out if we need to flush the pending changes that we collected sofar */
276
277 if (change == NULL) {
278 flush_needed = TRUE;
279 /* no changes left, flush everything */
280 } else {
281 flush_needed = additions != NULL
282 && change->kind != CHANGE_FILE_ADDED
283 && change->kind != CHANGE_POSITION_SET
284 && change->kind != CHANGE_POSITION_REMOVE;
285
286 flush_needed |= changes != NULL
287 && change->kind != CHANGE_FILE_CHANGED;
288
289 flush_needed |= moves != NULL
290 && change->kind != CHANGE_FILE_MOVED
291 && change->kind != CHANGE_POSITION_SET
292 && change->kind != CHANGE_POSITION_REMOVE;
293
294 flush_needed |= deletions != NULL
295 && change->kind != CHANGE_FILE_REMOVED;
296
297 flush_needed |= position_set_requests != NULL
298 && change->kind != CHANGE_POSITION_SET
299 && change->kind != CHANGE_POSITION_REMOVE
300 && change->kind != CHANGE_FILE_ADDED
301 && change->kind != CHANGE_FILE_MOVED;
302
303 flush_needed |= !consume_all && chunk_count >= CONSUME_CHANGES_MAX_CHUNK;
304 /* we have reached the chunk maximum */
305 }
306
307 if (flush_needed) {
308 /* Send changes we collected off.
309 * At one time we may only have one of the lists
310 * contain changes.
311 */
312
313 if (deletions != NULL) {
314 deletions = g_list_reverse (deletions);
315 nautilus_directory_notify_files_removed (deletions);
316 g_list_free_full (deletions, g_object_unref);
317 deletions = NULL;
318 }
319 if (moves != NULL) {
320 moves = g_list_reverse (moves);
321 nautilus_directory_notify_files_moved (moves);
322 pairs_list_free (moves);
323 moves = NULL;
324 }
325 if (additions != NULL) {
326 additions = g_list_reverse (additions);
327 nautilus_directory_notify_files_added (additions);
328 g_list_free_full (additions, g_object_unref);
329 additions = NULL;
330 }
331 if (changes != NULL) {
332 changes = g_list_reverse (changes);
333 nautilus_directory_notify_files_changed (changes);
334 g_list_free_full (changes, g_object_unref);
335 changes = NULL;
336 }
337 if (position_set_requests != NULL) {
338 position_set_requests = g_list_reverse (position_set_requests);
339 nautilus_directory_schedule_position_set (position_set_requests);
340 position_set_list_free (position_set_requests);
341 position_set_requests = NULL;
342 }
343 }
344
345 if (change == NULL) {
346 /* we are done */
347 return;
348 }
349
350 /* add the new change to the list */
351 switch (change->kind) {
352 case CHANGE_FILE_ADDED:
353 additions = g_list_prepend (additions, change->from);
354 break;
355
356 case CHANGE_FILE_CHANGED:
357 changes = g_list_prepend (changes, change->from);
358 break;
359
360 case CHANGE_FILE_REMOVED:
361 deletions = g_list_prepend (deletions, change->from);
362 break;
363
364 case CHANGE_FILE_MOVED:
365 pair = g_new (GFilePair, 1);
366 pair->from = change->from;
367 pair->to = change->to;
368 moves = g_list_prepend (moves, pair);
369 break;
370
371 case CHANGE_POSITION_SET:
372 position_set = g_new (NautilusFileChangesQueuePosition, 1);
373 position_set->location = change->from;
374 position_set->set = TRUE;
375 position_set->point = change->point;
376 position_set->screen = change->screen;
377 position_set_requests = g_list_prepend (position_set_requests,
378 position_set);
379 break;
380
381 case CHANGE_POSITION_REMOVE:
382 position_set = g_new (NautilusFileChangesQueuePosition, 1);
383 position_set->location = change->from;
384 position_set->set = FALSE;
385 position_set_requests = g_list_prepend (position_set_requests,
386 position_set);
387 break;
388
389 default:
390 g_assert_not_reached ();
391 break;
392 }
393
394 g_free (change);
395 }
396 }