gtkpathbar.c 56 KB
Newer Older
1
/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2
/* gtkpathbar.c
3 4 5 6 7 8 9 10 11 12 13 14 15
 * Copyright (C) 2004  Red Hat, Inc.,  Jonathan Blandford <jrb@gnome.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Javier Jardon's avatar
Javier Jardon committed
16
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 18
 */

19
#include "config.h"
20

21
#include "gtkpathbar.h"
22 23 24

#include <string.h>

25
#include "gtkarrow.h"
26
#include "gtkbox.h"
27
#include "gtkdnd.h"
28 29
#include "gtkiconfactory.h"
#include "gtkicontheme.h"
30 31
#include "gtkimage.h"
#include "gtkintl.h"
32
#include "gtklabel.h"
33 34
#include "gtkmain.h"
#include "gtkmarshalers.h"
35 36
#include "gtksettings.h"
#include "gtktogglebutton.h"
37
#include "gtkwidgetpath.h"
38
#include "gtkwidgetprivate.h"
39

40 41 42 43 44 45

enum {
  PATH_CLICKED,
  LAST_SIGNAL
};

46 47 48 49
typedef enum {
  NORMAL_BUTTON,
  ROOT_BUTTON,
  HOME_BUTTON,
50
  DESKTOP_BUTTON
51 52
} ButtonType;

53 54
#define BUTTON_DATA(x) ((ButtonData *)(x))

55
#define SCROLL_DELAY_FACTOR 5
56

57 58
static guint path_bar_signals [LAST_SIGNAL] = { 0 };

59
/* Icon size for if we can't get it from the theme */
60
#define FALLBACK_ICON_SIZE 16
61

62 63 64 65 66 67 68
typedef struct _ButtonData ButtonData;

struct _ButtonData
{
  GtkWidget *button;
  ButtonType type;
  char *dir_name;
69
  GFile *file;
70 71
  GtkWidget *image;
  GtkWidget *label;
72
  GCancellable *cancellable;
73 74
  guint ignore_changes : 1;
  guint file_is_hidden : 1;
75
};
76 77 78 79 80
/* This macro is used to check if a button can be used as a fake root.
 * All buttons in front of a fake root are automatically hidden when in a
 * directory below a fake root and replaced with the "<" arrow button.
 */
#define BUTTON_IS_FAKE_ROOT(button) ((button)->type == HOME_BUTTON)
81

Matthias Clasen's avatar
Matthias Clasen committed
82
G_DEFINE_TYPE (GtkPathBar, gtk_path_bar, GTK_TYPE_CONTAINER)
83

84 85
static void gtk_path_bar_finalize                 (GObject          *object);
static void gtk_path_bar_dispose                  (GObject          *object);
86 87
static void gtk_path_bar_realize                  (GtkWidget        *widget);
static void gtk_path_bar_unrealize                (GtkWidget        *widget);
88 89 90 91 92 93
static void gtk_path_bar_get_preferred_width      (GtkWidget        *widget,
                                                   gint             *minimum,
                                                   gint             *natural);
static void gtk_path_bar_get_preferred_height     (GtkWidget        *widget,
                                                   gint             *minimum,
                                                   gint             *natural);
94 95
static void gtk_path_bar_map                      (GtkWidget        *widget);
static void gtk_path_bar_unmap                    (GtkWidget        *widget);
96 97 98 99 100 101 102 103 104 105
static void gtk_path_bar_size_allocate            (GtkWidget        *widget,
						   GtkAllocation    *allocation);
static void gtk_path_bar_add                      (GtkContainer     *container,
						   GtkWidget        *widget);
static void gtk_path_bar_remove                   (GtkContainer     *container,
						   GtkWidget        *widget);
static void gtk_path_bar_forall                   (GtkContainer     *container,
						   gboolean          include_internals,
						   GtkCallback       callback,
						   gpointer          callback_data);
106 107
static GtkWidgetPath *gtk_path_bar_get_path_for_child (GtkContainer *container,
                                                       GtkWidget    *child);
108 109 110 111
static gboolean gtk_path_bar_scroll               (GtkWidget        *widget,
						   GdkEventScroll   *event);
static void gtk_path_bar_scroll_up                (GtkPathBar       *path_bar);
static void gtk_path_bar_scroll_down              (GtkPathBar       *path_bar);
112
static void gtk_path_bar_stop_scrolling           (GtkPathBar       *path_bar);
113 114 115 116 117 118
static gboolean gtk_path_bar_slider_up_defocus    (GtkWidget        *widget,
						   GdkEventButton   *event,
						   GtkPathBar       *path_bar);
static gboolean gtk_path_bar_slider_down_defocus  (GtkWidget        *widget,
						   GdkEventButton   *event,
						   GtkPathBar       *path_bar);
119 120 121 122 123 124 125 126 127 128
static gboolean gtk_path_bar_slider_button_press  (GtkWidget        *widget,
						   GdkEventButton   *event,
						   GtkPathBar       *path_bar);
static gboolean gtk_path_bar_slider_button_release(GtkWidget        *widget,
						   GdkEventButton   *event,
						   GtkPathBar       *path_bar);
static void gtk_path_bar_grab_notify              (GtkWidget        *widget,
						   gboolean          was_grabbed);
static void gtk_path_bar_state_changed            (GtkWidget        *widget,
						   GtkStateType      previous_state);
129
static void gtk_path_bar_style_updated            (GtkWidget        *widget);
130 131 132 133 134 135
static void gtk_path_bar_screen_changed           (GtkWidget        *widget,
						   GdkScreen        *previous_screen);
static void gtk_path_bar_check_icon_theme         (GtkPathBar       *path_bar);
static void gtk_path_bar_update_button_appearance (GtkPathBar       *path_bar,
						   ButtonData       *button_data,
						   gboolean          current_dir);
136

137 138 139 140 141
static void
on_slider_unmap (GtkWidget  *widget,
		 GtkPathBar *path_bar)
{
  if (path_bar->timer &&
142 143 144
      ((widget == path_bar->up_slider_button && path_bar->scrolling_up) ||
       (widget == path_bar->down_slider_button && path_bar->scrolling_down)))
    gtk_path_bar_stop_scrolling (path_bar);
145 146
}

147
static GtkWidget *
148 149
get_slider_button (GtkPathBar  *path_bar,
		   GtkArrowType arrow_type)
