nautilus-3.6.3/libnautilus-private/nautilus-file-changes-queue.c

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 }