1 /*
2  * gedit-file-browser-view.c - Gedit plugin providing easy file access
3  * from the sidepanel
4  *
5  * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <string.h>
22 
23 #include <glib-object.h>
24 #include <gio/gio.h>
25 #include <gdk/gdkkeysyms.h>
26 
27 #include "gedit-file-browser-store.h"
28 #include "gedit-file-bookmarks-store.h"
29 #include "gedit-file-browser-view.h"
30 #include "gedit-file-browser-enum-types.h"
31 
32 struct _GeditFileBrowserViewPrivate
33 {
34 	GtkTreeViewColumn               *column;
35 	GtkCellRenderer                 *pixbuf_renderer;
36 	GtkCellRenderer                 *text_renderer;
37 
38 	GtkTreeModel                    *model;
39 
40 	/* Used when renaming */
41 	gchar                           *orig_markup;
42 	GtkTreeRowReference             *editable;
43 
44 	/* Click policy */
45 	GeditFileBrowserViewClickPolicy  click_policy;
46 	/* Both clicks in a double click need to be on the same row */
47 	GtkTreePath                     *double_click_path[2];
48 	GtkTreePath                     *hover_path;
49 	GdkCursor                       *hand_cursor;
50 	gboolean                         ignore_release;
51 	gboolean                         selected_on_button_down;
52 	gint                             drag_button;
53 	gboolean                         drag_started;
54 
55 	gboolean                         restore_expand_state;
56 	gboolean                         is_refresh;
57 	GHashTable                      *expand_state;
58 };
59 
60 /* Properties */
61 enum
62 {
63 	PROP_0,
64 
65 	PROP_CLICK_POLICY,
66 	PROP_RESTORE_EXPAND_STATE
67 };
68 
69 /* Signals */
70 enum
71 {
72 	ERROR,
73 	FILE_ACTIVATED,
74 	DIRECTORY_ACTIVATED,
75 	BOOKMARK_ACTIVATED,
76 	NUM_SIGNALS
77 };
78 
79 static guint signals[NUM_SIGNALS] = { 0 };
80 
81 static const GtkTargetEntry drag_source_targets[] = {
82 	{ "text/uri-list", 0, 0 }
83 };
84 
85 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GeditFileBrowserView,
86 				gedit_file_browser_view,
87 				GTK_TYPE_TREE_VIEW,
88 				0,
89 				G_ADD_PRIVATE_DYNAMIC (GeditFileBrowserView))
90 
91 static void on_cell_edited 		(GtkCellRendererText    *cell,
92 				 	 gchar                  *path,
93 				 	 gchar                  *new_text,
94 				 	 GeditFileBrowserView   *tree_view);
95 
96 static void on_begin_refresh 		(GeditFileBrowserStore  *model,
97 					 GeditFileBrowserView   *view);
98 static void on_end_refresh 		(GeditFileBrowserStore  *model,
99 					 GeditFileBrowserView   *view);
100 
101 static void on_unload			(GeditFileBrowserStore  *model,
102 					 GFile                  *location,
103 					 GeditFileBrowserView   *view);
104 
105 static void on_row_inserted		(GeditFileBrowserStore  *model,
106 					 GtkTreePath            *path,
107 					 GtkTreeIter            *iter,
108 					 GeditFileBrowserView   *view);
109 
110 static void
gedit_file_browser_view_finalize(GObject * object)111 gedit_file_browser_view_finalize (GObject *object)
112 {
113 	GeditFileBrowserView *obj = GEDIT_FILE_BROWSER_VIEW (object);
114 
115 	if (obj->priv->hand_cursor)
116 		g_object_unref (obj->priv->hand_cursor);
117 
118 	if (obj->priv->hover_path)
119 		gtk_tree_path_free (obj->priv->hover_path);
120 
121 	if (obj->priv->expand_state)
122 	{
123 		g_hash_table_destroy (obj->priv->expand_state);
124 		obj->priv->expand_state = NULL;
125 	}
126 
127 	G_OBJECT_CLASS (gedit_file_browser_view_parent_class)->finalize (object);
128 }
129 
130 static void
add_expand_state(GeditFileBrowserView * view,GFile * location)131 add_expand_state (GeditFileBrowserView *view,
132 		  GFile                *location)
133 {
134 	if (!location)
135 		return;
136 
137 	if (view->priv->expand_state)
138 		g_hash_table_insert (view->priv->expand_state, location, g_object_ref (location));
139 }
140 
141 static void
remove_expand_state(GeditFileBrowserView * view,GFile * location)142 remove_expand_state (GeditFileBrowserView *view,
143 		     GFile                *location)
144 {
145 	if (!location)
146 		return;
147 
148 	if (view->priv->expand_state)
149 		g_hash_table_remove (view->priv->expand_state, location);
150 }
151 
152 static void
row_expanded(GtkTreeView * tree_view,GtkTreeIter * iter,GtkTreePath * path)153 row_expanded (GtkTreeView *tree_view,
154 	      GtkTreeIter *iter,
155 	      GtkTreePath *path)
156 {
157 	GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (tree_view);
158 
159 	if (GTK_TREE_VIEW_CLASS (gedit_file_browser_view_parent_class)->row_expanded)
160 		GTK_TREE_VIEW_CLASS (gedit_file_browser_view_parent_class)->row_expanded (tree_view, iter, path);
161 
162 	if (!GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
163 		return;
164 
165 	if (view->priv->restore_expand_state)
166 	{
167 		GFile *location;
168 
169 		gtk_tree_model_get (view->priv->model,
170 				    iter,
171 				    GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location,
172 				    -1);
173 
174 		add_expand_state (view, location);
175 
176 		if (location)
177 			g_object_unref (location);
178 	}
179 
180 	_gedit_file_browser_store_iter_expanded (GEDIT_FILE_BROWSER_STORE (view->priv->model),
181 						 iter);
182 }
183 
184 static void
row_collapsed(GtkTreeView * tree_view,GtkTreeIter * iter,GtkTreePath * path)185 row_collapsed (GtkTreeView *tree_view,
186 	       GtkTreeIter *iter,
187 	       GtkTreePath *path)
188 {
189 	GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (tree_view);
190 
191 	if (GTK_TREE_VIEW_CLASS (gedit_file_browser_view_parent_class)->row_collapsed)
192 		GTK_TREE_VIEW_CLASS (gedit_file_browser_view_parent_class)->row_collapsed (tree_view, iter, path);
193 
194 	if (!GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
195 		return;
196 
197 	if (view->priv->restore_expand_state)
198 	{
199 		GFile *location;
200 
201 		gtk_tree_model_get (view->priv->model,
202 				    iter,
203 				    GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location,
204 				    -1);
205 
206 		remove_expand_state (view, location);
207 
208 		if (location)
209 			g_object_unref (location);
210 	}
211 
212 	_gedit_file_browser_store_iter_collapsed (GEDIT_FILE_BROWSER_STORE (view->priv->model),
213 						  iter);
214 }
215 
216 static gboolean
leave_notify_event(GtkWidget * widget,GdkEventCrossing * event)217 leave_notify_event (GtkWidget        *widget,
218 		    GdkEventCrossing *event)
219 {
220 	GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
221 
222 	if (view->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE &&
223 	    view->priv->hover_path != NULL)
224 	{
225 		gtk_tree_path_free (view->priv->hover_path);
226 		view->priv->hover_path = NULL;
227 	}
228 
229 	/* Chainup */
230 	return GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->leave_notify_event (widget, event);
231 }
232 
233 static gboolean
enter_notify_event(GtkWidget * widget,GdkEventCrossing * event)234 enter_notify_event (GtkWidget        *widget,
235 		    GdkEventCrossing *event)
236 {
237 	GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
238 
239 	if (view->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE)
240 	{
241 		if (view->priv->hover_path != NULL)
242 			gtk_tree_path_free (view->priv->hover_path);
243 
244 		gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
245 					       event->x, event->y,
246 					       &view->priv->hover_path,
247 					       NULL, NULL, NULL);
248 
249 		if (view->priv->hover_path != NULL)
250 		{
251 			gdk_window_set_cursor (gtk_widget_get_window (widget),
252 					       view->priv->hand_cursor);
253 		}
254 	}
255 
256 	/* Chainup */
257 	return GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->enter_notify_event (widget, event);
258 }
259 
260 static gboolean
motion_notify_event(GtkWidget * widget,GdkEventMotion * event)261 motion_notify_event (GtkWidget *widget,
262 		     GdkEventMotion *event)
263 {
264 	GtkTreePath *old_hover_path;
265 	GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
266 
267 	if (view->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE)
268 	{
269 		old_hover_path = view->priv->hover_path;
270 		gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
271 					       event->x, event->y,
272 					       &view->priv->hover_path,
273 					       NULL, NULL, NULL);
274 
275 		if ((old_hover_path != NULL) != (view->priv->hover_path != NULL))
276 		{
277 			if (view->priv->hover_path != NULL)
278 			{
279 				gdk_window_set_cursor (gtk_widget_get_window (widget),
280 						       view->priv->hand_cursor);
281 			}
282 			else
283 			{
284 				gdk_window_set_cursor (gtk_widget_get_window (widget),
285 						       NULL);
286 			}
287 		}
288 
289 		if (old_hover_path != NULL)
290 			gtk_tree_path_free (old_hover_path);
291 	}
292 
293 	/* Chainup */
294 	return GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->motion_notify_event (widget, event);
295 }
296 
297 static void
set_click_policy_property(GeditFileBrowserView * obj,GeditFileBrowserViewClickPolicy click_policy)298 set_click_policy_property (GeditFileBrowserView            *obj,
299 			   GeditFileBrowserViewClickPolicy  click_policy)
300 {
301 	GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (obj));
302 
303 	obj->priv->click_policy = click_policy;
304 
305 	if (click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE)
306 	{
307 		if (obj->priv->hand_cursor == NULL)
308 			obj->priv->hand_cursor = gdk_cursor_new_from_name (display, "pointer");
309 	}
310 	else if (click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_DOUBLE)
311 	{
312 		if (obj->priv->hover_path != NULL)
313 		{
314 			GtkTreeIter iter;
315 
316 			if (gtk_tree_model_get_iter (GTK_TREE_MODEL (obj->priv->model),
317 						     &iter, obj->priv->hover_path))
318 			{
319 				gtk_tree_model_row_changed (GTK_TREE_MODEL (obj->priv->model),
320 							    obj->priv->hover_path, &iter);
321 			}
322 
323 			gtk_tree_path_free (obj->priv->hover_path);
324 			obj->priv->hover_path = NULL;
325 		}
326 
327 		if (gtk_widget_get_realized (GTK_WIDGET (obj)))
328 		{
329 			GdkWindow *win = gtk_widget_get_window (GTK_WIDGET (obj));
330 
331 			gdk_window_set_cursor (win, NULL);
332 
333 			if (display != NULL)
334 				gdk_display_flush (display);
335 		}
336 
337 		if (obj->priv->hand_cursor)
338 		{
339 			g_object_unref (obj->priv->hand_cursor);
340 			obj->priv->hand_cursor = NULL;
341 		}
342 	}
343 }
344 
345 static void
directory_activated(GeditFileBrowserView * view,GtkTreeIter * iter)346 directory_activated (GeditFileBrowserView *view,
347 		     GtkTreeIter          *iter)
348 {
349 	gedit_file_browser_store_set_virtual_root (GEDIT_FILE_BROWSER_STORE (view->priv->model), iter);
350 }
351 
352 static void
activate_selected_files(GeditFileBrowserView * view)353 activate_selected_files (GeditFileBrowserView *view)
354 {
355 	GtkTreeView *tree_view = GTK_TREE_VIEW (view);
356 	GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
357 	GList *rows = gtk_tree_selection_get_selected_rows (selection, &view->priv->model);
358 	GList *row;
359 	GtkTreePath *directory = NULL;
360 	GtkTreePath *path;
361 	GtkTreeIter iter;
362 	GeditFileBrowserStoreFlag flags;
363 
364 	for (row = rows; row; row = row->next)
365 	{
366 		path = (GtkTreePath *)(row->data);
367 
368 		/* Get iter from path */
369 		if (!gtk_tree_model_get_iter (view->priv->model, &iter, path))
370 			continue;
371 
372 		gtk_tree_model_get (view->priv->model, &iter, GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags, -1);
373 
374 		if (FILE_IS_DIR (flags) && directory == NULL)
375 			directory = path;
376 		else if (!FILE_IS_DUMMY (flags))
377 			g_signal_emit (view, signals[FILE_ACTIVATED], 0, &iter);
378 	}
379 
380 	if (directory != NULL &&
381 	    gtk_tree_model_get_iter (view->priv->model, &iter, directory))
382 	{
383 		g_signal_emit (view, signals[DIRECTORY_ACTIVATED], 0, &iter);
384 	}
385 
386 	g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
387 }
388 
389 static void
activate_selected_bookmark(GeditFileBrowserView * view)390 activate_selected_bookmark (GeditFileBrowserView *view)
391 {
392 	GtkTreeView *tree_view = GTK_TREE_VIEW (view);
393 	GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
394 	GtkTreeIter iter;
395 
396 	if (gtk_tree_selection_get_selected (selection, &view->priv->model, &iter))
397 		g_signal_emit (view, signals[BOOKMARK_ACTIVATED], 0, &iter);
398 }
399 
400 static void
activate_selected_items(GeditFileBrowserView * view)401 activate_selected_items (GeditFileBrowserView *view)
402 {
403 	if (GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
404 		activate_selected_files (view);
405 	else if (GEDIT_IS_FILE_BOOKMARKS_STORE (view->priv->model))
406 		activate_selected_bookmark (view);
407 }
408 
409 static void
row_activated(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column)410 row_activated (GtkTreeView       *tree_view,
411                GtkTreePath       *path,
412                GtkTreeViewColumn *column)
413 {
414 	GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
415 
416 	/* Make sure the activated row is the only one selected */
417 	gtk_tree_selection_unselect_all (selection);
418 	gtk_tree_selection_select_path (selection, path);
419 
420 	activate_selected_items (GEDIT_FILE_BROWSER_VIEW (tree_view));
421 }
422 
423 static void
toggle_hidden_filter(GeditFileBrowserView * view)424 toggle_hidden_filter (GeditFileBrowserView *view)
425 {
426 	GeditFileBrowserStoreFilterMode mode;
427 
428 	if (GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
429 	{
430 		mode = gedit_file_browser_store_get_filter_mode (GEDIT_FILE_BROWSER_STORE (view->priv->model));
431 		mode ^=	GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN;
432 		gedit_file_browser_store_set_filter_mode (GEDIT_FILE_BROWSER_STORE (view->priv->model), mode);
433 	}
434 }
435 
436 static gboolean
button_event_modifies_selection(GdkEventButton * event)437 button_event_modifies_selection (GdkEventButton *event)
438 {
439 	return (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
440 }
441 
442 static void
drag_begin(GtkWidget * widget,GdkDragContext * context)443 drag_begin (GtkWidget      *widget,
444 	    GdkDragContext *context)
445 {
446 	GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
447 
448 	view->priv->drag_button = 0;
449 	view->priv->drag_started = TRUE;
450 
451 	/* Chain up */
452 	GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->drag_begin (widget, context);
453 }
454 
455 static void
did_not_drag(GeditFileBrowserView * view,GdkEventButton * event)456 did_not_drag (GeditFileBrowserView *view,
457 	      GdkEventButton       *event)
458 {
459 	GtkTreeView *tree_view = GTK_TREE_VIEW (view);
460 	GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
461 	GtkTreePath *path;
462 
463 	if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y, &path, NULL, NULL, NULL))
464 	{
465 		if ((view->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE) &&
466 		    !button_event_modifies_selection (event) &&
467 		    (event->button == 1 || event->button == 2))
468 		{
469 		    	/* Activate all selected items, and leave them selected */
470 			activate_selected_items (view);
471 		}
472 		else if ((event->button == 1 || event->button == 2) &&
473 		         ((event->state & GDK_CONTROL_MASK) != 0 || (event->state & GDK_SHIFT_MASK) == 0) &&
474 		         view->priv->selected_on_button_down)
475 		{
476 			if (!button_event_modifies_selection (event))
477 			{
478 				gtk_tree_selection_unselect_all (selection);
479 				gtk_tree_selection_select_path (selection, path);
480 			}
481 			else
482 			{
483 				gtk_tree_selection_unselect_path (selection, path);
484 			}
485 		}
486 
487 		gtk_tree_path_free (path);
488 	}
489 }
490 
491 static gboolean
button_release_event(GtkWidget * widget,GdkEventButton * event)492 button_release_event (GtkWidget      *widget,
493 		      GdkEventButton *event)
494 {
495 	GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
496 
497 	if (event->button == view->priv->drag_button)
498 	{
499 		view->priv->drag_button = 0;
500 
501 		if (!view->priv->drag_started && !view->priv->ignore_release)
502 			did_not_drag (view, event);
503 	}
504 
505 	/* Chain up */
506 	return GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->button_release_event (widget, event);
507 }
508 
509 static gboolean
button_press_event(GtkWidget * widget,GdkEventButton * event)510 button_press_event (GtkWidget      *widget,
511 		    GdkEventButton *event)
512 {
513 	GtkWidgetClass *widget_parent = GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class);
514 	GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
515 	GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
516 	GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
517 	int double_click_time;
518 	static int click_count = 0;
519 	static guint32 last_click_time = 0;
520 	GtkTreePath *path;
521 	int expander_size;
522 	int horizontal_separator;
523 	gboolean on_expander;
524 	gboolean call_parent;
525 	gboolean selected;
526 
527 	/* Get double click time */
528 	g_object_get (G_OBJECT (gtk_widget_get_settings (widget)),
529 		      "gtk-double-click-time", &double_click_time,
530 		      NULL);
531 
532 	/* Determine click count */
533 	if (event->time - last_click_time < double_click_time)
534 		click_count++;
535 	else
536 		click_count = 0;
537 
538 	last_click_time = event->time;
539 
540 	/* Ignore double click if we are in single click mode */
541 	if (view->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE &&
542 	    click_count >= 2)
543 	{
544 		return TRUE;
545 	}
546 
547 	view->priv->ignore_release = FALSE;
548 	call_parent = TRUE;
549 
550 	if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y, &path, NULL, NULL, NULL))
551 	{
552 		/* Keep track of path of last click so double clicks only happen
553 		 * on the same item */
554 		if ((event->button == 1 || event->button == 2)  &&
555 		    event->type == GDK_BUTTON_PRESS)
556 		{
557 			if (view->priv->double_click_path[1])
558 				gtk_tree_path_free (view->priv->double_click_path[1]);
559 
560 			view->priv->double_click_path[1] = view->priv->double_click_path[0];
561 			view->priv->double_click_path[0] = gtk_tree_path_copy (path);
562 		}
563 
564 		if (event->type == GDK_2BUTTON_PRESS)
565 		{
566 			/* Do not chain up. The row-activated signal is normally
567 			 * already sent, which will activate the selected item
568 			 * and open the file.
569 			 */
570 		}
571 		else
572 		{
573 			/* We're going to filter out some situations where
574 			 * we can't let the default code run because all
575 			 * but one row would be deselected. We don't
576 			 * want that; we want the right click menu or single
577 			 * click to apply to everything that's currently selected. */
578 			selected = gtk_tree_selection_path_is_selected (selection, path);
579 
580 			if (event->button == GDK_BUTTON_SECONDARY && selected)
581 				call_parent = FALSE;
582 
583 			if ((event->button == 1 || event->button == 2) &&
584 			    ((event->state & GDK_CONTROL_MASK) != 0 ||
585 			     (event->state & GDK_SHIFT_MASK) == 0))
586 			{
587 				gtk_widget_style_get (widget,
588 						      "expander-size", &expander_size,
589 						      "horizontal-separator", &horizontal_separator,
590 						      NULL);
591 				on_expander = (event->x <= horizontal_separator / 2 +
592 					       gtk_tree_path_get_depth (path) * expander_size);
593 
594 				view->priv->selected_on_button_down = selected;
595 
596 				if (selected)
597 				{
598 					call_parent = on_expander || gtk_tree_selection_count_selected_rows (selection) == 1;
599 					view->priv->ignore_release = call_parent && view->priv->click_policy != GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE;
600 				}
601 				else if  ((event->state & GDK_CONTROL_MASK) != 0)
602 				{
603 					call_parent = FALSE;
604 					gtk_tree_selection_select_path (selection, path);
605 				}
606 				else
607 				{
608 					view->priv->ignore_release = on_expander;
609 				}
610 			}
611 
612 			if (call_parent)
613 			{
614 				/* Chain up */
615 				widget_parent->button_press_event (widget, event);
616 			}
617 			else if (selected)
618 			{
619 				gtk_widget_grab_focus (widget);
620 			}
621 
622 			if ((event->button == 1 || event->button == 2) &&
623 			    event->type == GDK_BUTTON_PRESS)
624 			{
625 				view->priv->drag_started = FALSE;
626 				view->priv->drag_button = event->button;
627 			}
628 		}
629 
630 		gtk_tree_path_free (path);
631 	}
632 	else
633 	{
634 		if ((event->button == 1 || event->button == 2)  &&
635 		    event->type == GDK_BUTTON_PRESS)
636 		{
637 			if (view->priv->double_click_path[1])
638 				gtk_tree_path_free (view->priv->double_click_path[1]);
639 
640 			view->priv->double_click_path[1] = view->priv->double_click_path[0];
641 			view->priv->double_click_path[0] = NULL;
642 		}
643 
644 		gtk_tree_selection_unselect_all (selection);
645 		/* Chain up */
646 		widget_parent->button_press_event (widget, event);
647 	}
648 
649 	/* We already chained up if nescessary, so just return TRUE */
650 	return TRUE;
651 }
652 
653 static gboolean
key_press_event(GtkWidget * widget,GdkEventKey * event)654 key_press_event (GtkWidget   *widget,
655 		 GdkEventKey *event)
656 {
657 	GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
658 	guint modifiers = gtk_accelerator_get_default_mod_mask ();
659 	gboolean handled = FALSE;
660 
661 	switch (event->keyval)
662 	{
663 		case GDK_KEY_space:
664 			if (event->state & GDK_CONTROL_MASK)
665 			{
666 				handled = FALSE;
667 				break;
668 			}
669 			if (!gtk_widget_has_focus (widget))
670 			{
671 				handled = FALSE;
672 				break;
673 			}
674 
675 			activate_selected_items (view);
676 			handled = TRUE;
677 			break;
678 
679 		case GDK_KEY_Return:
680 		case GDK_KEY_KP_Enter:
681 			activate_selected_items (view);
682 			handled = TRUE;
683 			break;
684 
685 		case GDK_KEY_h:
686 			if ((event->state & modifiers) == GDK_CONTROL_MASK)
687 			{
688 				toggle_hidden_filter (view);
689 				handled = TRUE;
690 				break;
691 			}
692 
693 		default:
694 			handled = FALSE;
695 			break;
696 	}
697 
698 	/* Chain up */
699 	if (!handled)
700 		return GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->key_press_event (widget, event);
701 
702 	return TRUE;
703 }
704 
705 static void
fill_expand_state(GeditFileBrowserView * view,GtkTreeIter * iter)706 fill_expand_state (GeditFileBrowserView *view,
707 		   GtkTreeIter          *iter)
708 {
709 	GtkTreePath *path;
710 	GtkTreeIter child;
711 
712 	if (!gtk_tree_model_iter_has_child (view->priv->model, iter))
713 		return;
714 
715 	path = gtk_tree_model_get_path (view->priv->model, iter);
716 
717 	if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), path))
718 	{
719 		GFile *location;
720 
721 		gtk_tree_model_get (view->priv->model,
722 				    iter,
723 				    GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location,
724 				    -1);
725 
726 		add_expand_state (view, location);
727 
728 		if (location)
729 			g_object_unref (location);
730 	}
731 
732 	if (gtk_tree_model_iter_children (view->priv->model, &child, iter))
733 	{
734 		do
735 		{
736 			fill_expand_state (view, &child);
737 		}
738 		while (gtk_tree_model_iter_next (view->priv->model, &child));
739 	}
740 
741 	gtk_tree_path_free (path);
742 }
743 
744 static void
uninstall_restore_signals(GeditFileBrowserView * tree_view,GtkTreeModel * model)745 uninstall_restore_signals (GeditFileBrowserView *tree_view,
746 			   GtkTreeModel         *model)
747 {
748 	g_signal_handlers_disconnect_by_func (model, on_begin_refresh, tree_view);
749 	g_signal_handlers_disconnect_by_func (model, on_end_refresh, tree_view);
750 	g_signal_handlers_disconnect_by_func (model, on_unload, tree_view);
751 	g_signal_handlers_disconnect_by_func (model, on_row_inserted, tree_view);
752 }
753 
754 static void
install_restore_signals(GeditFileBrowserView * tree_view,GtkTreeModel * model)755 install_restore_signals (GeditFileBrowserView *tree_view,
756 			 GtkTreeModel         *model)
757 {
758 	g_signal_connect (model, "begin-refresh", G_CALLBACK (on_begin_refresh), tree_view);
759 	g_signal_connect (model, "end-refresh", G_CALLBACK (on_end_refresh), tree_view);
760 	g_signal_connect (model, "unload", G_CALLBACK (on_unload), tree_view);
761 	g_signal_connect_after (model, "row-inserted", G_CALLBACK (on_row_inserted), tree_view);
762 }
763 
764 static void
set_restore_expand_state(GeditFileBrowserView * view,gboolean state)765 set_restore_expand_state (GeditFileBrowserView *view,
766 			  gboolean              state)
767 {
768 	if (state == view->priv->restore_expand_state)
769 		return;
770 
771 	if (view->priv->expand_state)
772 	{
773 		g_hash_table_destroy (view->priv->expand_state);
774 		view->priv->expand_state = NULL;
775 	}
776 
777 	if (state)
778 	{
779 		view->priv->expand_state = g_hash_table_new_full (g_file_hash,
780 								  (GEqualFunc)g_file_equal,
781 								  g_object_unref,
782 								  NULL);
783 
784 		if (view->priv->model && GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
785 		{
786 			fill_expand_state (view, NULL);
787 			install_restore_signals (view, view->priv->model);
788 		}
789 	}
790 	else if (view->priv->model && GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
791 	{
792 		uninstall_restore_signals (view, view->priv->model);
793 	}
794 
795 	view->priv->restore_expand_state = state;
796 }
797 
798 static void
get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)799 get_property (GObject    *object,
800 	      guint       prop_id,
801 	      GValue     *value,
802 	      GParamSpec *pspec)
803 {
804 	GeditFileBrowserView *obj = GEDIT_FILE_BROWSER_VIEW (object);
805 
806 	switch (prop_id)
807 	{
808 		case PROP_CLICK_POLICY:
809 			g_value_set_enum (value, obj->priv->click_policy);
810 			break;
811 		case PROP_RESTORE_EXPAND_STATE:
812 			g_value_set_boolean (value, obj->priv->restore_expand_state);
813 			break;
814 		default:
815 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
816 			break;
817 	}
818 }
819 
820 static void
set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)821 set_property (GObject      *object,
822 	      guint         prop_id,
823 	      const GValue *value,
824 	      GParamSpec   *pspec)
825 {
826 	GeditFileBrowserView *obj = GEDIT_FILE_BROWSER_VIEW (object);
827 
828 	switch (prop_id)
829 	{
830 		case PROP_CLICK_POLICY:
831 			set_click_policy_property (obj, g_value_get_enum (value));
832 			break;
833 		case PROP_RESTORE_EXPAND_STATE:
834 			set_restore_expand_state (obj, g_value_get_boolean (value));
835 			break;
836 		default:
837 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
838 			break;
839 	}
840 }
841 
842 static void
gedit_file_browser_view_class_init(GeditFileBrowserViewClass * klass)843 gedit_file_browser_view_class_init (GeditFileBrowserViewClass *klass)
844 {
845 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
846 	GtkTreeViewClass *tree_view_class = GTK_TREE_VIEW_CLASS (klass);
847 	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
848 
849 	object_class->finalize = gedit_file_browser_view_finalize;
850 	object_class->get_property = get_property;
851 	object_class->set_property = set_property;
852 
853 	/* Event handlers */
854 	widget_class->motion_notify_event = motion_notify_event;
855 	widget_class->enter_notify_event = enter_notify_event;
856 	widget_class->leave_notify_event = leave_notify_event;
857 	widget_class->button_press_event = button_press_event;
858 	widget_class->button_release_event = button_release_event;
859 	widget_class->drag_begin = drag_begin;
860 	widget_class->key_press_event = key_press_event;
861 
862 	/* Tree view handlers */
863 	tree_view_class->row_activated = row_activated;
864 	tree_view_class->row_expanded = row_expanded;
865 	tree_view_class->row_collapsed = row_collapsed;
866 
867 	/* Default handlers */
868 	klass->directory_activated = directory_activated;
869 
870 	g_object_class_install_property (object_class, PROP_CLICK_POLICY,
871 					 g_param_spec_enum ("click-policy",
872 					 		    "Click Policy",
873 					 		    "The click policy",
874 					 		     GEDIT_TYPE_FILE_BROWSER_VIEW_CLICK_POLICY,
875 					 		     GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_DOUBLE,
876 					 		     G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
877 
878 	g_object_class_install_property (object_class, PROP_RESTORE_EXPAND_STATE,
879 					 g_param_spec_boolean ("restore-expand-state",
880 					 		       "Restore Expand State",
881 					 		       "Restore expanded state of loaded directories",
882 					 		       FALSE,
883 					 		       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
884 
885 	signals[ERROR] =
886 	    g_signal_new ("error",
887 			  G_OBJECT_CLASS_TYPE (object_class),
888 			  G_SIGNAL_RUN_LAST,
889 			  G_STRUCT_OFFSET (GeditFileBrowserViewClass, error),
890 			  NULL, NULL, NULL,
891 			  G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
892 	signals[FILE_ACTIVATED] =
893 	    g_signal_new ("file-activated",
894 			  G_OBJECT_CLASS_TYPE (object_class),
895 			  G_SIGNAL_RUN_LAST,
896 			  G_STRUCT_OFFSET (GeditFileBrowserViewClass, file_activated),
897 			  NULL, NULL, NULL,
898 			  G_TYPE_NONE, 1, GTK_TYPE_TREE_ITER);
899 	signals[DIRECTORY_ACTIVATED] =
900 	    g_signal_new ("directory-activated",
901 			  G_OBJECT_CLASS_TYPE (object_class),
902 			  G_SIGNAL_RUN_LAST,
903 			  G_STRUCT_OFFSET (GeditFileBrowserViewClass, directory_activated),
904 			  NULL, NULL, NULL,
905 			  G_TYPE_NONE, 1, GTK_TYPE_TREE_ITER);
906 	signals[BOOKMARK_ACTIVATED] =
907 	    g_signal_new ("bookmark-activated",
908 			  G_OBJECT_CLASS_TYPE (object_class),
909 			  G_SIGNAL_RUN_LAST,
910 			  G_STRUCT_OFFSET (GeditFileBrowserViewClass, bookmark_activated),
911 			  NULL, NULL, NULL,
912 			  G_TYPE_NONE, 1, GTK_TYPE_TREE_ITER);
913 }
914 
915 static void
gedit_file_browser_view_class_finalize(GeditFileBrowserViewClass * klass)916 gedit_file_browser_view_class_finalize (GeditFileBrowserViewClass *klass)
917 {
918 }
919 
920 static void
cell_data_cb(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,GeditFileBrowserView * obj)921 cell_data_cb (GtkTreeViewColumn    *tree_column,
922 	      GtkCellRenderer      *cell,
923 	      GtkTreeModel         *tree_model,
924 	      GtkTreeIter          *iter,
925 	      GeditFileBrowserView *obj)
926 {
927 	GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter);
928 	PangoUnderline underline = PANGO_UNDERLINE_NONE;
929 	gboolean editable = FALSE;
930 
931 	if (obj->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE &&
932 	    obj->priv->hover_path != NULL &&
933 	    gtk_tree_path_compare (path, obj->priv->hover_path) == 0)
934 	{
935 		underline = PANGO_UNDERLINE_SINGLE;
936 	}
937 
938 	if (GEDIT_IS_FILE_BROWSER_STORE (tree_model) &&
939 	    obj->priv->editable != NULL &&
940 	    gtk_tree_row_reference_valid (obj->priv->editable))
941 	{
942 		GtkTreePath *edpath = gtk_tree_row_reference_get_path (obj->priv->editable);
943 
944 		editable = edpath && gtk_tree_path_compare (path, edpath) == 0;
945 
946 		gtk_tree_path_free (edpath);
947 	}
948 
949 	gtk_tree_path_free (path);
950 	g_object_set (cell, "editable", editable, "underline", underline, NULL);
951 }
952 
953 static void
icon_renderer_cb(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,GeditFileBrowserView * obj)954 icon_renderer_cb (GtkTreeViewColumn    *tree_column,
955                   GtkCellRenderer      *cell,
956                   GtkTreeModel         *tree_model,
957                   GtkTreeIter          *iter,
958                   GeditFileBrowserView *obj)
959 {
960 	GdkPixbuf *pixbuf;
961 	gchar *icon_name;
962 	gboolean set_pixbuf = FALSE;
963 
964 	gtk_tree_model_get (tree_model,
965 			    iter,
966 			    GEDIT_FILE_BROWSER_STORE_COLUMN_ICON_NAME, &icon_name,
967 			    GEDIT_FILE_BROWSER_STORE_COLUMN_ICON, &pixbuf,
968 			    -1);
969 
970 	if (pixbuf != NULL && (GEDIT_IS_FILE_BROWSER_STORE (tree_model) || icon_name == NULL))
971 		set_pixbuf = TRUE;
972 
973 	if (set_pixbuf)
974 		g_object_set (cell, "pixbuf", pixbuf, NULL);
975 	else
976 		g_object_set (cell, "icon-name", icon_name, NULL);
977 
978 	g_clear_object (&pixbuf);
979 	g_free (icon_name);
980 }
981 
982 static void
gedit_file_browser_view_init(GeditFileBrowserView * obj)983 gedit_file_browser_view_init (GeditFileBrowserView *obj)
984 {
985 	obj->priv = gedit_file_browser_view_get_instance_private (obj);
986 
987 	obj->priv->column = gtk_tree_view_column_new ();
988 
989 	obj->priv->pixbuf_renderer = gtk_cell_renderer_pixbuf_new ();
990 	gtk_tree_view_column_pack_start (obj->priv->column,
991 					 obj->priv->pixbuf_renderer,
992 					 FALSE);
993 
994 	gtk_tree_view_column_set_cell_data_func (obj->priv->column,
995 						 obj->priv->pixbuf_renderer,
996 						 (GtkTreeCellDataFunc)icon_renderer_cb,
997 						 obj,
998 						 NULL);
999 
1000 	obj->priv->text_renderer = gtk_cell_renderer_text_new ();
1001 	gtk_tree_view_column_pack_start (obj->priv->column,
1002 					 obj->priv->text_renderer, TRUE);
1003 	gtk_tree_view_column_add_attribute (obj->priv->column,
1004 					    obj->priv->text_renderer,
1005 					    "markup",
1006 					    GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP);
1007 
1008 	g_signal_connect (obj->priv->text_renderer, "edited",
1009 			  G_CALLBACK (on_cell_edited), obj);
1010 
1011 	gtk_tree_view_append_column (GTK_TREE_VIEW (obj),
1012 				     obj->priv->column);
1013 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (obj), FALSE);
1014 
1015 	gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (obj),
1016 						GDK_BUTTON1_MASK,
1017 						drag_source_targets,
1018 						G_N_ELEMENTS (drag_source_targets),
1019 						GDK_ACTION_COPY);
1020 }
1021 
1022 static gboolean
bookmarks_separator_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)1023 bookmarks_separator_func (GtkTreeModel *model,
1024 			  GtkTreeIter  *iter,
1025 			  gpointer      user_data)
1026 {
1027 	guint flags;
1028 
1029 	gtk_tree_model_get (model,
1030 			    iter,
1031 			    GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS, &flags,
1032 			    -1);
1033 
1034 	return (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_SEPARATOR);
1035 }
1036 
1037 /* Public */
1038 GtkWidget *
gedit_file_browser_view_new(void)1039 gedit_file_browser_view_new (void)
1040 {
1041 	GeditFileBrowserView *obj = GEDIT_FILE_BROWSER_VIEW (g_object_new (GEDIT_TYPE_FILE_BROWSER_VIEW, NULL));
1042 
1043 	return GTK_WIDGET (obj);
1044 }
1045 
1046 void
gedit_file_browser_view_set_model(GeditFileBrowserView * tree_view,GtkTreeModel * model)1047 gedit_file_browser_view_set_model (GeditFileBrowserView *tree_view,
1048 				   GtkTreeModel         *model)
1049 {
1050 	GtkTreeSelection *selection;
1051 	gint search_column;
1052 
1053 	if (tree_view->priv->model == model)
1054 		return;
1055 
1056 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
1057 
1058 	if (GEDIT_IS_FILE_BOOKMARKS_STORE (model))
1059 	{
1060 		gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1061 		gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (tree_view), bookmarks_separator_func, NULL, NULL);
1062 		gtk_tree_view_column_set_cell_data_func (tree_view->priv->column,
1063 							 tree_view->priv->text_renderer,
1064 							 (GtkTreeCellDataFunc)cell_data_cb,
1065 							 tree_view, NULL);
1066 		search_column = GEDIT_FILE_BOOKMARKS_STORE_COLUMN_NAME;
1067 	}
1068 	else
1069 	{
1070 		gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1071 		gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (tree_view), NULL, NULL, NULL);
1072 		gtk_tree_view_column_set_cell_data_func (tree_view->priv->column,
1073 							 tree_view->priv->text_renderer,
1074 							 (GtkTreeCellDataFunc)cell_data_cb,
1075 							 tree_view, NULL);
1076 		search_column = GEDIT_FILE_BROWSER_STORE_COLUMN_NAME;
1077 
1078 		if (tree_view->priv->restore_expand_state)
1079 			install_restore_signals (tree_view, model);
1080 
1081 	}
1082 
1083 	if (tree_view->priv->hover_path != NULL)
1084 	{
1085 		gtk_tree_path_free (tree_view->priv->hover_path);
1086 		tree_view->priv->hover_path = NULL;
1087 	}
1088 
1089 	if (GEDIT_IS_FILE_BROWSER_STORE (tree_view->priv->model) &&
1090 	    tree_view->priv->restore_expand_state)
1091 	{
1092 		uninstall_restore_signals (tree_view, tree_view->priv->model);
1093 	}
1094 
1095 	tree_view->priv->model = model;
1096 	gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), model);
1097 	gtk_tree_view_set_search_column (GTK_TREE_VIEW (tree_view), search_column);
1098 }
1099 
1100 void
gedit_file_browser_view_start_rename(GeditFileBrowserView * tree_view,GtkTreeIter * iter)1101 gedit_file_browser_view_start_rename (GeditFileBrowserView *tree_view,
1102 				      GtkTreeIter          *iter)
1103 {
1104 	gchar *name;
1105 	gchar *markup;
1106 	guint flags;
1107 	GValue name_escaped = G_VALUE_INIT;
1108 	GtkTreeRowReference *rowref;
1109 	GtkTreePath *path;
1110 
1111 	g_return_if_fail (GEDIT_IS_FILE_BROWSER_VIEW (tree_view));
1112 	g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_view->priv->model));
1113 	g_return_if_fail (iter != NULL);
1114 
1115 	gtk_tree_model_get (tree_view->priv->model,
1116 			    iter,
1117 	                    GEDIT_FILE_BROWSER_STORE_COLUMN_NAME, &name,
1118 	                    GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP, &markup,
1119 	                    GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
1120 	                    -1);
1121 
1122 	if (!(FILE_IS_DIR (flags) || !FILE_IS_DUMMY (flags)))
1123 	{
1124 		g_free (name);
1125 		g_free (markup);
1126 		return;
1127 	}
1128 
1129 	/* Restore the markup to the original
1130 	 * name, a plugin might have changed the markup.
1131 	 */
1132 	g_value_init (&name_escaped, G_TYPE_STRING);
1133 	g_value_take_string (&name_escaped, g_markup_escape_text (name, -1));
1134 	gedit_file_browser_store_set_value (GEDIT_FILE_BROWSER_STORE (tree_view->priv->model),
1135 					    iter,
1136 	                                    GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP, &name_escaped);
1137 
1138 	path = gtk_tree_model_get_path (tree_view->priv->model, iter);
1139 	rowref = gtk_tree_row_reference_new (tree_view->priv->model, path);
1140 
1141 	/* Start editing */
1142 	gtk_widget_grab_focus (GTK_WIDGET (tree_view));
1143 
1144 	if (gtk_tree_path_up (path))
1145 		gtk_tree_view_expand_to_path (GTK_TREE_VIEW (tree_view), path);
1146 
1147 	gtk_tree_path_free (path);
1148 
1149 	tree_view->priv->orig_markup = markup;
1150 	tree_view->priv->editable = rowref;
1151 
1152 	/* grab focus on the text cell which is editable */
1153 	gtk_tree_view_column_focus_cell (tree_view->priv->column, tree_view->priv->text_renderer);
1154 
1155 	path = gtk_tree_row_reference_get_path (tree_view->priv->editable),
1156 	gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree_view), path, tree_view->priv->column, TRUE);
1157 	gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree_view),
1158 				      path, tree_view->priv->column,
1159 				      FALSE, 0.0, 0.0);
1160 
1161 	gtk_tree_path_free (path);
1162 	g_value_unset (&name_escaped);
1163 	g_free (name);
1164 }
1165 
1166 void
gedit_file_browser_view_set_click_policy(GeditFileBrowserView * tree_view,GeditFileBrowserViewClickPolicy policy)1167 gedit_file_browser_view_set_click_policy (GeditFileBrowserView            *tree_view,
1168 					  GeditFileBrowserViewClickPolicy  policy)
1169 {
1170 	g_return_if_fail (GEDIT_IS_FILE_BROWSER_VIEW (tree_view));
1171 
1172 	set_click_policy_property (tree_view, policy);
1173 
1174 	g_object_notify (G_OBJECT (tree_view), "click-policy");
1175 }
1176 
1177 void
gedit_file_browser_view_set_restore_expand_state(GeditFileBrowserView * tree_view,gboolean restore_expand_state)1178 gedit_file_browser_view_set_restore_expand_state (GeditFileBrowserView *tree_view,
1179 						  gboolean              restore_expand_state)
1180 {
1181 	g_return_if_fail (GEDIT_IS_FILE_BROWSER_VIEW (tree_view));
1182 
1183 	set_restore_expand_state (tree_view, restore_expand_state);
1184 	g_object_notify (G_OBJECT (tree_view), "restore-expand-state");
1185 }
1186 
1187 /* Signal handlers */
1188 static void
on_cell_edited(GtkCellRendererText * cell,gchar * path,gchar * new_text,GeditFileBrowserView * tree_view)1189 on_cell_edited (GtkCellRendererText  *cell,
1190 		gchar                *path,
1191 		gchar                *new_text,
1192 		GeditFileBrowserView *tree_view)
1193 {
1194 	GtkTreePath *treepath = gtk_tree_path_new_from_string (path);
1195 	GtkTreeIter iter;
1196 	gboolean ret;
1197 	GValue orig_markup = G_VALUE_INIT;
1198 	GError *error = NULL;
1199 
1200 	ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_view->priv->model), &iter, treepath);
1201 	gtk_tree_path_free (treepath);
1202 
1203 	if (ret)
1204 	{
1205 		/* Restore the original markup */
1206 		g_value_init (&orig_markup, G_TYPE_STRING);
1207 		g_value_set_string (&orig_markup, tree_view->priv->orig_markup);
1208 		gedit_file_browser_store_set_value (GEDIT_FILE_BROWSER_STORE (tree_view->priv->model), &iter,
1209 		                                    GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP, &orig_markup);
1210 
1211 		if (new_text != NULL && *new_text != '\0' &&
1212 		    gedit_file_browser_store_rename (GEDIT_FILE_BROWSER_STORE (tree_view->priv->model),
1213 						     &iter,
1214 						     new_text,
1215 						     &error))
1216 		{
1217 			treepath = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_view->priv->model), &iter);
1218 			gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree_view),
1219 						      treepath, NULL,
1220 						      FALSE, 0.0, 0.0);
1221 			gtk_tree_path_free (treepath);
1222 		}
1223 		else if (error)
1224 		{
1225 			g_signal_emit (tree_view, signals[ERROR], 0, error->code, error->message);
1226 			g_error_free (error);
1227 		}
1228 
1229 		g_value_unset (&orig_markup);
1230 	}
1231 
1232 	g_free (tree_view->priv->orig_markup);
1233 	tree_view->priv->orig_markup = NULL;
1234 
1235 	gtk_tree_row_reference_free (tree_view->priv->editable);
1236 	tree_view->priv->editable = NULL;
1237 }
1238 
1239 static void
on_begin_refresh(GeditFileBrowserStore * model,GeditFileBrowserView * view)1240 on_begin_refresh (GeditFileBrowserStore *model,
1241 		  GeditFileBrowserView  *view)
1242 {
1243 	/* Store the refresh state, so we can handle unloading of nodes while
1244 	   refreshing properly */
1245 	view->priv->is_refresh = TRUE;
1246 }
1247 
1248 static void
on_end_refresh(GeditFileBrowserStore * model,GeditFileBrowserView * view)1249 on_end_refresh (GeditFileBrowserStore *model,
1250 		GeditFileBrowserView  *view)
1251 {
1252 	/* Store the refresh state, so we can handle unloading of nodes while
1253 	   refreshing properly */
1254 	view->priv->is_refresh = FALSE;
1255 }
1256 
1257 static void
on_unload(GeditFileBrowserStore * model,GFile * location,GeditFileBrowserView * view)1258 on_unload (GeditFileBrowserStore *model,
1259 	   GFile                 *location,
1260 	   GeditFileBrowserView  *view)
1261 {
1262 	/* Don't remove the expand state if we are refreshing */
1263 	if (!view->priv->restore_expand_state || view->priv->is_refresh)
1264 		return;
1265 
1266 	remove_expand_state (view, location);
1267 }
1268 
1269 static void
restore_expand_state(GeditFileBrowserView * view,GeditFileBrowserStore * model,GtkTreeIter * iter)1270 restore_expand_state (GeditFileBrowserView  *view,
1271 		      GeditFileBrowserStore *model,
1272 		      GtkTreeIter           *iter)
1273 {
1274 	GFile *location;
1275 
1276 	gtk_tree_model_get (GTK_TREE_MODEL (model),
1277 			    iter,
1278 			    GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location,
1279 			    -1);
1280 
1281 	if (location)
1282 	{
1283 		GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
1284 
1285 		if (g_hash_table_lookup (view->priv->expand_state, location))
1286 			gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, FALSE);
1287 
1288 		gtk_tree_path_free (path);
1289 		g_object_unref (location);
1290 	}
1291 }
1292 
1293 static void
on_row_inserted(GeditFileBrowserStore * model,GtkTreePath * path,GtkTreeIter * iter,GeditFileBrowserView * view)1294 on_row_inserted (GeditFileBrowserStore *model,
1295 		 GtkTreePath           *path,
1296 		 GtkTreeIter           *iter,
1297 		 GeditFileBrowserView  *view)
1298 {
1299 	GtkTreeIter parent;
1300 	GtkTreePath *copy;
1301 
1302 	if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (model), iter))
1303 		restore_expand_state (view, model, iter);
1304 
1305 	copy = gtk_tree_path_copy (path);
1306 
1307 	if (gtk_tree_path_up (copy) &&
1308 	    (gtk_tree_path_get_depth (copy) != 0) &&
1309 	    gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &parent, copy))
1310 	{
1311 		restore_expand_state (view, model, &parent);
1312 	}
1313 
1314 	gtk_tree_path_free (copy);
1315 }
1316 
1317 void
_gedit_file_browser_view_register_type(GTypeModule * type_module)1318 _gedit_file_browser_view_register_type (GTypeModule *type_module)
1319 {
1320 	gedit_file_browser_view_register_type (type_module);
1321 }
1322 
1323 /* ex:set ts=8 noet: */
1324