150 151
{
  GtkWidget *button;
152
  AtkObject *atk_obj;
153 154 155

  gtk_widget_push_composite_child ();

156
  button = gtk_button_new ();
157 158 159 160 161 162
  atk_obj = gtk_widget_get_accessible (button);
  if (arrow_type == GTK_ARROW_LEFT)
    atk_object_set_name (atk_obj, _("Up Path"));
  else
    atk_object_set_name (atk_obj, _("Down Path"));

163
  gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
Matthias Clasen's avatar
Matthias Clasen committed
164
  gtk_widget_add_events (button, GDK_SCROLL_MASK);
165 166
  gtk_container_add (GTK_CONTAINER (button),
                     gtk_arrow_new (arrow_type, GTK_SHADOW_OUT));
167 168 169
  gtk_container_add (GTK_CONTAINER (path_bar), button);
  gtk_widget_show_all (button);

170 171 172
  g_signal_connect (G_OBJECT (button), "unmap",
		    G_CALLBACK (on_slider_unmap), path_bar);

173 174
  gtk_widget_pop_composite_child ();

175 176 177 178 179 180
  return button;
}

static void
gtk_path_bar_init (GtkPathBar *path_bar)
{
181 182
  GtkStyleContext *context;

183
  gtk_widget_set_has_window (GTK_WIDGET (path_bar), FALSE);
184 185
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (path_bar), FALSE);

186
  path_bar->get_info_cancellable = NULL;
187

188
  path_bar->spacing = 0;
189 190
  path_bar->up_slider_button = get_slider_button (path_bar, GTK_ARROW_LEFT);
  path_bar->down_slider_button = get_slider_button (path_bar, GTK_ARROW_RIGHT);
191
  path_bar->icon_size = FALLBACK_ICON_SIZE;
192
  
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
  g_signal_connect_swapped (path_bar->up_slider_button, "clicked",
                            G_CALLBACK (gtk_path_bar_scroll_up), path_bar);
  g_signal_connect_swapped (path_bar->down_slider_button, "clicked",
                            G_CALLBACK (gtk_path_bar_scroll_down), path_bar);

  g_signal_connect (path_bar->up_slider_button, "focus-out-event",
                    G_CALLBACK (gtk_path_bar_slider_up_defocus), path_bar);
  g_signal_connect (path_bar->down_slider_button, "focus-out-event",
                    G_CALLBACK (gtk_path_bar_slider_down_defocus), path_bar);

  g_signal_connect (path_bar->up_slider_button, "button-press-event",
                    G_CALLBACK (gtk_path_bar_slider_button_press), path_bar);
  g_signal_connect (path_bar->up_slider_button, "button-release-event",
                    G_CALLBACK (gtk_path_bar_slider_button_release), path_bar);
  g_signal_connect (path_bar->down_slider_button, "button-press-event",
                    G_CALLBACK (gtk_path_bar_slider_button_press), path_bar);
  g_signal_connect (path_bar->down_slider_button, "button-release-event",
                    G_CALLBACK (gtk_path_bar_slider_button_release), path_bar);
211 212 213

  context = gtk_widget_get_style_context (GTK_WIDGET (path_bar));
  gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
214 215 216 217 218 219 220 221 222 223 224 225 226
}

static void
gtk_path_bar_class_init (GtkPathBarClass *path_bar_class)
{
  GObjectClass *gobject_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

  gobject_class = (GObjectClass *) path_bar_class;
  widget_class = (GtkWidgetClass *) path_bar_class;
  container_class = (GtkContainerClass *) path_bar_class;

227
  gobject_class->finalize = gtk_path_bar_finalize;
228
  gobject_class->dispose = gtk_path_bar_dispose;
229

230 231
  widget_class->get_preferred_width = gtk_path_bar_get_preferred_width;
  widget_class->get_preferred_height = gtk_path_bar_get_preferred_height;
232 233 234
  widget_class->realize = gtk_path_bar_realize;
  widget_class->unrealize = gtk_path_bar_unrealize;
  widget_class->map = gtk_path_bar_map;
235
  widget_class->unmap = gtk_path_bar_unmap;
236
  widget_class->size_allocate = gtk_path_bar_size_allocate;
237
  widget_class->style_updated = gtk_path_bar_style_updated;
238
  widget_class->screen_changed = gtk_path_bar_screen_changed;
239 240
  widget_class->grab_notify = gtk_path_bar_grab_notify;
  widget_class->state_changed = gtk_path_bar_state_changed;
241
  widget_class->scroll_event = gtk_path_bar_scroll;
242 243 244 245

  container_class->add = gtk_path_bar_add;
  container_class->forall = gtk_path_bar_forall;
  container_class->remove = gtk_path_bar_remove;
246
  container_class->get_path_for_child = gtk_path_bar_get_path_for_child;
247
  gtk_container_class_handle_border_width (container_class);
248 249
  /* FIXME: */
  /*  container_class->child_type = gtk_path_bar_child_type;*/
250 251

  path_bar_signals [PATH_CLICKED] =
252
    g_signal_new (I_("path-clicked"),
253
		  G_OBJECT_CLASS_TYPE (gobject_class),
254 255 256
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkPathBarClass, path_clicked),
		  NULL, NULL,
257 258 259
		  _gtk_marshal_VOID__POINTER_POINTER_BOOLEAN,
		  G_TYPE_NONE, 3,
		  G_TYPE_POINTER,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
260 261
		  G_TYPE_POINTER,
		  G_TYPE_BOOLEAN);
262 263 264 265
}


static void
266
gtk_path_bar_finalize (GObject *object)
267 268 269 270
{
  GtkPathBar *path_bar;

  path_bar = GTK_PATH_BAR (object);
271 272 273

  gtk_path_bar_stop_scrolling (path_bar);

274
  g_list_free (path_bar->button_list);
275 276 277 278 279 280
  if (path_bar->root_file)
    g_object_unref (path_bar->root_file);
  if (path_bar->home_file)
    g_object_unref (path_bar->home_file);
  if (path_bar->desktop_file)
    g_object_unref (path_bar->desktop_file);
281

282
  if (path_bar->root_icon)
283
    g_object_unref (path_bar->root_icon);
284 285 286 287 288
  if (path_bar->home_icon)
    g_object_unref (path_bar->home_icon);
  if (path_bar->desktop_icon)
    g_object_unref (path_bar->desktop_icon);

289 290
  if (path_bar->file_system)
    g_object_unref (path_bar->file_system);
291

292
  G_OBJECT_CLASS (gtk_path_bar_parent_class)->finalize (object);
293 294
}

