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