Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
nautilus-pathbar.c:1057:29 | clang-analyzer | Access to field 'data' results in a dereference of a null pointer (loaded from variable 'up_button') |
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* nautilus-pathbar.c
3 * Copyright (C) 2004 Red Hat, Inc., Jonathan Blandford <jrb@gnome.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library 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 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21
22 #include <config.h>
23 #include <string.h>
24 #include <gtk/gtk.h>
25 #include <glib/gi18n.h>
26 #include <gio/gio.h>
27
28 #include "nautilus-pathbar.h"
29
30 #include <libnautilus-private/nautilus-file.h>
31 #include <libnautilus-private/nautilus-file-utilities.h>
32 #include <libnautilus-private/nautilus-global-preferences.h>
33 #include <libnautilus-private/nautilus-icon-names.h>
34 #include <libnautilus-private/nautilus-trash-monitor.h>
35
36 #include "nautilus-window-slot-dnd.h"
37
38 enum {
39 PATH_CLICKED,
40 PATH_EVENT,
41 LAST_SIGNAL
42 };
43
44 typedef enum {
45 NORMAL_BUTTON,
46 ROOT_BUTTON,
47 HOME_BUTTON,
48 MOUNT_BUTTON
49 } ButtonType;
50
51 #define BUTTON_DATA(x) ((ButtonData *)(x))
52
53 #define SCROLL_TIMEOUT 150
54 #define INITIAL_SCROLL_TIMEOUT 300
55
56 static guint path_bar_signals [LAST_SIGNAL] = { 0 };
57
58 #define NAUTILUS_PATH_BAR_ICON_SIZE 16
59 #define NAUTILUS_PATH_BAR_BUTTON_MAX_WIDTH 250
60
61 typedef struct {
62 GtkWidget *button;
63 ButtonType type;
64 char *dir_name;
65 GFile *path;
66 NautilusFile *file;
67 unsigned int file_changed_signal_id;
68
69 GtkWidget *image;
70 GtkWidget *label;
71 GtkWidget *bold_label;
72
73 guint ignore_changes : 1;
74 guint is_root : 1;
75 } ButtonData;
76
77 struct _NautilusPathBarDetails {
78 GdkWindow *event_window;
79
80 GFile *current_path;
81 gpointer current_button_data;
82
83 GList *button_list;
84 GList *first_scrolled_button;
85 GtkWidget *up_slider_button;
86 GtkWidget *down_slider_button;
87 guint settings_signal_id;
88 gint16 slider_width;
89 guint timer;
90 guint slider_visible : 1;
91 guint need_timer : 1;
92 guint ignore_click : 1;
93
94 unsigned int drag_slider_timeout;
95 gboolean drag_slider_timeout_for_up_button;
96 };
97
98
99 G_DEFINE_TYPE (NautilusPathBar, nautilus_path_bar,
100 GTK_TYPE_CONTAINER);
101
102 static void nautilus_path_bar_scroll_up (NautilusPathBar *path_bar);
103 static void nautilus_path_bar_scroll_down (NautilusPathBar *path_bar);
104 static void nautilus_path_bar_stop_scrolling (NautilusPathBar *path_bar);
105 static gboolean nautilus_path_bar_slider_button_press (GtkWidget *widget,
106 GdkEventButton *event,
107 NautilusPathBar *path_bar);
108 static gboolean nautilus_path_bar_slider_button_release (GtkWidget *widget,
109 GdkEventButton *event,
110 NautilusPathBar *path_bar);
111 static void nautilus_path_bar_check_icon_theme (NautilusPathBar *path_bar);
112 static void nautilus_path_bar_update_button_appearance (ButtonData *button_data);
113 static void nautilus_path_bar_update_button_state (ButtonData *button_data,
114 gboolean current_dir);
115 static void nautilus_path_bar_update_path (NautilusPathBar *path_bar,
116 GFile *file_path);
117
118 static GtkWidget *
119 get_slider_button (NautilusPathBar *path_bar,
120 GtkArrowType arrow_type)
121 {
122 GtkWidget *button;
123
124 gtk_widget_push_composite_child ();
125
126 button = gtk_button_new ();
127 gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
128 gtk_widget_add_events (button, GDK_SCROLL_MASK);
129 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (arrow_type, GTK_SHADOW_OUT));
130 gtk_container_add (GTK_CONTAINER (path_bar), button);
131 gtk_widget_show_all (button);
132
133 gtk_widget_pop_composite_child ();
134
135 return button;
136 }
137
138 static gboolean
139 slider_timeout (gpointer user_data)
140 {
141 NautilusPathBar *path_bar;
142
143 path_bar = NAUTILUS_PATH_BAR (user_data);
144
145 path_bar->priv->drag_slider_timeout = 0;
146
147 if (gtk_widget_get_visible (GTK_WIDGET (path_bar))) {
148 if (path_bar->priv->drag_slider_timeout_for_up_button) {
149 nautilus_path_bar_scroll_up (path_bar);
150 } else {
151 nautilus_path_bar_scroll_down (path_bar);
152 }
153 }
154
155 return FALSE;
156 }
157
158 static void
159 nautilus_path_bar_slider_drag_motion (GtkWidget *widget,
160 GdkDragContext *context,
161 int x,
162 int y,
163 unsigned int time,
164 gpointer user_data)
165 {
166 NautilusPathBar *path_bar;
167 GtkSettings *settings;
168 unsigned int timeout;
169
170 path_bar = NAUTILUS_PATH_BAR (user_data);
171
172 if (path_bar->priv->drag_slider_timeout == 0) {
173 settings = gtk_widget_get_settings (widget);
174
175 g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
176 path_bar->priv->drag_slider_timeout =
177 g_timeout_add (timeout,
178 slider_timeout,
179 path_bar);
180
181 path_bar->priv->drag_slider_timeout_for_up_button =
182 widget == path_bar->priv->up_slider_button;
183 }
184 }
185
186 static void
187 nautilus_path_bar_slider_drag_leave (GtkWidget *widget,
188 GdkDragContext *context,
189 unsigned int time,
190 gpointer user_data)
191 {
192 NautilusPathBar *path_bar;
193
194 path_bar = NAUTILUS_PATH_BAR (user_data);
195
196 if (path_bar->priv->drag_slider_timeout != 0) {
197 g_source_remove (path_bar->priv->drag_slider_timeout);
198 path_bar->priv->drag_slider_timeout = 0;
199 }
200 }
201
202 static void
203 nautilus_path_bar_init (NautilusPathBar *path_bar)
204 {
205 path_bar->priv = G_TYPE_INSTANCE_GET_PRIVATE (path_bar, NAUTILUS_TYPE_PATH_BAR, NautilusPathBarDetails);
206
207 gtk_widget_set_has_window (GTK_WIDGET (path_bar), FALSE);
208 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (path_bar), FALSE);
209
210 path_bar->priv->up_slider_button = get_slider_button (path_bar, GTK_ARROW_LEFT);
211 path_bar->priv->down_slider_button = get_slider_button (path_bar, GTK_ARROW_RIGHT);
212
213 g_signal_connect_swapped (path_bar->priv->up_slider_button, "clicked", G_CALLBACK (nautilus_path_bar_scroll_up), path_bar);
214 g_signal_connect_swapped (path_bar->priv->down_slider_button, "clicked", G_CALLBACK (nautilus_path_bar_scroll_down), path_bar);
215
216 g_signal_connect (path_bar->priv->up_slider_button, "button_press_event", G_CALLBACK (nautilus_path_bar_slider_button_press), path_bar);
217 g_signal_connect (path_bar->priv->up_slider_button, "button_release_event", G_CALLBACK (nautilus_path_bar_slider_button_release), path_bar);
218 g_signal_connect (path_bar->priv->down_slider_button, "button_press_event", G_CALLBACK (nautilus_path_bar_slider_button_press), path_bar);
219 g_signal_connect (path_bar->priv->down_slider_button, "button_release_event", G_CALLBACK (nautilus_path_bar_slider_button_release), path_bar);
220
221 gtk_drag_dest_set (GTK_WIDGET (path_bar->priv->up_slider_button),
222 0, NULL, 0, 0);
223 gtk_drag_dest_set_track_motion (GTK_WIDGET (path_bar->priv->up_slider_button), TRUE);
224 g_signal_connect (path_bar->priv->up_slider_button,
225 "drag-motion",
226 G_CALLBACK (nautilus_path_bar_slider_drag_motion),
227 path_bar);
228 g_signal_connect (path_bar->priv->up_slider_button,
229 "drag-leave",
230 G_CALLBACK (nautilus_path_bar_slider_drag_leave),
231 path_bar);
232
233 gtk_drag_dest_set (GTK_WIDGET (path_bar->priv->down_slider_button),
234 0, NULL, 0, 0);
235 gtk_drag_dest_set_track_motion (GTK_WIDGET (path_bar->priv->up_slider_button), TRUE);
236 g_signal_connect (path_bar->priv->down_slider_button,
237 "drag-motion",
238 G_CALLBACK (nautilus_path_bar_slider_drag_motion),
239 path_bar);
240 g_signal_connect (path_bar->priv->down_slider_button,
241 "drag-leave",
242 G_CALLBACK (nautilus_path_bar_slider_drag_leave),
243 path_bar);
244
245 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (path_bar)),
246 GTK_STYLE_CLASS_LINKED);
247 }
248
249 static void
250 nautilus_path_bar_finalize (GObject *object)
251 {
252 NautilusPathBar *path_bar;
253
254 path_bar = NAUTILUS_PATH_BAR (object);
255
256 nautilus_path_bar_stop_scrolling (path_bar);
257
258 if (path_bar->priv->drag_slider_timeout != 0) {
259 g_source_remove (path_bar->priv->drag_slider_timeout);
260 path_bar->priv->drag_slider_timeout = 0;
261 }
262
263 g_list_free (path_bar->priv->button_list);
264
265 G_OBJECT_CLASS (nautilus_path_bar_parent_class)->finalize (object);
266 }
267
268 /* Removes the settings signal handler. It's safe to call multiple times */
269 static void
270 remove_settings_signal (NautilusPathBar *path_bar,
271 GdkScreen *screen)
272 {
273 if (path_bar->priv->settings_signal_id) {
274 GtkSettings *settings;
275
276 settings = gtk_settings_get_for_screen (screen);
277 g_signal_handler_disconnect (settings,
278 path_bar->priv->settings_signal_id);
279 path_bar->priv->settings_signal_id = 0;
280 }
281 }
282
283 static void
284 nautilus_path_bar_dispose (GObject *object)
285 {
286 remove_settings_signal (NAUTILUS_PATH_BAR (object), gtk_widget_get_screen (GTK_WIDGET (object)));
287
288 G_OBJECT_CLASS (nautilus_path_bar_parent_class)->dispose (object);
289 }
290
291 static const char *
292 get_dir_name (ButtonData *button_data)
293 {
294 if (button_data->type == HOME_BUTTON) {
295 return _("Home");
296 } else {
297 return button_data->dir_name;
298 }
299 }
300
301 /* We always want to request the same size for the label, whether
302 * or not the contents are bold
303 */
304 static void
305 set_label_size_request (ButtonData *button_data)
306 {
307 gint width, height;
308 GtkRequisition min_req, bold_req;
309
310 if (button_data->label == NULL) {
311 return;
312 }
313
314 gtk_label_set_ellipsize (GTK_LABEL (button_data->label), PANGO_ELLIPSIZE_NONE);
315 gtk_widget_get_preferred_size (button_data->label, &min_req, NULL);
316 gtk_label_set_ellipsize (GTK_LABEL (button_data->label), PANGO_ELLIPSIZE_MIDDLE);
317
318 gtk_widget_get_preferred_size (button_data->bold_label, &bold_req, NULL);
319
320 width = MAX (min_req.width, bold_req.width);
321 width = MIN (width, NAUTILUS_PATH_BAR_BUTTON_MAX_WIDTH);
322 height = MAX (min_req.height, bold_req.height);
323
324 gtk_widget_set_size_request (button_data->label, width, height);
325 }
326
327 /* Size requisition:
328 *
329 * Ideally, our size is determined by another widget, and we are just filling
330 * available space.
331 */
332 static void
333 nautilus_path_bar_get_preferred_width (GtkWidget *widget,
334 gint *minimum,
335 gint *natural)
336 {
337 ButtonData *button_data;
338 NautilusPathBar *path_bar;
339 GList *list;
340 gint child_height;
341 gint height;
342 gint child_min, child_nat;
343
344 path_bar = NAUTILUS_PATH_BAR (widget);
345
346 *minimum = *natural = 0;
347 height = 0;
348
349 for (list = path_bar->priv->button_list; list; list = list->next) {
350 button_data = BUTTON_DATA (list->data);
351 set_label_size_request (button_data);
352
353 gtk_widget_get_preferred_width (button_data->button, &child_min, &child_nat);
354 gtk_widget_get_preferred_height (button_data->button, &child_height, NULL);
355 height = MAX (height, child_height);
356
357 if (button_data->type == NORMAL_BUTTON) {
358 /* Use 2*Height as button width because of ellipsized label. */
359 child_min = MAX (child_min, child_height * 2);
360 child_nat = MAX (child_min, child_height * 2);
361 }
362
363 *minimum = MAX (*minimum, child_min);
364 *natural = MAX (*natural, child_nat);
365 }
366
367 /* Add space for slider, if we have more than one path */
368 /* Theoretically, the slider could be bigger than the other button. But we're
369 * not going to worry about that now.
370 */
371 path_bar->priv->slider_width = MIN (height * 2 / 3 + 5, height);
372
373 if (path_bar->priv->button_list && path_bar->priv->button_list->next != NULL) {
374 *minimum += (path_bar->priv->slider_width) * 2;
375 *natural += (path_bar->priv->slider_width) * 2;
376 }
377 }
378
379 static void
380 nautilus_path_bar_get_preferred_height (GtkWidget *widget,
381 gint *minimum,
382 gint *natural)
383 {
384 ButtonData *button_data;
385 NautilusPathBar *path_bar;
386 GList *list;
387 gint child_min, child_nat;
388
389 path_bar = NAUTILUS_PATH_BAR (widget);
390
391 *minimum = *natural = 0;
392
393 for (list = path_bar->priv->button_list; list; list = list->next) {
394 button_data = BUTTON_DATA (list->data);
395 set_label_size_request (button_data);
396
397 gtk_widget_get_preferred_height (button_data->button, &child_min, &child_nat);
398
399 *minimum = MAX (*minimum, child_min);
400 *natural = MAX (*natural, child_nat);
401 }
402 }
403
404 static void
405 nautilus_path_bar_update_slider_buttons (NautilusPathBar *path_bar)
406 {
407 if (path_bar->priv->button_list) {
408
409 GtkWidget *button;
410
411 button = BUTTON_DATA (path_bar->priv->button_list->data)->button;
412 if (gtk_widget_get_child_visible (button)) {
413 gtk_widget_set_sensitive (path_bar->priv->down_slider_button, FALSE);
414 } else {
415 gtk_widget_set_sensitive (path_bar->priv->down_slider_button, TRUE);
416 }
417 button = BUTTON_DATA (g_list_last (path_bar->priv->button_list)->data)->button;
418 if (gtk_widget_get_child_visible (button)) {
419 gtk_widget_set_sensitive (path_bar->priv->up_slider_button, FALSE);
420 } else {
421 gtk_widget_set_sensitive (path_bar->priv->up_slider_button, TRUE);
422 }
423 }
424 }
425
426 static void
427 nautilus_path_bar_unmap (GtkWidget *widget)
428 {
429 nautilus_path_bar_stop_scrolling (NAUTILUS_PATH_BAR (widget));
430 gdk_window_hide (NAUTILUS_PATH_BAR (widget)->priv->event_window);
431
432 GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->unmap (widget);
433 }
434
435 static void
436 nautilus_path_bar_map (GtkWidget *widget)
437 {
438 gdk_window_show (NAUTILUS_PATH_BAR (widget)->priv->event_window);
439
440 GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->map (widget);
441 }
442
443
444 static void
445 child_ordering_changed (NautilusPathBar *path_bar)
446 {
447 GList *l;
448
449 if (path_bar->priv->up_slider_button) {
450 gtk_style_context_invalidate (gtk_widget_get_style_context (path_bar->priv->up_slider_button));
451 }
452 if (path_bar->priv->down_slider_button) {
453 gtk_style_context_invalidate (gtk_widget_get_style_context (path_bar->priv->down_slider_button));
454 }
455
456 for (l = path_bar->priv->button_list; l; l = l->next) {
457 ButtonData *data = l->data;
458 gtk_style_context_invalidate (gtk_widget_get_style_context (data->button));
459 }
460 }
461
462 /* This is a tad complicated */
463 static void
464 nautilus_path_bar_size_allocate (GtkWidget *widget,
465 GtkAllocation *allocation)
466 {
467 GtkWidget *child;
468 NautilusPathBar *path_bar;
469 GtkTextDirection direction;
470 GtkAllocation child_allocation;
471 GList *list, *first_button;
472 gint width;
473 gint largest_width;
474 gboolean need_sliders;
475 gint up_slider_offset;
476 gint down_slider_offset;
477 GtkRequisition child_requisition;
478 gboolean needs_reorder = FALSE;
479
480 need_sliders = FALSE;
481 up_slider_offset = 0;
482 down_slider_offset = 0;
483 path_bar = NAUTILUS_PATH_BAR (widget);
484
485 gtk_widget_set_allocation (widget, allocation);
486
487 if (gtk_widget_get_realized (widget)) {
488 gdk_window_move_resize (path_bar->priv->event_window,
489 allocation->x, allocation->y,
490 allocation->width, allocation->height);
491 }
492
493 /* No path is set so we don't have to allocate anything. */
494 if (path_bar->priv->button_list == NULL) {
495 return;
496 }
497 direction = gtk_widget_get_direction (widget);
498
499 /* First, we check to see if we need the scrollbars. */
500 width = 0;
501
502 gtk_widget_get_preferred_size (BUTTON_DATA (path_bar->priv->button_list->data)->button,
503 &child_requisition, NULL);
504 width += child_requisition.width;
505
506 for (list = path_bar->priv->button_list->next; list; list = list->next) {
507 child = BUTTON_DATA (list->data)->button;
508 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
509 width += child_requisition.width;
510 }
511
512 if (width <= allocation->width) {
513 first_button = g_list_last (path_bar->priv->button_list);
514 } else {
515 gboolean reached_end;
516 gint slider_space;
517 reached_end = FALSE;
518 slider_space = 2 * (path_bar->priv->slider_width);
519
520 if (path_bar->priv->first_scrolled_button) {
521 first_button = path_bar->priv->first_scrolled_button;
522 } else {
523 first_button = path_bar->priv->button_list;
524 }
525
526 need_sliders = TRUE;
527 /* To see how much space we have, and how many buttons we can display.
528 * We start at the first button, count forward until hit the new
529 * button, then count backwards.
530 */
531 /* Count down the path chain towards the end. */
532 gtk_widget_get_preferred_size (BUTTON_DATA (first_button->data)->button,
533 &child_requisition, NULL);
534 width = child_requisition.width;
535 list = first_button->prev;
536 while (list && !reached_end) {
537 child = BUTTON_DATA (list->data)->button;
538 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
539
540 if (width + child_requisition.width + slider_space > allocation->width) {
541 reached_end = TRUE;
542 } else {
543 width += child_requisition.width;
544 }
545
546 list = list->prev;
547 }
548
549 /* Finally, we walk up, seeing how many of the previous buttons we can add*/
550
551 while (first_button->next && ! reached_end) {
552 child = BUTTON_DATA (first_button->next->data)->button;
553 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
554
555 if (width + child_requisition.width + slider_space > allocation->width) {
556 reached_end = TRUE;
557 } else {
558 width += child_requisition.width;
559 first_button = first_button->next;
560 }
561 }
562 }
563
564 /* Now, we allocate space to the buttons */
565 child_allocation.y = allocation->y;
566 child_allocation.height = allocation->height;
567
568 if (direction == GTK_TEXT_DIR_RTL) {
569 child_allocation.x = allocation->x + allocation->width;
570 if (need_sliders) {
571 child_allocation.x -= path_bar->priv->slider_width;
572 up_slider_offset = allocation->width - path_bar->priv->slider_width;
573 }
574 } else {
575 child_allocation.x = allocation->x;
576 if (need_sliders) {
577 up_slider_offset = 0;
578 child_allocation.x += path_bar->priv->slider_width;
579 }
580 }
581
582 /* Determine the largest possible allocation size */
583 largest_width = allocation->width;
584 if (need_sliders) {
585 largest_width -= (path_bar->priv->slider_width) * 2;
586 }
587
588 for (list = first_button; list; list = list->prev) {
589 child = BUTTON_DATA (list->data)->button;
590 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
591
592 child_allocation.width = MIN (child_requisition.width, largest_width);
593 if (direction == GTK_TEXT_DIR_RTL) {
594 child_allocation.x -= child_allocation.width;
595 }
596 /* Check to see if we've don't have any more space to allocate buttons */
597 if (need_sliders && direction == GTK_TEXT_DIR_RTL) {
598 if (child_allocation.x - path_bar->priv->slider_width < allocation->x) {
599 break;
600 }
601 } else {
602 if (need_sliders && direction == GTK_TEXT_DIR_LTR) {
603 if (child_allocation.x + child_allocation.width + path_bar->priv->slider_width > allocation->x + allocation->width) {
604 break;
605 }
606 }
607 }
608
609 needs_reorder |= gtk_widget_get_child_visible (child) == FALSE;
610 gtk_widget_set_child_visible (child, TRUE);
611 gtk_widget_size_allocate (child, &child_allocation);
612
613 if (direction == GTK_TEXT_DIR_RTL) {
614 down_slider_offset = child_allocation.x - allocation->x - path_bar->priv->slider_width;
615 } else {
616 down_slider_offset += child_allocation.width;
617 child_allocation.x += child_allocation.width;
618 }
619 }
620 /* Now we go hide all the widgets that don't fit */
621 while (list) {
622 child = BUTTON_DATA (list->data)->button;
623 needs_reorder |= gtk_widget_get_child_visible (child) == TRUE;
624 gtk_widget_set_child_visible (child, FALSE);
625 list = list->prev;
626 }
627 for (list = first_button->next; list; list = list->next) {
628 child = BUTTON_DATA (list->data)->button;
629 needs_reorder |= gtk_widget_get_child_visible (child) == TRUE;
630 gtk_widget_set_child_visible (child, FALSE);
631 }
632
633 if (need_sliders) {
634 child_allocation.width = path_bar->priv->slider_width;
635 child_allocation.x = up_slider_offset + allocation->x;
636 gtk_widget_size_allocate (path_bar->priv->up_slider_button, &child_allocation);
637
638 needs_reorder |= gtk_widget_get_child_visible (path_bar->priv->up_slider_button) == FALSE;
639 gtk_widget_set_child_visible (path_bar->priv->up_slider_button, TRUE);
640 gtk_widget_show_all (path_bar->priv->up_slider_button);
641
642 if (direction == GTK_TEXT_DIR_LTR) {
643 down_slider_offset += path_bar->priv->slider_width;
644 }
645 } else {
646 needs_reorder |= gtk_widget_get_child_visible (path_bar->priv->up_slider_button) == TRUE;
647 gtk_widget_set_child_visible (path_bar->priv->up_slider_button, FALSE);
648 }
649
650 if (need_sliders) {
651 child_allocation.width = path_bar->priv->slider_width;
652 child_allocation.x = down_slider_offset + allocation->x;
653 gtk_widget_size_allocate (path_bar->priv->down_slider_button, &child_allocation);
654
655 needs_reorder |= gtk_widget_get_child_visible (path_bar->priv->down_slider_button) == FALSE;
656 gtk_widget_set_child_visible (path_bar->priv->down_slider_button, TRUE);
657 gtk_widget_show_all (path_bar->priv->down_slider_button);
658 nautilus_path_bar_update_slider_buttons (path_bar);
659 } else {
660 needs_reorder |= gtk_widget_get_child_visible (path_bar->priv->down_slider_button) == TRUE;
661 gtk_widget_set_child_visible (path_bar->priv->down_slider_button, FALSE);
662 }
663
664 if (needs_reorder) {
665 child_ordering_changed (path_bar);
666 }
667 }
668
669 static void
670 nautilus_path_bar_style_updated (GtkWidget *widget)
671 {
672 GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->style_updated (widget);
673
674 nautilus_path_bar_check_icon_theme (NAUTILUS_PATH_BAR (widget));
675 }
676
677 static void
678 nautilus_path_bar_screen_changed (GtkWidget *widget,
679 GdkScreen *previous_screen)
680 {
681 if (GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->screen_changed) {
682 GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->screen_changed (widget, previous_screen);
683 }
684 /* We might nave a new settings, so we remove the old one */
685 if (previous_screen) {
686 remove_settings_signal (NAUTILUS_PATH_BAR (widget), previous_screen);
687 }
688 nautilus_path_bar_check_icon_theme (NAUTILUS_PATH_BAR (widget));
689 }
690
691 static gboolean
692 nautilus_path_bar_scroll (GtkWidget *widget,
693 GdkEventScroll *event)
694 {
695 NautilusPathBar *path_bar;
696
697 path_bar = NAUTILUS_PATH_BAR (widget);
698
699 switch (event->direction) {
700 case GDK_SCROLL_RIGHT:
701 case GDK_SCROLL_DOWN:
702 nautilus_path_bar_scroll_down (path_bar);
703 return TRUE;
704
705 case GDK_SCROLL_LEFT:
706 case GDK_SCROLL_UP:
707 nautilus_path_bar_scroll_up (path_bar);
708 return TRUE;
709 case GDK_SCROLL_SMOOTH:
710 break;
711 }
712
713 return FALSE;
714 }
715
716 static void
717 nautilus_path_bar_realize (GtkWidget *widget)
718 {
719 NautilusPathBar *path_bar;
720 GtkAllocation allocation;
721 GdkWindow *window;
722 GdkWindowAttr attributes;
723 gint attributes_mask;
724
725 gtk_widget_set_realized (widget, TRUE);
726
727 path_bar = NAUTILUS_PATH_BAR (widget);
728 window = gtk_widget_get_parent_window (widget);
729 gtk_widget_set_window (widget, window);
730 g_object_ref (window);
731
732 gtk_widget_get_allocation (widget, &allocation);
733
734 attributes.window_type = GDK_WINDOW_CHILD;
735 attributes.x = allocation.x;
736 attributes.y = allocation.y;
737 attributes.width = allocation.width;
738 attributes.height = allocation.height;
739 attributes.wclass = GDK_INPUT_ONLY;
740 attributes.event_mask = gtk_widget_get_events (widget);
741 attributes.event_mask |=
742 GDK_SCROLL_MASK |
743 GDK_BUTTON_PRESS_MASK |
744 GDK_BUTTON_RELEASE_MASK;
745 attributes_mask = GDK_WA_X | GDK_WA_Y;
746
747 path_bar->priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
748 &attributes, attributes_mask);
749 gdk_window_set_user_data (path_bar->priv->event_window, widget);
750 }
751
752 static void
753 nautilus_path_bar_unrealize (GtkWidget *widget)
754 {
755 NautilusPathBar *path_bar;
756
757 path_bar = NAUTILUS_PATH_BAR (widget);
758
759 gdk_window_set_user_data (path_bar->priv->event_window, NULL);
760 gdk_window_destroy (path_bar->priv->event_window);
761 path_bar->priv->event_window = NULL;
762
763 GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->unrealize (widget);
764 }
765
766 static void
767 nautilus_path_bar_add (GtkContainer *container,
768 GtkWidget *widget)
769 {
770 gtk_widget_set_parent (widget, GTK_WIDGET (container));
771 }
772
773 static void
774 nautilus_path_bar_remove_1 (GtkContainer *container,
775 GtkWidget *widget)
776 {
777 gboolean was_visible = gtk_widget_get_visible (widget);
778 gtk_widget_unparent (widget);
779 if (was_visible) {
780 gtk_widget_queue_resize (GTK_WIDGET (container));
781 }
782 }
783
784 static void
785 nautilus_path_bar_remove (GtkContainer *container,
786 GtkWidget *widget)
787 {
788 NautilusPathBar *path_bar;
789 GList *children;
790
791 path_bar = NAUTILUS_PATH_BAR (container);
792
793 if (widget == path_bar->priv->up_slider_button) {
794 nautilus_path_bar_remove_1 (container, widget);
795 path_bar->priv->up_slider_button = NULL;
796 return;
797 }
798
799 if (widget == path_bar->priv->down_slider_button) {
800 nautilus_path_bar_remove_1 (container, widget);
801 path_bar->priv->down_slider_button = NULL;
802 return;
803 }
804
805 children = path_bar->priv->button_list;
806 while (children) {
807 if (widget == BUTTON_DATA (children->data)->button) {
808 nautilus_path_bar_remove_1 (container, widget);
809 path_bar->priv->button_list = g_list_remove_link (path_bar->priv->button_list, children);
810 g_list_free_1 (children);
811 return;
812 }
813 children = children->next;
814 }
815 }
816
817 static void
818 nautilus_path_bar_forall (GtkContainer *container,
819 gboolean include_internals,
820 GtkCallback callback,
821 gpointer callback_data)
822 {
823 NautilusPathBar *path_bar;
824 GList *children;
825
826 g_return_if_fail (callback != NULL);
827 path_bar = NAUTILUS_PATH_BAR (container);
828
829 children = path_bar->priv->button_list;
830 while (children) {
831 GtkWidget *child;
832 child = BUTTON_DATA (children->data)->button;
833 children = children->next;
834 (* callback) (child, callback_data);
835 }
836
837 if (path_bar->priv->up_slider_button) {
838 (* callback) (path_bar->priv->up_slider_button, callback_data);
839 }
840
841 if (path_bar->priv->down_slider_button) {
842 (* callback) (path_bar->priv->down_slider_button, callback_data);
843 }
844 }
845
846 static void
847 nautilus_path_bar_grab_notify (GtkWidget *widget,
848 gboolean was_grabbed)
849 {
850 if (!was_grabbed) {
851 nautilus_path_bar_stop_scrolling (NAUTILUS_PATH_BAR (widget));
852 }
853 }
854
855 static void
856 nautilus_path_bar_state_changed (GtkWidget *widget,
857 GtkStateType previous_state)
858 {
859 if (!gtk_widget_get_sensitive (widget)) {
860 nautilus_path_bar_stop_scrolling (NAUTILUS_PATH_BAR (widget));
861 }
862 }
863
864 static GtkWidgetPath *
865 nautilus_path_bar_get_path_for_child (GtkContainer *container,
866 GtkWidget *child)
867 {
868 NautilusPathBar *path_bar = NAUTILUS_PATH_BAR (container);
869 GtkWidgetPath *path;
870
871 path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (path_bar)));
872
873 if (gtk_widget_get_visible (child) &&
874 gtk_widget_get_child_visible (child)) {
875 GtkWidgetPath *sibling_path;
876 GList *visible_children;
877 GList *l;
878 int pos;
879
880 /* 1. Build the list of visible children, in visually left-to-right order
881 * (i.e. independently of the widget's direction). Note that our
882 * button_list is stored in innermost-to-outermost path order!
883 */
884
885 visible_children = NULL;
886
887 if (gtk_widget_get_visible (path_bar->priv->down_slider_button) &&
888 gtk_widget_get_child_visible (path_bar->priv->down_slider_button)) {
889 visible_children = g_list_prepend (visible_children, path_bar->priv->down_slider_button);
890 }
891
892 for (l = path_bar->priv->button_list; l; l = l->next) {
893 ButtonData *data = l->data;
894
895 if (gtk_widget_get_visible (data->button) &&
896 gtk_widget_get_child_visible (data->button))
897 visible_children = g_list_prepend (visible_children, data->button);
898 }
899
900 if (gtk_widget_get_visible (path_bar->priv->up_slider_button) &&
901 gtk_widget_get_child_visible (path_bar->priv->up_slider_button)) {
902 visible_children = g_list_prepend (visible_children, path_bar->priv->up_slider_button);
903 }
904
905 if (gtk_widget_get_direction (GTK_WIDGET (path_bar)) == GTK_TEXT_DIR_RTL) {
906 visible_children = g_list_reverse (visible_children);
907 }
908
909 /* 2. Find the index of the child within that list */
910
911 pos = 0;
912
913 for (l = visible_children; l; l = l->next) {
914 GtkWidget *button = l->data;
915
916 if (button == child) {
917 break;
918 }
919
920 pos++;
921 }
922
923 /* 3. Build the path */
924
925 sibling_path = gtk_widget_path_new ();
926
927 for (l = visible_children; l; l = l->next) {
928 gtk_widget_path_append_for_widget (sibling_path, l->data);
929 }
930
931 gtk_widget_path_append_with_siblings (path, sibling_path, pos);
932
933 g_list_free (visible_children);
934 gtk_widget_path_unref (sibling_path);
935 } else {
936 gtk_widget_path_append_for_widget (path, child);
937 }
938
939 return path;
940 }
941
942 static void
943 nautilus_path_bar_class_init (NautilusPathBarClass *path_bar_class)
944 {
945 GObjectClass *gobject_class;
946 GtkWidgetClass *widget_class;
947 GtkContainerClass *container_class;
948
949 gobject_class = (GObjectClass *) path_bar_class;
950 widget_class = (GtkWidgetClass *) path_bar_class;
951 container_class = (GtkContainerClass *) path_bar_class;
952
953 gobject_class->finalize = nautilus_path_bar_finalize;
954 gobject_class->dispose = nautilus_path_bar_dispose;
955
956 widget_class->get_preferred_height = nautilus_path_bar_get_preferred_height;
957 widget_class->get_preferred_width = nautilus_path_bar_get_preferred_width;
958 widget_class->realize = nautilus_path_bar_realize;
959 widget_class->unrealize = nautilus_path_bar_unrealize;
960 widget_class->unmap = nautilus_path_bar_unmap;
961 widget_class->map = nautilus_path_bar_map;
962 widget_class->size_allocate = nautilus_path_bar_size_allocate;
963 widget_class->style_updated = nautilus_path_bar_style_updated;
964 widget_class->screen_changed = nautilus_path_bar_screen_changed;
965 widget_class->grab_notify = nautilus_path_bar_grab_notify;
966 widget_class->state_changed = nautilus_path_bar_state_changed;
967 widget_class->scroll_event = nautilus_path_bar_scroll;
968
969 container_class->add = nautilus_path_bar_add;
970 container_class->forall = nautilus_path_bar_forall;
971 container_class->remove = nautilus_path_bar_remove;
972 container_class->get_path_for_child = nautilus_path_bar_get_path_for_child;
973
974 path_bar_signals [PATH_CLICKED] =
975 g_signal_new ("path-clicked",
976 G_OBJECT_CLASS_TYPE (path_bar_class),
977 G_SIGNAL_RUN_FIRST,
978 G_STRUCT_OFFSET (NautilusPathBarClass, path_clicked),
979 NULL, NULL,
980 g_cclosure_marshal_VOID__OBJECT,
981 G_TYPE_NONE, 1,
982 G_TYPE_FILE);
983 path_bar_signals [PATH_EVENT] =
984 g_signal_new ("path-event",
985 G_OBJECT_CLASS_TYPE (path_bar_class),
986 G_SIGNAL_RUN_FIRST,
987 G_STRUCT_OFFSET (NautilusPathBarClass, path_event),
988 NULL, NULL, NULL,
989 G_TYPE_NONE, 2,
990 G_TYPE_FILE,
991 GDK_TYPE_EVENT);
992
993 gtk_container_class_handle_border_width (container_class);
994 g_type_class_add_private (path_bar_class, sizeof (NautilusPathBarDetails));
995 }
996
997 static void
998 nautilus_path_bar_scroll_down (NautilusPathBar *path_bar)
999 {
1000 GList *list;
1001 GList *down_button;
1002 GList *up_button;
1003 gint space_available;
1004 gint space_needed;
1005 GtkTextDirection direction;
1006 GtkAllocation allocation, button_allocation, slider_allocation;
1007
1008 down_button = NULL;
1009 up_button = NULL;
1010
1011 if (path_bar->priv->ignore_click) {
1012 path_bar->priv->ignore_click = FALSE;
1013 return;
1014 }
1015
1016 gtk_widget_queue_resize (GTK_WIDGET (path_bar));
1017
1018 direction = gtk_widget_get_direction (GTK_WIDGET (path_bar));
1019
1020 /* We find the button at the 'down' end that we have to make */
1021 /* visible */
1022 for (list = path_bar->priv->button_list; list; list = list->next) {
1023 if (list->next && gtk_widget_get_child_visible (BUTTON_DATA (list->next->data)->button)) {
1024 down_button = list;
1025 break;
1026 }
1027 }
1028
1029 if (down_button == NULL) {
1030 return;
1031 }
1032
1033 /* Find the last visible button on the 'up' end */
1034 for (list = g_list_last (path_bar->priv->button_list); list; list = list->prev) {
1035 if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button)) {
1036 up_button = list;
1037 break;
1038 }
1039 }
1040
1041 gtk_widget_get_allocation (BUTTON_DATA (down_button->data)->button, &button_allocation);
1042 gtk_widget_get_allocation (GTK_WIDGET (path_bar), &allocation);
1043 gtk_widget_get_allocation (path_bar->priv->down_slider_button, &slider_allocation);
1044
1045 space_needed = button_allocation.width;
1046 if (direction == GTK_TEXT_DIR_RTL) {
1047 space_available = slider_allocation.x - allocation.x;
1048 } else {
1049 space_available = (allocation.x + allocation.width) -
1050 (slider_allocation.x + slider_allocation.width);
1051 }
1052
1053 /* We have space_available extra space that's not being used. We
1054 * need space_needed space to make the button fit. So we walk down
1055 * from the end, removing buttons until we get all the space we
1056 * need. */
1057 gtk_widget_get_allocation (BUTTON_DATA (up_button->data)->button, &button_allocation);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1058 while ((space_available < space_needed) &&
1059 (up_button != NULL)) {
1060 space_available += button_allocation.width;
1061 up_button = up_button->prev;
1062 path_bar->priv->first_scrolled_button = up_button;
1063 }
1064 }
1065
1066 static void
1067 nautilus_path_bar_scroll_up (NautilusPathBar *path_bar)
1068 {
1069 GList *list;
1070
1071 if (path_bar->priv->ignore_click) {
1072 path_bar->priv->ignore_click = FALSE;
1073 return;
1074 }
1075
1076 gtk_widget_queue_resize (GTK_WIDGET (path_bar));
1077
1078 for (list = g_list_last (path_bar->priv->button_list); list; list = list->prev) {
1079 if (list->prev && gtk_widget_get_child_visible (BUTTON_DATA (list->prev->data)->button)) {
1080 path_bar->priv->first_scrolled_button = list;
1081 return;
1082 }
1083 }
1084 }
1085
1086 static gboolean
1087 nautilus_path_bar_scroll_timeout (NautilusPathBar *path_bar)
1088 {
1089 gboolean retval = FALSE;
1090
1091 if (path_bar->priv->timer) {
1092 if (gtk_widget_has_focus (path_bar->priv->up_slider_button)) {
1093 nautilus_path_bar_scroll_up (path_bar);
1094 } else {
1095 if (gtk_widget_has_focus (path_bar->priv->down_slider_button)) {
1096 nautilus_path_bar_scroll_down (path_bar);
1097 }
1098 }
1099 if (path_bar->priv->need_timer) {
1100 path_bar->priv->need_timer = FALSE;
1101
1102 path_bar->priv->timer =
1103 g_timeout_add (SCROLL_TIMEOUT,
1104 (GSourceFunc) nautilus_path_bar_scroll_timeout,
1105 path_bar);
1106
1107 } else {
1108 retval = TRUE;
1109 }
1110 }
1111
1112 return retval;
1113 }
1114
1115 static void
1116 nautilus_path_bar_stop_scrolling (NautilusPathBar *path_bar)
1117 {
1118 if (path_bar->priv->timer) {
1119 g_source_remove (path_bar->priv->timer);
1120 path_bar->priv->timer = 0;
1121 path_bar->priv->need_timer = FALSE;
1122 }
1123 }
1124
1125 static gboolean
1126 nautilus_path_bar_slider_button_press (GtkWidget *widget,
1127 GdkEventButton *event,
1128 NautilusPathBar *path_bar)
1129 {
1130 if (!gtk_widget_has_focus (widget)) {
1131 gtk_widget_grab_focus (widget);
1132 }
1133
1134 if (event->type != GDK_BUTTON_PRESS || event->button != 1) {
1135 return FALSE;
1136 }
1137
1138 path_bar->priv->ignore_click = FALSE;
1139
1140 if (widget == path_bar->priv->up_slider_button) {
1141 nautilus_path_bar_scroll_up (path_bar);
1142 } else {
1143 if (widget == path_bar->priv->down_slider_button) {
1144 nautilus_path_bar_scroll_down (path_bar);
1145 }
1146 }
1147
1148 if (!path_bar->priv->timer) {
1149 path_bar->priv->need_timer = TRUE;
1150 path_bar->priv->timer =
1151 g_timeout_add (INITIAL_SCROLL_TIMEOUT,
1152 (GSourceFunc) nautilus_path_bar_scroll_timeout,
1153 path_bar);
1154 }
1155
1156 return FALSE;
1157 }
1158
1159 static gboolean
1160 nautilus_path_bar_slider_button_release (GtkWidget *widget,
1161 GdkEventButton *event,
1162 NautilusPathBar *path_bar)
1163 {
1164 if (event->type != GDK_BUTTON_RELEASE) {
1165 return FALSE;
1166 }
1167
1168 path_bar->priv->ignore_click = TRUE;
1169 nautilus_path_bar_stop_scrolling (path_bar);
1170
1171 return FALSE;
1172 }
1173
1174
1175 /* Changes the icons wherever it is needed */
1176 static void
1177 reload_icons (NautilusPathBar *path_bar)
1178 {
1179 GList *list;
1180
1181 for (list = path_bar->priv->button_list; list; list = list->next) {
1182 ButtonData *button_data;
1183
1184 button_data = BUTTON_DATA (list->data);
1185 if (button_data->type != NORMAL_BUTTON || button_data->is_root) {
1186 nautilus_path_bar_update_button_appearance (button_data);
1187 }
1188
1189 }
1190 }
1191
1192 /* Callback used when a GtkSettings value changes */
1193 static void
1194 settings_notify_cb (GObject *object,
1195 GParamSpec *pspec,
1196 NautilusPathBar *path_bar)
1197 {
1198 const char *name;
1199
1200 name = g_param_spec_get_name (pspec);
1201
1202 if (! strcmp (name, "gtk-icon-theme-name") || ! strcmp (name, "gtk-icon-sizes")) {
1203 reload_icons (path_bar);
1204 }
1205 }
1206
1207 static void
1208 nautilus_path_bar_check_icon_theme (NautilusPathBar *path_bar)
1209 {
1210 GtkSettings *settings;
1211
1212 if (path_bar->priv->settings_signal_id) {
1213 return;
1214 }
1215
1216 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (path_bar)));
1217 path_bar->priv->settings_signal_id = g_signal_connect (settings, "notify", G_CALLBACK (settings_notify_cb), path_bar);
1218
1219 reload_icons (path_bar);
1220 }
1221
1222 /* Public functions and their helpers */
1223 static void
1224 nautilus_path_bar_clear_buttons (NautilusPathBar *path_bar)
1225 {
1226 while (path_bar->priv->button_list != NULL) {
1227 gtk_container_remove (GTK_CONTAINER (path_bar), BUTTON_DATA (path_bar->priv->button_list->data)->button);
1228 }
1229 path_bar->priv->first_scrolled_button = NULL;
1230 }
1231
1232 static void
1233 button_clicked_cb (GtkWidget *button,
1234 gpointer data)
1235 {
1236 ButtonData *button_data;
1237 NautilusPathBar *path_bar;
1238 GList *button_list;
1239
1240 button_data = BUTTON_DATA (data);
1241 if (button_data->ignore_changes) {
1242 return;
1243 }
1244
1245 path_bar = NAUTILUS_PATH_BAR (gtk_widget_get_parent (button));
1246
1247 button_list = g_list_find (path_bar->priv->button_list, button_data);
1248 g_assert (button_list != NULL);
1249
1250 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
1251
1252 g_signal_emit (path_bar, path_bar_signals [PATH_CLICKED], 0, button_data->path);
1253 }
1254
1255 static gboolean
1256 button_event_cb (GtkWidget *button,
1257 GdkEventButton *event,
1258 gpointer data)
1259 {
1260 ButtonData *button_data;
1261 NautilusPathBar *path_bar;
1262 GList *button_list;
1263
1264 button_data = BUTTON_DATA (data);
1265 path_bar = NAUTILUS_PATH_BAR (gtk_widget_get_parent (button));
1266
1267 if (event->type == GDK_BUTTON_PRESS) {
1268 g_object_set_data (G_OBJECT (button), "handle-button-release",
1269 GINT_TO_POINTER (TRUE));
1270 }
1271
1272 if (event->type == GDK_BUTTON_RELEASE &&
1273 !GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (button),
1274 "handle-button-release"))) {
1275 return FALSE;
1276 }
1277
1278 button_list = g_list_find (path_bar->priv->button_list, button_data);
1279 g_assert (button_list != NULL);
1280
1281 g_signal_emit (path_bar, path_bar_signals [PATH_EVENT], 0, button_data->path, event);
1282
1283 return FALSE;
1284 }
1285
1286 static void
1287 button_drag_begin_cb (GtkWidget *widget,
1288 GdkDragContext *drag_context,
1289 gpointer user_data)
1290 {
1291 g_object_set_data (G_OBJECT (widget), "handle-button-release",
1292 GINT_TO_POINTER (FALSE));
1293 }
1294
1295 static GIcon *
1296 get_gicon_for_mount (ButtonData *button_data)
1297 {
1298 GIcon *icon;
1299 GMount *mount;
1300
1301 icon = NULL;
1302 mount = nautilus_get_mounted_mount_for_root (button_data->path);
1303
1304 if (mount != NULL) {
1305 icon = g_mount_get_symbolic_icon (mount);
1306 g_object_unref (mount);
1307 }
1308
1309 return icon;
1310 }
1311
1312 static GIcon *
1313 get_gicon (ButtonData *button_data)
1314 {
1315 switch (button_data->type)
1316 {
1317 case ROOT_BUTTON:
1318 return g_themed_icon_new (NAUTILUS_ICON_FILESYSTEM);
1319 case HOME_BUTTON:
1320 return g_themed_icon_new (NAUTILUS_ICON_HOME);
1321 case MOUNT_BUTTON:
1322 return get_gicon_for_mount (button_data);
1323 default:
1324 return NULL;
1325 }
1326
1327 return NULL;
1328 }
1329
1330 static void
1331 button_data_free (ButtonData *button_data)
1332 {
1333 g_object_unref (button_data->path);
1334 g_free (button_data->dir_name);
1335 if (button_data->file != NULL) {
1336 g_signal_handler_disconnect (button_data->file,
1337 button_data->file_changed_signal_id);
1338 nautilus_file_monitor_remove (button_data->file, button_data);
1339 nautilus_file_unref (button_data->file);
1340 }
1341
1342 g_free (button_data);
1343 }
1344
1345 static void
1346 nautilus_path_bar_update_button_appearance (ButtonData *button_data)
1347 {
1348 const gchar *dir_name = get_dir_name (button_data);
1349 GIcon *icon;
1350
1351 if (button_data->label != NULL) {
1352 char *markup;
1353
1354 markup = g_markup_printf_escaped ("<b>%s</b>", dir_name);
1355
1356 if (gtk_label_get_use_markup (GTK_LABEL (button_data->label))) {
1357 gtk_label_set_markup (GTK_LABEL (button_data->label), markup);
1358 } else {
1359 gtk_label_set_text (GTK_LABEL (button_data->label), dir_name);
1360 }
1361
1362 gtk_label_set_markup (GTK_LABEL (button_data->bold_label), markup);
1363 g_free (markup);
1364 }
1365
1366 icon = get_gicon (button_data);
1367 if (icon != NULL) {
1368 gtk_image_set_from_gicon (GTK_IMAGE (button_data->image), icon, GTK_ICON_SIZE_MENU);
1369 gtk_widget_show (GTK_WIDGET (button_data->image));
1370 g_object_unref (icon);
1371 } else {
1372 gtk_widget_hide (GTK_WIDGET (button_data->image));
1373 }
1374 }
1375
1376 static void
1377 nautilus_path_bar_update_button_state (ButtonData *button_data,
1378 gboolean current_dir)
1379 {
1380 if (button_data->label != NULL) {
1381 gtk_label_set_label (GTK_LABEL (button_data->label), NULL);
1382 gtk_label_set_label (GTK_LABEL (button_data->bold_label), NULL);
1383 gtk_label_set_use_markup (GTK_LABEL (button_data->label), current_dir);
1384 }
1385
1386 nautilus_path_bar_update_button_appearance (button_data);
1387
1388 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button)) != current_dir) {
1389 button_data->ignore_changes = TRUE;
1390 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button_data->button), current_dir);
1391 button_data->ignore_changes = FALSE;
1392 }
1393 }
1394
1395 static void
1396 setup_button_type (ButtonData *button_data,
1397 NautilusPathBar *path_bar,
1398 GFile *location)
1399 {
1400 GMount *mount;
1401
1402 if (nautilus_is_root_directory (location)) {
1403 button_data->type = ROOT_BUTTON;
1404 } else if (nautilus_is_home_directory (location)) {
1405 button_data->type = HOME_BUTTON;
1406 button_data->is_root = TRUE;
1407 } else if ((mount = nautilus_get_mounted_mount_for_root (location)) != NULL) {
1408 button_data->dir_name = g_mount_get_name (mount);
1409 button_data->type = MOUNT_BUTTON;
1410 button_data->is_root = TRUE;
1411
1412 g_object_unref (mount);
1413 } else {
1414 button_data->type = NORMAL_BUTTON;
1415 }
1416 }
1417
1418 static void
1419 button_drag_data_get_cb (GtkWidget *widget,
1420 GdkDragContext *context,
1421 GtkSelectionData *selection_data,
1422 guint info,
1423 guint time_,
1424 gpointer user_data)
1425 {
1426 ButtonData *button_data;
1427 char *uri_list[2];
1428 char *tmp;
1429
1430 button_data = user_data;
1431
1432 uri_list[0] = g_file_get_uri (button_data->path);
1433 uri_list[1] = NULL;
1434
1435 if (info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) {
1436 tmp = g_strdup_printf ("%s\r\n", uri_list[0]);
1437 gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
1438 8, (const guchar *) tmp, strlen (tmp));
1439 g_free (tmp);
1440 } else if (info == NAUTILUS_ICON_DND_URI_LIST) {
1441 gtk_selection_data_set_uris (selection_data, uri_list);
1442 }
1443
1444 g_free (uri_list[0]);
1445 }
1446
1447 static void
1448 setup_button_drag_source (ButtonData *button_data)
1449 {
1450 GtkTargetList *target_list;
1451 const GtkTargetEntry targets[] = {
1452 { NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST }
1453 };
1454
1455 gtk_drag_source_set (button_data->button,
1456 GDK_BUTTON1_MASK |
1457 GDK_BUTTON2_MASK,
1458 NULL, 0,
1459 GDK_ACTION_MOVE |
1460 GDK_ACTION_COPY |
1461 GDK_ACTION_LINK |
1462 GDK_ACTION_ASK);
1463
1464 target_list = gtk_target_list_new (targets, G_N_ELEMENTS (targets));
1465 gtk_target_list_add_uri_targets (target_list, NAUTILUS_ICON_DND_URI_LIST);
1466 gtk_drag_source_set_target_list (button_data->button, target_list);
1467 gtk_target_list_unref (target_list);
1468
1469 g_signal_connect (button_data->button, "drag-data-get",
1470 G_CALLBACK (button_drag_data_get_cb),
1471 button_data);
1472 }
1473
1474 static void
1475 button_data_file_changed (NautilusFile *file,
1476 ButtonData *button_data)
1477 {
1478 GFile *location, *current_location, *parent, *button_parent;
1479 ButtonData *current_button_data;
1480 char *display_name;
1481 NautilusPathBar *path_bar;
1482 gboolean renamed, child;
1483
1484 path_bar = (NautilusPathBar *) gtk_widget_get_ancestor (button_data->button,
1485 NAUTILUS_TYPE_PATH_BAR);
1486 if (path_bar == NULL) {
1487 return;
1488 }
1489
1490 g_assert (path_bar->priv->current_path != NULL);
1491 g_assert (path_bar->priv->current_button_data != NULL);
1492
1493 current_button_data = path_bar->priv->current_button_data;
1494
1495 location = nautilus_file_get_location (file);
1496 if (!g_file_equal (button_data->path, location)) {
1497 parent = g_file_get_parent (location);
1498 button_parent = g_file_get_parent (button_data->path);
1499
1500 renamed = (parent != NULL && button_parent != NULL) &&
1501 g_file_equal (parent, button_parent);
1502
1503 if (parent != NULL) {
1504 g_object_unref (parent);
1505 }
1506 if (button_parent != NULL) {
1507 g_object_unref (button_parent);
1508 }
1509
1510 if (renamed) {
1511 button_data->path = g_object_ref (location);
1512 } else {
1513 /* the file has been moved.
1514 * If it was below the currently displayed location, remove it.
1515 * If it was not below the currently displayed location, update the path bar
1516 */
1517 child = g_file_has_prefix (button_data->path,
1518 path_bar->priv->current_path);
1519
1520 if (child) {
1521 /* moved file inside current path hierarchy */
1522 g_object_unref (location);
1523 location = g_file_get_parent (button_data->path);
1524 current_location = g_object_ref (path_bar->priv->current_path);
1525 } else {
1526 /* moved current path, or file outside current path hierarchy.
1527 * Update path bar to new locations.
1528 */
1529 current_location = nautilus_file_get_location (current_button_data->file);
1530 }
1531
1532 nautilus_path_bar_update_path (path_bar, location);
1533 nautilus_path_bar_set_path (path_bar, current_location);
1534 g_object_unref (location);
1535 g_object_unref (current_location);
1536 return;
1537 }
1538 } else if (nautilus_file_is_gone (file)) {
1539 gint idx, position;
1540
1541 /* if the current or a parent location are gone, clear all the buttons,
1542 * the view will set the new path.
1543 */
1544 current_location = nautilus_file_get_location (current_button_data->file);
1545
1546 if (g_file_has_prefix (current_location, location) ||
1547 g_file_equal (current_location, location)) {
1548 nautilus_path_bar_clear_buttons (path_bar);
1549 } else if (g_file_has_prefix (location, current_location)) {
1550 /* remove this and the following buttons */
1551 position = g_list_position (path_bar->priv->button_list,
1552 g_list_find (path_bar->priv->button_list, button_data));
1553
1554 if (position != -1) {
1555 for (idx = 0; idx <= position; idx++) {
1556 gtk_container_remove (GTK_CONTAINER (path_bar),
1557 BUTTON_DATA (path_bar->priv->button_list->data)->button);
1558 }
1559 }
1560 }
1561
1562 g_object_unref (current_location);
1563 g_object_unref (location);
1564 return;
1565 }
1566 g_object_unref (location);
1567
1568 /* MOUNTs use the GMount as the name, so don't update for those */
1569 if (button_data->type != MOUNT_BUTTON) {
1570 display_name = nautilus_file_get_display_name (file);
1571 if (g_strcmp0 (display_name, button_data->dir_name) != 0) {
1572 g_free (button_data->dir_name);
1573 button_data->dir_name = g_strdup (display_name);
1574 }
1575
1576 g_free (display_name);
1577 }
1578 nautilus_path_bar_update_button_appearance (button_data);
1579 }
1580
1581 static ButtonData *
1582 make_button_data (NautilusPathBar *path_bar,
1583 NautilusFile *file,
1584 gboolean current_dir)
1585 {
1586 GFile *path;
1587 GtkWidget *child;
1588 ButtonData *button_data;
1589
1590 path = nautilus_file_get_location (file);
1591 child = NULL;
1592
1593 /* Is it a special button? */
1594 button_data = g_new0 (ButtonData, 1);
1595
1596 setup_button_type (button_data, path_bar, path);
1597 button_data->button = gtk_toggle_button_new ();
1598 gtk_button_set_focus_on_click (GTK_BUTTON (button_data->button), FALSE);
1599 gtk_widget_add_events (button_data->button, GDK_SCROLL_MASK);
1600 /* TODO update button type when xdg directories change */
1601
1602 button_data->image = gtk_image_new ();
1603
1604 switch (button_data->type) {
1605 case ROOT_BUTTON:
1606 child = button_data->image;
1607 button_data->label = NULL;
1608 break;
1609 case HOME_BUTTON:
1610 case MOUNT_BUTTON:
1611 case NORMAL_BUTTON:
1612 default:
1613 button_data->label = gtk_label_new (NULL);
1614 child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
1615 gtk_box_pack_start (GTK_BOX (child), button_data->image, FALSE, FALSE, 0);
1616 gtk_box_pack_start (GTK_BOX (child), button_data->label, FALSE, FALSE, 0);
1617 break;
1618 }
1619
1620 if (button_data->label != NULL) {
1621 gtk_label_set_ellipsize (GTK_LABEL (button_data->label), PANGO_ELLIPSIZE_MIDDLE);
1622 gtk_label_set_single_line_mode (GTK_LABEL (button_data->label), TRUE);
1623
1624 button_data->bold_label = gtk_label_new (NULL);
1625 gtk_widget_set_no_show_all (button_data->bold_label, TRUE);
1626 gtk_label_set_single_line_mode (GTK_LABEL (button_data->bold_label), TRUE);
1627 gtk_box_pack_start (GTK_BOX (child), button_data->bold_label, FALSE, FALSE, 0);
1628 }
1629
1630 if (button_data->path == NULL) {
1631 button_data->path = g_object_ref (path);
1632 }
1633 if (button_data->dir_name == NULL) {
1634 button_data->dir_name = nautilus_file_get_display_name (file);
1635 }
1636 if (button_data->file == NULL) {
1637 button_data->file = nautilus_file_ref (file);
1638 nautilus_file_monitor_add (button_data->file, button_data,
1639 NAUTILUS_FILE_ATTRIBUTES_FOR_ICON);
1640 button_data->file_changed_signal_id =
1641 g_signal_connect (button_data->file, "changed",
1642 G_CALLBACK (button_data_file_changed),
1643 button_data);
1644 }
1645
1646 gtk_container_add (GTK_CONTAINER (button_data->button), child);
1647 gtk_widget_show_all (button_data->button);
1648
1649 nautilus_path_bar_update_button_state (button_data, current_dir);
1650
1651 g_signal_connect (button_data->button, "clicked", G_CALLBACK (button_clicked_cb), button_data);
1652 g_signal_connect (button_data->button, "button-press-event", G_CALLBACK (button_event_cb), button_data);
1653 g_signal_connect (button_data->button, "button-release-event", G_CALLBACK (button_event_cb), button_data);
1654 g_signal_connect (button_data->button, "drag-begin", G_CALLBACK (button_drag_begin_cb), button_data);
1655 g_object_weak_ref (G_OBJECT (button_data->button), (GWeakNotify) button_data_free, button_data);
1656
1657 setup_button_drag_source (button_data);
1658
1659 nautilus_drag_slot_proxy_init (button_data->button, button_data->file, NULL);
1660
1661 g_object_unref (path);
1662
1663 return button_data;
1664 }
1665
1666 static gboolean
1667 nautilus_path_bar_check_parent_path (NautilusPathBar *path_bar,
1668 GFile *location,
1669 ButtonData **current_button_data)
1670 {
1671 GList *list;
1672 ButtonData *button_data, *current_data;
1673 gboolean is_active;
1674
1675 current_data = NULL;
1676
1677 for (list = path_bar->priv->button_list; list; list = list->next) {
1678 button_data = list->data;
1679 if (g_file_equal (location, button_data->path)) {
1680 current_data = button_data;
1681 is_active = TRUE;
1682
1683 if (!gtk_widget_get_child_visible (current_data->button)) {
1684 path_bar->priv->first_scrolled_button = list;
1685 gtk_widget_queue_resize (GTK_WIDGET (path_bar));
1686 }
1687 } else {
1688 is_active = FALSE;
1689 }
1690
1691 nautilus_path_bar_update_button_state (button_data, is_active);
1692 }
1693
1694 if (current_button_data != NULL) {
1695 *current_button_data = current_data;
1696 }
1697
1698 return (current_data != NULL);
1699 }
1700
1701 static void
1702 nautilus_path_bar_update_path (NautilusPathBar *path_bar,
1703 GFile *file_path)
1704 {
1705 NautilusFile *file;
1706 gboolean first_directory;
1707 GList *new_buttons, *l;
1708 ButtonData *button_data;
1709
1710 g_return_if_fail (NAUTILUS_IS_PATH_BAR (path_bar));
1711 g_return_if_fail (file_path != NULL);
1712
1713 first_directory = TRUE;
1714 new_buttons = NULL;
1715
1716 file = nautilus_file_get (file_path);
1717
1718 gtk_widget_push_composite_child ();
1719
1720 while (file != NULL) {
1721 NautilusFile *parent_file;
1722
1723 parent_file = nautilus_file_get_parent (file);
1724 button_data = make_button_data (path_bar, file, first_directory);
1725 nautilus_file_unref (file);
1726
1727 if (first_directory) {
1728 first_directory = FALSE;
1729 }
1730
1731 new_buttons = g_list_prepend (new_buttons, button_data);
1732
1733 if (parent_file != NULL &&
1734 button_data->is_root) {
1735 nautilus_file_unref (parent_file);
1736 break;
1737 }
1738
1739 file = parent_file;
1740 }
1741
1742 nautilus_path_bar_clear_buttons (path_bar);
1743 path_bar->priv->button_list = g_list_reverse (new_buttons);
1744
1745 for (l = path_bar->priv->button_list; l; l = l->next) {
1746 GtkWidget *button;
1747 button = BUTTON_DATA (l->data)->button;
1748 gtk_container_add (GTK_CONTAINER (path_bar), button);
1749 }
1750
1751 gtk_widget_pop_composite_child ();
1752
1753 child_ordering_changed (path_bar);
1754 }
1755
1756 void
1757 nautilus_path_bar_set_path (NautilusPathBar *path_bar,
1758 GFile *file_path)
1759 {
1760 ButtonData *button_data;
1761
1762 g_return_if_fail (NAUTILUS_IS_PATH_BAR (path_bar));
1763 g_return_if_fail (file_path != NULL);
1764
1765 /* Check whether the new path is already present in the pathbar as buttons.
1766 * This could be a parent directory or a previous selected subdirectory. */
1767 if (!nautilus_path_bar_check_parent_path (path_bar, file_path, &button_data)) {
1768 nautilus_path_bar_update_path (path_bar, file_path);
1769 button_data = g_list_nth_data (path_bar->priv->button_list, 0);
1770 }
1771
1772 if (path_bar->priv->current_path != NULL) {
1773 g_object_unref (path_bar->priv->current_path);
1774 }
1775
1776 path_bar->priv->current_path = g_object_ref (file_path);
1777 path_bar->priv->current_button_data = button_data;
1778 }