295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
/* Removes the settings signal handler.  It's safe to call multiple times */
static void
remove_settings_signal (GtkPathBar *path_bar,
			GdkScreen  *screen)
{
  if (path_bar->settings_signal_id)
    {
      GtkSettings *settings;

      settings = gtk_settings_get_for_screen (screen);
      g_signal_handler_disconnect (settings,
				   path_bar->settings_signal_id);
      path_bar->settings_signal_id = 0;
    }
}

static void
gtk_path_bar_dispose (GObject *object)
{
314 315 316 317
  GtkPathBar *path_bar = GTK_PATH_BAR (object);

  remove_settings_signal (path_bar, gtk_widget_get_screen (GTK_WIDGET (object)));

318 319 320
  if (path_bar->get_info_cancellable)
    g_cancellable_cancel (path_bar->get_info_cancellable);
  path_bar->get_info_cancellable = NULL;
321 322 323 324

  G_OBJECT_CLASS (gtk_path_bar_parent_class)->dispose (object);
}

325 326 327 328 329 330
/* Size requisition:
 * 
 * Ideally, our size is determined by another widget, and we are just filling
 * available space.
 */
static void
331 332 333
gtk_path_bar_get_preferred_width (GtkWidget *widget,
                                  gint      *minimum,
                                  gint      *natural)
334
{
335
  ButtonData *button_data;
336 337
  GtkPathBar *path_bar;
  GList *list;
338 339 340
  gint child_height;
  gint height;
  gint child_min, child_nat;
341 342 343

  path_bar = GTK_PATH_BAR (widget);

344 345
  *minimum = *natural = 0;
  height = 0;
346 347 348

  for (list = path_bar->button_list; list; list = list->next)
    {
349
      button_data = BUTTON_DATA (list->data);
350 351 352
      gtk_widget_get_preferred_width (button_data->button, &child_min, &child_nat);
      gtk_widget_get_preferred_height (button_data->button, &child_height, NULL);
      height = MAX (height, child_height);
353

354
      if (button_data->type == NORMAL_BUTTON)
355 356 357 358 359 360 361 362
        {
          /* Use 2*Height as button width because of ellipsized label.  */
          child_min = MAX (child_min, child_height * 2);
          child_nat = MAX (child_min, child_height * 2);
        }

      *minimum = MAX (*minimum, child_min);
      *natural = MAX (*natural, child_nat);
363 364 365 366 367 368
    }

  /* Add space for slider, if we have more than one path */
  /* Theoretically, the slider could be bigger than the other button.  But we're
   * not going to worry about that now.
   */
369
  path_bar->slider_width = MIN (height * 2 / 3 + 5, height);
370
  if (path_bar->button_list && path_bar->button_list->next != NULL)
371 372 373 374 375
    {
      *minimum += (path_bar->spacing + path_bar->slider_width) * 2;
      *natural += (path_bar->spacing + path_bar->slider_width) * 2;
    }
}
376

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
static void
gtk_path_bar_get_preferred_height (GtkWidget *widget,
                                   gint      *minimum,
                                   gint      *natural)
{
  ButtonData *button_data;
  GtkPathBar *path_bar;
  GList *list;
  gint child_min, child_nat;

  path_bar = GTK_PATH_BAR (widget);

  *minimum = *natural = 0;

  for (list = path_bar->button_list; list; list = list->next)
    {
      button_data = BUTTON_DATA (list->data);
      gtk_widget_get_preferred_height (button_data->button, &child_min, &child_nat);

      *minimum = MAX (*minimum, child_min);
      *natural = MAX (*natural, child_nat);
    }
399 400 401 402 403 404 405 406 407
}

static void
gtk_path_bar_update_slider_buttons (GtkPathBar *path_bar)
{
  if (path_bar->button_list)
    {
      GtkWidget *button;

408
      button = BUTTON_DATA (path_bar->button_list->data)->button;
409
      if (gtk_widget_get_child_visible (button))
410 411 412 413
	{
	  gtk_path_bar_stop_scrolling (path_bar);
	  gtk_widget_set_sensitive (path_bar->down_slider_button, FALSE);
	}
414 415 416
      else
	gtk_widget_set_sensitive (path_bar->down_slider_button, TRUE);

417
      button = BUTTON_DATA (g_list_last (path_bar->button_list)->data)->button;
418
      if (gtk_widget_get_child_visible (button))
419 420 421 422
	{
	  gtk_path_bar_stop_scrolling (path_bar);
	  gtk_widget_set_sensitive (path_bar->up_slider_button, FALSE);
	}
423 424 425 426 427
      else
	gtk_widget_set_sensitive (path_bar->up_slider_button, TRUE);
    }
}

428 429 430 431 432 433 434 435
static void
gtk_path_bar_map (GtkWidget *widget)
{
  gdk_window_show (GTK_PATH_BAR (widget)->event_window);

  GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->map (widget);
}

436 437 438 439
static void
gtk_path_bar_unmap (GtkWidget *widget)
{
  gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
440
  gdk_window_hide (GTK_PATH_BAR (widget)->event_window);
441 442 443 444

  GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->unmap (widget);
}

445 446 447 448
static void
gtk_path_bar_realize (GtkWidget *widget)
{
  GtkPathBar *path_bar;
449 450
  GtkAllocation allocation;
  GdkWindow *window;
451 452 453
  GdkWindowAttr attributes;
  gint attributes_mask;

454
  gtk_widget_set_realized (widget, TRUE);
455 456

  path_bar = GTK_PATH_BAR (widget);
457 458 459 460 461
  window = gtk_widget_get_parent_window (widget);
  gtk_widget_set_window (widget, window);
  g_object_ref (window);

  gtk_widget_get_allocation (widget, &allocation);
462 463

  attributes.window_type = GDK_WINDOW_CHILD;
464 465 466 467
  attributes.x = allocation.x;
  attributes.y = allocation.y;
  attributes.width = allocation.width;
  attributes.height = allocation.height;
468 469 470 471 472 473
  attributes.wclass = GDK_INPUT_ONLY;
  attributes.event_mask = gtk_widget_get_events (widget);
  attributes.event_mask |= GDK_SCROLL_MASK;
  attributes_mask = GDK_WA_X | GDK_WA_Y;

  path_bar->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
474
                                           &attributes, attributes_mask);
475 476 477 478 479 480 481 482 483 484 485 486 487 488
  gdk_window_set_user_data (path_bar->event_window, widget);
}

