nautilus-3.6.3/src/nautilus-pathbar.c

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);
Access to field 'data' results in a dereference of a null pointer (loaded from variable 'up_button')
(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 }