static void
gtk_path_bar_unrealize (GtkWidget *widget)
{
  GtkPathBar *path_bar;

  path_bar = GTK_PATH_BAR (widget);

  gdk_window_set_user_data (path_bar->event_window, NULL);
  gdk_window_destroy (path_bar->event_window);
  path_bar->event_window = NULL;

489
  GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->unrealize (widget);
490 491
}

492 493 494 495 496 497
static void
child_ordering_changed (GtkPathBar *path_bar)
{
  GList *l;

  if (path_bar->up_slider_button)
498 499
    _gtk_widget_invalidate_style_context (path_bar->up_slider_button,
                                          GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
500
  if (path_bar->down_slider_button)
501 502
    _gtk_widget_invalidate_style_context (path_bar->down_slider_button,
                                          GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
503 504 505 506 507

  for (l = path_bar->button_list; l; l = l->next)
    {
      ButtonData *data = l->data;

508 509
      _gtk_widget_invalidate_style_context (data->button,
                                            GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
510 511 512
    }
}

513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
/* This is a tad complicated
 */
static void
gtk_path_bar_size_allocate (GtkWidget     *widget,
			    GtkAllocation *allocation)
{
  GtkWidget *child;
  GtkPathBar *path_bar = GTK_PATH_BAR (widget);
  GtkTextDirection direction;
  GtkAllocation child_allocation;
  GList *list, *first_button;
  gint width;
  gint allocation_width;
  gboolean need_sliders = FALSE;
  gint up_slider_offset = 0;
528
  GtkRequisition child_requisition;
529
  gboolean needs_reorder = FALSE;
530

531
  gtk_widget_set_allocation (widget, allocation);
532

533
  if (gtk_widget_get_realized (widget))
534 535 536 537
    gdk_window_move_resize (path_bar->event_window,
			    allocation->x, allocation->y,
			    allocation->width, allocation->height);

538 539 540 541 542
  /* No path is set; we don't have to allocate anything. */
  if (path_bar->button_list == NULL)
    return;

  direction = gtk_widget_get_direction (widget);
543
  allocation_width = allocation->width;
544 545

  /* First, we check to see if we need the scrollbars. */
546 547 548
  if (path_bar->fake_root)
    width = path_bar->spacing + path_bar->slider_width;
  else
549
    width = 0;
550 551

  for (list = path_bar->button_list; list; list = list->next)
552
    {
553
      child = BUTTON_DATA (list->data)->button;
554

555
      gtk_widget_get_preferred_size (child, &child_requisition, NULL);
556 557

      width += child_requisition.width + path_bar->spacing;
558 559
      if (list == path_bar->fake_root)
	break;
560 561 562 563
    }

  if (width <= allocation_width)
    {
564 565 566 567
      if (path_bar->fake_root)
	first_button = path_bar->fake_root;
      else
	first_button = g_list_last (path_bar->button_list);
568 569 570 571 572 573
    }
  else
    {
      gboolean reached_end = FALSE;
      gint slider_space = 2 * (path_bar->spacing + path_bar->slider_width);

574 575 576 577
      if (path_bar->first_scrolled_button)
	first_button = path_bar->first_scrolled_button;
      else
	first_button = path_bar->button_list;
578 579 580 581 582 583 584
      need_sliders = TRUE;
      
      /* To see how much space we have, and how many buttons we can display.
       * We start at the first button, count forward until hit the new
       * button, then count backwards.
       */
      /* Count down the path chain towards the end. */
585 586
      gtk_widget_get_preferred_size (BUTTON_DATA (first_button->data)->button,
                                     &child_requisition, NULL);
587 588

      width = child_requisition.width;
589 590 591
      list = first_button->prev;
      while (list && !reached_end)
	{
592
	  child = BUTTON_DATA (list->data)->button;
593

594
          gtk_widget_get_preferred_size (child, &child_requisition, NULL);
595 596

	  if (width + child_requisition.width +
597
	      path_bar->spacing + slider_space > allocation_width)
598
	    reached_end = TRUE;
599 600
	  else if (list == path_bar->fake_root)
	    break;
601
	  else
602
	    width += child_requisition.width + path_bar->spacing;
603 604 605 606 607 608

	  list = list->prev;
	}

      /* Finally, we walk up, seeing how many of the previous buttons we can
       * add */
609
      while (first_button->next && !reached_end)
610
	{
611
	  child = BUTTON_DATA (first_button->next->data)->button;
612

613
          gtk_widget_get_preferred_size (child, &child_requisition, NULL);
614 615

	  if (width + child_requisition.width + path_bar->spacing + slider_space > allocation_width)
616 617 618 619 620
	    {
	      reached_end = TRUE;
	    }
	  else
	    {
621
	      width += child_requisition.width + path_bar->spacing;
622 623
	      if (first_button == path_bar->fake_root)
		break;
624 625 626 627 628 629
	      first_button = first_button->next;
	    }
	}
    }

  /* Now, we allocate space to the buttons */
630 631
  child_allocation.y = allocation->y;
  child_allocation.height = allocation->height;
632 633 634

  if (direction == GTK_TEXT_DIR_RTL)
    {
635
      child_allocation.x = allocation->x + allocation->width;
636
      if (need_sliders || path_bar->fake_root)
637 638
	{
	  child_allocation.x -= (path_bar->spacing + path_bar->slider_width);
639
	  up_slider_offset = allocation->width - path_bar->slider_width;
640 641 642 643
	}
    }
  else
    {
644
      child_allocation.x = allocation->x;
645
      if (need_sliders || path_bar->fake_root)
646
	{
647
	  up_slider_offset = 0;
648 649 650 651 652 653
	  child_allocation.x += (path_bar->spacing + path_bar->slider_width);
	}
    }

  for (list = first_button; list; list = list->prev)
    {
654
      GtkAllocation widget_allocation;
655 656 657 658 659
      ButtonData *button_data;

      button_data = BUTTON_DATA (list->data);
      child = button_data->button;

660
      gtk_widget_get_preferred_size (child, &child_requisition, NULL);
661 662

      child_allocation.width = MIN (child_requisition.width,
663
				    allocation_width - (path_bar->spacing + path_bar->slider_width) * 2);
664

665 666
      if (direction == GTK_TEXT_DIR_RTL)
	child_allocation.x -= child_allocation.width;
667 668 669 670

      /* Check to see if we've don't have any more space to allocate buttons */
      if (need_sliders && direction == GTK_TEXT_DIR_RTL)
	{
671
          gtk_widget_get_allocation (widget, &widget_allocation);
672
	  if (child_allocation.x - path_bar->spacing - path_bar->slider_width < widget_allocation.x)
673 674 675 676
	    break;
	}
      else if (need_sliders && direction == GTK_TEXT_DIR_LTR)
	{
677
          gtk_widget_get_allocation (widget, &widget_allocation);
678
	  if (child_allocation.x + child_allocation.width + path_bar->spacing + path_bar->slider_width >
679
	      widget_allocation.x + allocation_width)
680 681 682
	    break;
	}

683
      if (child_allocation.width < child_requisition.width)
684 685 686 687 688 689 690
	{
	  if (!gtk_widget_get_has_tooltip (child))
	    gtk_widget_set_tooltip_text (child, button_data->dir_name);
	}
      else if (gtk_widget_get_has_tooltip (child))
	gtk_widget_set_tooltip_text (child, NULL);
      
691
      needs_reorder |= gtk_widget_get_child_visible (child) == FALSE;
692
      gtk_widget_set_child_visible (child, TRUE);
693 694 695
      gtk_widget_size_allocate (child, &child_allocation);

      if (direction == GTK_TEXT_DIR_RTL)
696
	child_allocation.x -= path_bar->spacing;
697
      else
698
	child_allocation.x += child_allocation.width + path_bar->spacing;
699 700 701 702
    }
  /* Now we go hide all the widgets that don't fit */
  while (list)
    {
703
      child = BUTTON_DATA (list->data)->button;
704
      needs_reorder |= gtk_widget_get_child_visible (child) == TRUE;
705
      gtk_widget_set_child_visible (child, FALSE);
706 707 708 709
      list = list->prev;
    }
  for (list = first_button->next; list; list = list->next)
    {
710
      child = BUTTON_DATA (list->data)->button;
711
      needs_reorder |= gtk_widget_get_child_visible (child) == TRUE;
712
      gtk_widget_set_child_visible (child, FALSE);
713 714
    }

715
  if (need_sliders || path_bar->fake_root)
716 717 718 719 720
    {
      child_allocation.width = path_bar->slider_width;
      child_allocation.x = up_slider_offset + allocation->x;
      gtk_widget_size_allocate (path_bar->up_slider_button, &child_allocation);

721
      needs_reorder |= gtk_widget_get_child_visible (path_bar->up_slider_button) == FALSE;
722 723 724 725
      gtk_widget_set_child_visible (path_bar->up_slider_button, TRUE);
      gtk_widget_show_all (path_bar->up_slider_button);
    }
  else
726 727 728 729
    {
      needs_reorder |= gtk_widget_get_child_visible (path_bar->up_slider_button) == TRUE;
      gtk_widget_set_child_visible (path_bar->up_slider_button, FALSE);
    }
730 731 732 733
      
  if (need_sliders)
    {
      child_allocation.width = path_bar->slider_width;
734 735

      if (direction == GTK_TEXT_DIR_RTL)
736
	child_allocation.x = 0;
737
      else
738
	child_allocation.x = allocation->width - path_bar->slider_width;
739 740 741

      child_allocation.x += allocation->x;
      
742 743
      gtk_widget_size_allocate (path_bar->down_slider_button, &child_allocation);

744
      needs_reorder |= gtk_widget_get_child_visible (path_bar->down_slider_button) == FALSE;
745 746 747 748 749
      gtk_widget_set_child_visible (path_bar->down_slider_button, TRUE);
      gtk_widget_show_all (path_bar->down_slider_button);
      gtk_path_bar_update_slider_buttons (path_bar);
    }
  else
750 751 752 753
    {
      needs_reorder |= gtk_widget_get_child_visible (path_bar->down_slider_button) == TRUE;
      gtk_widget_set_child_visible (path_bar->down_slider_button, FALSE);
    }
754

755 756
  if (needs_reorder)
    child_ordering_changed (path_bar);
757 758
}

759
static void
760
gtk_path_bar_style_updated (GtkWidget *widget)
761
{
762
  GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->style_updated (widget);
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780

  gtk_path_bar_check_icon_theme (GTK_PATH_BAR (widget));
}

static void
gtk_path_bar_screen_changed (GtkWidget *widget,
			     GdkScreen *previous_screen)
{
  if (GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->screen_changed)
    GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->screen_changed (widget, previous_screen);

  /* We might nave a new settings, so we remove the old one */
  if (previous_screen)
    remove_settings_signal (GTK_PATH_BAR (widget), previous_screen);

  gtk_path_bar_check_icon_theme (GTK_PATH_BAR (widget));
}

781 782 783 784 785 786 787 788 789 790 791 792 793 794
static gboolean
gtk_path_bar_scroll (GtkWidget      *widget,
		     GdkEventScroll *event)
{
  switch (event->direction)
    {
    case GDK_SCROLL_RIGHT:
    case GDK_SCROLL_DOWN:
      gtk_path_bar_scroll_down (GTK_PATH_BAR (widget));
      break;
    case GDK_SCROLL_LEFT:
    case GDK_SCROLL_UP:
      gtk_path_bar_scroll_up (GTK_PATH_BAR (widget));
      break;
Benjamin Otte's avatar
Benjamin Otte committed
795 796
    case GDK_SCROLL_SMOOTH:
      break;
797 798 799 800 801
    }

  return TRUE;
}

802 803 804
static void
gtk_path_bar_add (GtkContainer *container,
		  GtkWidget    *widget)
805

806 807 808 809
{
  gtk_widget_set_parent (widget, GTK_WIDGET (container));
}

810 811 812 813
static void
gtk_path_bar_remove_1 (GtkContainer *container,
		       GtkWidget    *widget)
{
814
  gboolean was_visible = gtk_widget_get_visible (widget);
815 816 817 818 819
  gtk_widget_unparent (widget);
  if (was_visible)
    gtk_widget_queue_resize (GTK_WIDGET (container));
}

820 821 822 823 824 825 826 827 828
static void
gtk_path_bar_remove (GtkContainer *container,
		     GtkWidget    *widget)
{
  GtkPathBar *path_bar;
  GList *children;

  path_bar = GTK_PATH_BAR (container);

829 830 831 832 833 834 835 836 837 838 839 840 841
  if (widget == path_bar->up_slider_button)
    {
      gtk_path_bar_remove_1 (container, widget);
      path_bar->up_slider_button = NULL;
      return;
    }

  if (widget == path_bar->down_slider_button)
    {
      gtk_path_bar_remove_1 (container, widget);
      path_bar->down_slider_button = NULL;
      return;
    }
842

843
  children = path_bar->button_list;
844 845
  while (children)
    {
846
      if (widget == BUTTON_DATA (children->data)->button)
847
	{
848
	  gtk_path_bar_remove_1 (container, widget);
849 850
	  path_bar->button_list = g_list_remove_link (path_bar->button_list, children);
	  g_list_free (children);
851
	  return;
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
	}
      
      children = children->next;
    }
}

static void
gtk_path_bar_forall (GtkContainer *container,
		     gboolean      include_internals,
		     GtkCallback   callback,
		     gpointer      callback_data)
{
  GtkPathBar *path_bar;
  GList *children;

  g_return_if_fail (callback != NULL);
  path_bar = GTK_PATH_BAR (container);

  children = path_bar->button_list;
  while (children)
    {
      GtkWidget *child;
874
      child = BUTTON_DATA (children->data)->button;
875 876 877 878 879
      children = children->next;

      (* callback) (child, callback_data);
    }

880 881 882 883 884
  if (path_bar->up_slider_button)
    (* callback) (path_bar->up_slider_button, callback_data);

  if (path_bar->down_slider_button)
    (* callback) (path_bar->down_slider_button, callback_data);
885 886
}

887 888 889 890 891 892 893
static GtkWidgetPath *
gtk_path_bar_get_path_for_child (GtkContainer *container,
                                 GtkWidget    *child)
{
  GtkPathBar *path_bar = GTK_PATH_BAR (container);
  GtkWidgetPath *path;

894
  path = _gtk_widget_create_path (GTK_WIDGET (path_bar));
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962

  if (gtk_widget_get_visible (child) &&
      gtk_widget_get_child_visible (child))
    {
      GtkWidgetPath *sibling_path;
      GList *visible_children;
      GList *l;
      int pos;

      /* 1. Build the list of visible children, in visually left-to-right order
       * (i.e. independently of the widget's direction).  Note that our
       * button_list is stored in innermost-to-outermost path order!
       */

      visible_children = NULL;

      if (gtk_widget_get_visible (path_bar->down_slider_button) &&
          gtk_widget_get_child_visible (path_bar->down_slider_button))
        visible_children = g_list_prepend (visible_children, path_bar->down_slider_button);

      for (l = path_bar->button_list; l; l = l->next)
        {
          ButtonData *data = l->data;

          if (gtk_widget_get_visible (data->button) &&
              gtk_widget_get_child_visible (data->button))
            visible_children = g_list_prepend (visible_children, data->button);
        }

      if (gtk_widget_get_visible (path_bar->up_slider_button) &&
          gtk_widget_get_child_visible (path_bar->up_slider_button))
        visible_children = g_list_prepend (visible_children, path_bar->up_slider_button);

      if (gtk_widget_get_direction (GTK_WIDGET (path_bar)) == GTK_TEXT_DIR_RTL)
        visible_children = g_list_reverse (visible_children);

      /* 2. Find the index of the child within that list */

      pos = 0;

      for (l = visible_children; l; l = l->next)
        {
          GtkWidget *button = l->data;

          if (button == child)
            break;

          pos++;
        }

      /* 3. Build the path */

      sibling_path = gtk_widget_path_new ();

      for (l = visible_children; l; l = l->next)
        gtk_widget_path_append_for_widget (sibling_path, l->data);

      gtk_widget_path_append_with_siblings (path, sibling_path, pos);

      g_list_free (visible_children);
      gtk_widget_path_unref (sibling_path);
    }
  else
    gtk_widget_path_append_for_widget (path, child);

  return path;
}

963
static void
964
gtk_path_bar_scroll_down (GtkPathBar *path_bar)
965
{
966
  GtkAllocation allocation, button_allocation;
967
  GList *list;
968
  GList *down_button = NULL;
969
  gint space_available;
970 971 972 973 974 975 976

  if (path_bar->ignore_click)
    {
      path_bar->ignore_click = FALSE;
      return;   
    }

977 978 979 980 981 982
  if (gtk_widget_get_child_visible (BUTTON_DATA (path_bar->button_list->data)->button))
    {
      /* Return if the last button is already visible */
      return;
    }

983 984
  gtk_widget_queue_resize (GTK_WIDGET (path_bar));

985 986 987 988
  /* We find the button at the 'down' end that we have to make
   * visible */
  for (list = path_bar->button_list; list; list = list->next)
    {
989
      if (list->next && gtk_widget_get_child_visible (BUTTON_DATA (list->next->data)->button))
990 991 992 993 994 995
	{
	  down_button = list;
	  break;
	}
    }

996 997 998 999
  gtk_widget_get_allocation (GTK_WIDGET (path_bar), &allocation);
  gtk_widget_get_allocation (BUTTON_DATA (down_button->data)->button, &button_allocation);

  space_available = (allocation.width
1000
		     - 2 * path_bar->spacing - 2 * path_bar->slider_width
1001
                     - button_allocation.width);
1002 1003 1004 1005
  path_bar->first_scrolled_button = down_button;
  
  /* We have space_available free space that's not being used.  
   * So we walk down from the end, adding buttons until we use all free space.
1006
   */
1007
  while (space_available > 0)
1008
    {
1009 1010 1011 1012
      path_bar->first_scrolled_button = down_button;
      down_button = down_button->next;
      if (!down_button)
	break;
1013
      space_available -= (button_allocation.width
1014
			  + path_bar->spacing);
1015 1016 1017 1018
    }
}

static void
1019
gtk_path_bar_scroll_up (GtkPathBar *path_bar)
1020 1021 1022
{
  GList *list;

1023 1024 1025 1026 1027 1028
  if (path_bar->ignore_click)
    {
      path_bar->ignore_click = FALSE;
      return;   
    }

1029 1030 1031 1032 1033 1034 1035 1036
  list = g_list_last (path_bar->button_list);

  if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
    {
      /* Return if the first button is already visible */
      return;
    }

1037 1038
  gtk_widget_queue_resize (GTK_WIDGET (path_bar));

1039
  for ( ; list; list = list->prev)
1040
    {
1041
      if (list->prev && gtk_widget_get_child_visible (BUTTON_DATA (list->prev->data)->button))
1042
	{
1043 1044
	  if (list->prev == path_bar->fake_root)
	    path_bar->fake_root = NULL;
1045 1046 1047 1048 1049 1050
	  path_bar->first_scrolled_button = list;
	  return;
	}
    }
}

1051 1052 1053 1054 1055 1056 1057
static gboolean
gtk_path_bar_scroll_timeout (GtkPathBar *path_bar)
{
  gboolean retval = FALSE;

  if (path_bar->timer)
    {
1058
      if (path_bar->scrolling_up)
1059
	gtk_path_bar_scroll_up (path_bar);
1060
      else if (path_bar->scrolling_down)
1061
	gtk_path_bar_scroll_down (path_bar);
1062 1063 1064

      if (path_bar->need_timer) 
	{
1065 1066 1067 1068 1069
          GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (path_bar));
          guint        timeout;

          g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);

1070 1071
	  path_bar->need_timer = FALSE;

1072
	  path_bar->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
					   (GSourceFunc)gtk_path_bar_scroll_timeout,
					   path_bar);
	}
      else
	retval = TRUE;
    }

  return retval;
}

static void 
gtk_path_bar_stop_scrolling (GtkPathBar *path_bar)
{
  if (path_bar->timer)
    {
      g_source_remove (path_bar->timer);
      path_bar->timer = 0;
      path_bar->need_timer = FALSE;
    }
}

1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
static gboolean
gtk_path_bar_slider_up_defocus (GtkWidget      *widget,
                                    GdkEventButton *event,
                                    GtkPathBar     *path_bar)
{
  GList *list;
  GList *up_button = NULL;

  if (event->type != GDK_FOCUS_CHANGE)
    return FALSE;

  for (list = g_list_last (path_bar->button_list); list; list = list->prev)
    {
      if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
        {
          up_button = list;
          break;
        }
    }

  /* don't let the focus vanish */
1115
  if ((!gtk_widget_is_sensitive (path_bar->up_slider_button)) ||
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
      (!gtk_widget_get_child_visible (path_bar->up_slider_button)))
    gtk_widget_grab_focus (BUTTON_DATA (up_button->data)->button);

  return FALSE;
}

static gboolean
gtk_path_bar_slider_down_defocus (GtkWidget      *widget,
                                    GdkEventButton *event,
                                    GtkPathBar     *path_bar)
{
  GList *list;
  GList *down_button = NULL;

  if (event->type != GDK_FOCUS_CHANGE)
    return FALSE;

  for (list = path_bar->button_list; list; list = list->next)
    {
      if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
        {
          down_button = list;
          break;
        }
    }

  /* don't let the focus vanish */
1143
  if ((!gtk_widget_is_sensitive (path_bar->down_slider_button)) ||
1144 1145 1146 1147 1148 1149
      (!gtk_widget_get_child_visible (path_bar->down_slider_button)))
    gtk_widget_grab_focus (BUTTON_DATA (down_button->data)->button);

  return FALSE;
}

1150 1151 1152 1153 1154
static gboolean
gtk_path_bar_slider_button_press (GtkWidget      *widget, 
				  GdkEventButton *event,
				  GtkPathBar     *path_bar)
{
1155
  if (event->type != GDK_BUTTON_PRESS || event->button != GDK_BUTTON_PRIMARY)
1156 1157 1158 1159 1160
    return FALSE;

  path_bar->ignore_click = FALSE;

  if (widget == path_bar->up_slider_button)
1161
    {
1162
      path_bar->scrolling_down = FALSE;
1163
      path_bar->scrolling_up = TRUE;
1164
      gtk_path_bar_scroll_up (path_bar);
1165
    }
1166
  else if (widget == path_bar->down_slider_button)
1167 1168
    {
      path_bar->scrolling_up = FALSE;
1169
      path_bar->scrolling_down = TRUE;
1170
      gtk_path_bar_scroll_down (path_bar);
1171
    }
1172 1173 1174

  if (!path_bar->timer)
    {
1175 1176 1177 1178 1179
      GtkSettings *settings = gtk_widget_get_settings (widget);
      guint        timeout;

      g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);

1180
      path_bar->need_timer = TRUE;
1181
      path_bar->timer = gdk_threads_add_timeout (timeout,
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
				       (GSourceFunc)gtk_path_bar_scroll_timeout,
				       path_bar);
    }

  return FALSE;
}

static gboolean
gtk_path_bar_slider_button_release (GtkWidget      *widget, 
				    GdkEventButton *event,
				    GtkPathBar     *path_bar)
{
  if (event->type != GDK_BUTTON_RELEASE)
    return FALSE;

  path_bar->ignore_click = TRUE;
  gtk_path_bar_stop_scrolling (path_bar);

  return FALSE;
}

static void
gtk_path_bar_grab_notify (GtkWidget *widget,
			  gboolean   was_grabbed)
{
  if (!was_grabbed)
    gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
}

static void
gtk_path_bar_state_changed (GtkWidget    *widget,
			    GtkStateType  previous_state)
{
1215
  if (!gtk_widget_is_sensitive (widget))
1216 1217 1218 1219
    gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
}


1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264
/* Changes the icons wherever it is needed */
static void
reload_icons (GtkPathBar *path_bar)
{
  GList *list;

  if (path_bar->root_icon)
    {
      g_object_unref (path_bar->root_icon);
      path_bar->root_icon = NULL;
    }
  if (path_bar->home_icon)
    {
      g_object_unref (path_bar->home_icon);
      path_bar->home_icon = NULL;
    }
  if (path_bar->desktop_icon)
    {
      g_object_unref (path_bar->desktop_icon);
      path_bar->desktop_icon = NULL;
    }

  for (list = path_bar->button_list; list; list = list->next)
    {
      ButtonData *button_data;
      gboolean current_dir;

      button_data = BUTTON_DATA (list->data);
      if (button_data->type != NORMAL_BUTTON)
	{
	  current_dir = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button));
	  gtk_path_bar_update_button_appearance (path_bar, button_data, current_dir);
	}
    }
  
}

static void
change_icon_theme (GtkPathBar *path_bar)
{
  GtkSettings *settings;
  gint width, height;

  settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (path_bar)));

1265
  if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301
    path_bar->icon_size = MAX (width, height);
  else
    path_bar->icon_size = FALLBACK_ICON_SIZE;

  reload_icons (path_bar);
}
/* Callback used when a GtkSettings value changes */
static void
settings_notify_cb (GObject    *object,
		    GParamSpec *pspec,
		    GtkPathBar *path_bar)
{
  const char *name;

  name = g_param_spec_get_name (pspec);

  if (! strcmp (name, "gtk-icon-theme-name") ||
      ! strcmp (name, "gtk-icon-sizes"))
    change_icon_theme (path_bar);
}

static void
gtk_path_bar_check_icon_theme (GtkPathBar *path_bar)
{
  GtkSettings *settings;

  if (path_bar->settings_signal_id)
    return;

  settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (path_bar)));
  path_bar->settings_signal_id = g_signal_connect (settings, "notify", G_CALLBACK (settings_notify_cb), path_bar);

  change_icon_theme (path_bar);
}

/* Public functions and their helpers */
1302 1303 1304 1305 1306
static void
gtk_path_bar_clear_buttons (GtkPathBar *path_bar)
{
  while (path_bar->button_list != NULL)
    {
1307
      gtk_container_remove (GTK_CONTAINER (path_bar), BUTTON_DATA (path_bar->button_list->data)->button);
1308
    }
1309
  path_bar->first_scrolled_button = NULL;
1310
  path_bar->fake_root = NULL;
1311 1312
}

1313 1314 1315 1316
static void
button_clicked_cb (GtkWidget *button,
		   gpointer   data)
{
1317
  ButtonData *button_data;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1318 1319 1320
  GtkPathBar *path_bar;
  GList *button_list;
  gboolean child_is_hidden;
1321
  GFile *child_file;
1322 1323 1324 1325

  button_data = BUTTON_DATA (data);
  if (button_data->ignore_changes)
    return;
1326

1327
  path_bar = GTK_PATH_BAR (gtk_widget_get_parent (button));
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1328 1329 1330 1331

  button_list = g_list_find (path_bar->button_list, button_data);
  g_assert (button_list != NULL);

1332 1333
  g_signal_handlers_block_by_func (button,
				   G_CALLBACK (button_clicked_cb), data);
1334
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
1335 1336
  g_signal_handlers_unblock_by_func (button,
				     G_CALLBACK (button_clicked_cb), data);
1337

Federico Mena Quintero's avatar
Federico Mena Quintero committed
1338 1339 1340 1341 1342
  if (button_list->prev)
    {
      ButtonData *child_data;

      child_data = BUTTON_DATA (button_list->prev->data);
1343
      child_file = child_data->file;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1344 1345 1346
      child_is_hidden = child_data->file_is_hidden;
    }
  else
1347
    {
1348
      child_file = NULL;
1349 1350
      child_is_hidden = FALSE;
    }
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1351

1352
  g_signal_emit (path_bar, path_bar_signals [PATH_CLICKED], 0,
1353
		 button_data->file, child_file, child_is_hidden);
1354 1355
}

1356 1357 1358 1359 1360 1361 1362
struct SetButtonImageData
{
  GtkPathBar *path_bar;
  ButtonData *button_data;
};

static void
1363 1364 1365 1366
set_button_image_get_info_cb (GCancellable *cancellable,
			      GFileInfo    *info,
			      const GError *error,
			      gpointer      user_data)
1367
{
1368
  gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1369 1370 1371
  GdkPixbuf *pixbuf;
  struct SetButtonImageData *data = user_data;

1372
  if (cancellable != data->button_data->cancellable)
1373 1374
    goto out;

1375
  data->button_data->cancellable = NULL;
1376

1377 1378 1379 1380 1381 1382
  if (!data->button_data->button)
    {
      g_free (data->button_data);
      goto out;
    }

1383 1384 1385
  if (cancelled || error)
    goto out;

1386 1387
  pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->path_bar),
			 	       data->path_bar->icon_size);
1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411
  gtk_image_set_from_pixbuf (GTK_IMAGE (data->button_data->image), pixbuf);

  switch (data->button_data->type)
    {
      case HOME_BUTTON:
	if (data->path_bar->home_icon)
	  g_object_unref (pixbuf);
	else
	  data->path_bar->home_icon = pixbuf;
	break;

      case DESKTOP_BUTTON:
	if (data->path_bar->desktop_icon)
	  g_object_unref (pixbuf);
	else
	  data->path_bar->desktop_icon = pixbuf;
	break;

      default:
	break;
    };

out:
  g_free (data);
1412
  g_object_unref (cancellable);
1413 1414 1415 1416 1417
}

static void
set_button_image (GtkPathBar *path_bar,
		  ButtonData *button_data)
1418
{
1419
  GtkFileSystemVolume *volume;
1420
  struct SetButtonImageData *data;
1421

1422
  switch (button_data->type)
1423
    {
1424
    case ROOT_BUTTON:
1425 1426

      if (path_bar->root_icon != NULL)
1427 1428 1429 1430
        {
          gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
	  break;
	}
1431

1432
      volume = _gtk_file_system_get_volume_for_file (path_bar->file_system, path_bar->root_file);
1433
      if (volume == NULL)
1434
	return;
1435

1436 1437 1438 1439
      path_bar->root_icon = _gtk_file_system_volume_render_icon (volume,
								 GTK_WIDGET (path_bar),
								 path_bar->icon_size,
								 NULL);
1440
      _gtk_file_system_volume_unref (volume);
1441

1442 1443 1444
      gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
      break;

1445
    case HOME_BUTTON:
1446
      if (path_bar->home_icon != NULL)
1447 1448 1449 1450 1451 1452 1453 1454 1455
        {
	  gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->home_icon);
	  break;
	}

      data = g_new0 (struct SetButtonImageData, 1);
      data->path_bar = path_bar;
      data->button_data = button_data;

1456 1457
      if (button_data->cancellable)
	g_cancellable_cancel (button_data->cancellable);
1458

1459
      button_data->cancellable =
1460 1461 1462 1463 1464
        _gtk_file_system_get_info (path_bar->file_system,
				   path_bar->home_file,
				   "standard::icon",
				   set_button_image_get_info_cb,
				   data);
1465 1466
      break;

1467 1468
    case DESKTOP_BUTTON:
      if (path_bar->desktop_icon != NULL)
1469 1470 1471 1472 1473 1474 1475 1476 1477
        {
	  gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->desktop_icon);
	  break;
	}

      data = g_new0 (struct SetButtonImageData, 1);
      data->path_bar = path_bar;
      data->button_data = button_data;

1478 1479
      if (button_data->cancellable)
	g_cancellable_cancel (button_data->cancellable);