]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkpathbar.c
Custom tab label
[~andy/gtk] / gtk / gtkpathbar.c
index ade55d219752ae242a343d606845e240d3a4cf4e..1e25b6b7cfe6cdbaba0badbf1e3e4b59ee50c650 100644 (file)
@@ -48,8 +48,7 @@ typedef enum {
 
 #define BUTTON_DATA(x) ((ButtonData *)(x))
 
-#define SCROLL_TIMEOUT           150
-#define INITIAL_SCROLL_TIMEOUT   300
+#define SCROLL_DELAY_FACTOR 5
 
 static guint path_bar_signals [LAST_SIGNAL] = { 0 };
 
@@ -66,6 +65,7 @@ struct _ButtonData
   GtkFilePath *path;
   GtkWidget *image;
   GtkWidget *label;
+  GtkFileSystemHandle *handle;
   guint ignore_changes : 1;
   guint file_is_hidden : 1;
 };
@@ -75,7 +75,7 @@ struct _ButtonData
  */
 #define BUTTON_IS_FAKE_ROOT(button) ((button)->type == HOME_BUTTON)
 
-G_DEFINE_TYPE (GtkPathBar, gtk_path_bar, GTK_TYPE_CONTAINER);
+G_DEFINE_TYPE (GtkPathBar, gtk_path_bar, GTK_TYPE_CONTAINER)
 
 static void gtk_path_bar_finalize                 (GObject          *object);
 static void gtk_path_bar_dispose                  (GObject          *object);
@@ -125,7 +125,7 @@ get_slider_button (GtkPathBar  *path_bar,
   gtk_widget_push_composite_child ();
 
   button = gtk_button_new ();
-  gtk_button_set_focus_on_click (button, FALSE);
+  gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
   gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (arrow_type, GTK_SHADOW_OUT));
   gtk_container_add (GTK_CONTAINER (path_bar), button);
   gtk_widget_show_all (button);
@@ -141,7 +141,9 @@ gtk_path_bar_init (GtkPathBar *path_bar)
   GTK_WIDGET_SET_FLAGS (path_bar, GTK_NO_WINDOW);
   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (path_bar), FALSE);
 
-  path_bar->spacing = 3;
+  path_bar->set_path_handle = NULL;
+
+  path_bar->spacing = 0;
   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);
   path_bar->icon_size = FALLBACK_ICON_SIZE;
@@ -191,8 +193,9 @@ gtk_path_bar_class_init (GtkPathBarClass *path_bar_class)
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkPathBarClass, path_clicked),
                  NULL, NULL,
-                 _gtk_marshal_VOID__POINTER_BOOLEAN,
-                 G_TYPE_NONE, 2,
+                 _gtk_marshal_VOID__POINTER_POINTER_BOOLEAN,
+                 G_TYPE_NONE, 3,
+                 G_TYPE_POINTER,
                  G_TYPE_POINTER,
                  G_TYPE_BOOLEAN);
 }
@@ -247,8 +250,23 @@ remove_settings_signal (GtkPathBar *path_bar,
 static void
 gtk_path_bar_dispose (GObject *object)
 {
-  remove_settings_signal (GTK_PATH_BAR (object),
-                         gtk_widget_get_screen (GTK_WIDGET (object)));
+  GList *list;
+  GtkPathBar *path_bar = GTK_PATH_BAR (object);
+
+  remove_settings_signal (path_bar, gtk_widget_get_screen (GTK_WIDGET (object)));
+
+  if (path_bar->set_path_handle)
+    gtk_file_system_cancel_operation (path_bar->set_path_handle);
+  path_bar->set_path_handle = NULL;
+
+  for (list = path_bar->button_list; list; list = list->next)
+    {
+      ButtonData *button_data = BUTTON_DATA (list->data);
+
+      if (button_data->handle)
+       gtk_file_system_cancel_operation (button_data->handle);
+      button_data->handle = NULL;
+    }
 
   G_OBJECT_CLASS (gtk_path_bar_parent_class)->dispose (object);
 }
@@ -306,13 +324,19 @@ gtk_path_bar_update_slider_buttons (GtkPathBar *path_bar)
 
       button = BUTTON_DATA (path_bar->button_list->data)->button;
       if (gtk_widget_get_child_visible (button))
-       gtk_widget_set_sensitive (path_bar->down_slider_button, FALSE);
+       {
+         gtk_path_bar_stop_scrolling (path_bar);
+         gtk_widget_set_sensitive (path_bar->down_slider_button, FALSE);
+       }
       else
        gtk_widget_set_sensitive (path_bar->down_slider_button, TRUE);
 
       button = BUTTON_DATA (g_list_last (path_bar->button_list)->data)->button;
       if (gtk_widget_get_child_visible (button))
-       gtk_widget_set_sensitive (path_bar->up_slider_button, FALSE);
+       {
+         gtk_path_bar_stop_scrolling (path_bar);
+         gtk_widget_set_sensitive (path_bar->up_slider_button, FALSE);
+       }
       else
        gtk_widget_set_sensitive (path_bar->up_slider_button, TRUE);
     }
@@ -733,21 +757,24 @@ gtk_path_bar_scroll_timeout (GtkPathBar *path_bar)
     {
       if (path_bar->scrolling_up)
        gtk_path_bar_scroll_up (path_bar->up_slider_button, path_bar);
-      else 
+      else if (path_bar->scrolling_down)
        gtk_path_bar_scroll_down (path_bar->down_slider_button, path_bar);
 
       if (path_bar->need_timer) 
        {
+          GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (path_bar));
+          guint        timeout;
+
+          g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
+
          path_bar->need_timer = FALSE;
 
-         path_bar->timer = g_timeout_add (SCROLL_TIMEOUT,
+         path_bar->timer = g_timeout_add (timeout * SCROLL_DELAY_FACTOR,
                                           (GSourceFunc)gtk_path_bar_scroll_timeout,
                                           path_bar);
-         
        }
       else
        retval = TRUE;
-      
     }
 
   GDK_THREADS_LEAVE ();
@@ -778,19 +805,26 @@ gtk_path_bar_slider_button_press (GtkWidget      *widget,
 
   if (widget == path_bar->up_slider_button)
     {
+      path_bar->scrolling_down = FALSE;
       path_bar->scrolling_up = TRUE;
       gtk_path_bar_scroll_up (path_bar->up_slider_button, path_bar);
     }
   else if (widget == path_bar->down_slider_button)
     {
       path_bar->scrolling_up = FALSE;
+      path_bar->scrolling_down = TRUE;
       gtk_path_bar_scroll_down (path_bar->down_slider_button, path_bar);
     }
 
   if (!path_bar->timer)
     {
+      GtkSettings *settings = gtk_widget_get_settings (widget);
+      guint        timeout;
+
+      g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
+
       path_bar->need_timer = TRUE;
-      path_bar->timer = g_timeout_add (INITIAL_SCROLL_TIMEOUT,
+      path_bar->timer = g_timeout_add (timeout,
                                       (GSourceFunc)gtk_path_bar_scroll_timeout,
                                       path_bar);
     }
@@ -930,6 +964,7 @@ button_clicked_cb (GtkWidget *button,
   GtkPathBar *path_bar;
   GList *button_list;
   gboolean child_is_hidden;
+  GtkFilePath *child_path;
 
   button_data = BUTTON_DATA (data);
   if (button_data->ignore_changes)
@@ -940,37 +975,103 @@ button_clicked_cb (GtkWidget *button,
   button_list = g_list_find (path_bar->button_list, button_data);
   g_assert (button_list != NULL);
 
+  g_signal_handlers_block_by_func (button,
+                                  G_CALLBACK (button_clicked_cb), data);
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+  g_signal_handlers_unblock_by_func (button,
+                                    G_CALLBACK (button_clicked_cb), data);
 
   if (button_list->prev)
     {
       ButtonData *child_data;
 
       child_data = BUTTON_DATA (button_list->prev->data);
+      child_path = child_data->path;
       child_is_hidden = child_data->file_is_hidden;
     }
   else
-    child_is_hidden = FALSE;
+    {
+      child_path = NULL;
+      child_is_hidden = FALSE;
+    }
 
-  g_signal_emit (path_bar, path_bar_signals [PATH_CLICKED], 0, button_data->path, child_is_hidden);
+  g_signal_emit (path_bar, path_bar_signals [PATH_CLICKED], 0,
+                button_data->path, child_path, child_is_hidden);
 }
 
-static GdkPixbuf *
-get_button_image (GtkPathBar *path_bar,
-                 ButtonType  button_type)
+struct SetButtonImageData
+{
+  GtkPathBar *path_bar;
+  ButtonData *button_data;
+};
+
+static void
+set_button_image_get_info_cb (GtkFileSystemHandle *handle,
+                             const GtkFileInfo   *info,
+                             const GError        *error,
+                             gpointer             user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  GdkPixbuf *pixbuf;
+  struct SetButtonImageData *data = user_data;
+
+  if (handle != data->button_data->handle)
+    goto out;
+
+  data->button_data->handle = NULL;
+
+  if (cancelled || error)
+    goto out;
+
+  pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (data->path_bar),
+                                     data->path_bar->icon_size, NULL);
+  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);
+  g_object_unref (handle);
+}
+
+static void
+set_button_image (GtkPathBar *path_bar,
+                 ButtonData *button_data)
 {
   GtkFileSystemVolume *volume;
+  struct SetButtonImageData *data;
 
-  switch (button_type)
+  switch (button_data->type)
     {
     case ROOT_BUTTON:
 
       if (path_bar->root_icon != NULL)
-       return path_bar->root_icon;
+        {
+          gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
+         break;
+       }
       
       volume = gtk_file_system_get_volume_for_path (path_bar->file_system, path_bar->root_path);
       if (volume == NULL)
-       return NULL;
+       return;
 
       path_bar->root_icon = gtk_file_system_volume_render_icon (path_bar->file_system,
                                                                volume,
@@ -979,37 +1080,63 @@ get_button_image (GtkPathBar *path_bar,
                                                                NULL);
       gtk_file_system_volume_free (path_bar->file_system, volume);
 
-      return path_bar->root_icon;
+      gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
+      break;
+
     case HOME_BUTTON:
       if (path_bar->home_icon != NULL)
-       return path_bar->home_icon;
-
-      path_bar->home_icon = gtk_file_system_render_icon (path_bar->file_system,
-                                                        path_bar->home_path,
-                                                        GTK_WIDGET (path_bar),
-                                                        path_bar->icon_size,
-                                                        NULL);
-      return path_bar->home_icon;
+        {
+         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;
+
+      if (button_data->handle)
+       gtk_file_system_cancel_operation (button_data->handle);
+
+      button_data->handle =
+        gtk_file_system_get_info (path_bar->file_system,
+                                 path_bar->home_path,
+                                 GTK_FILE_INFO_ICON,
+                                 set_button_image_get_info_cb,
+                                 data);
+      break;
+
     case DESKTOP_BUTTON:
       if (path_bar->desktop_icon != NULL)
-       return path_bar->desktop_icon;
-
-      path_bar->desktop_icon = gtk_file_system_render_icon (path_bar->file_system,
-                                                           path_bar->desktop_path,
-                                                           GTK_WIDGET (path_bar),
-                                                           path_bar->icon_size,
-                                                           NULL);
-      return path_bar->desktop_icon;
+        {
+         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;
+
+      if (button_data->handle)
+       gtk_file_system_cancel_operation (button_data->handle);
+
+      button_data->handle =
+        gtk_file_system_get_info (path_bar->file_system,
+                                 path_bar->desktop_path,
+                                 GTK_FILE_INFO_ICON,
+                                 set_button_image_get_info_cb,
+                                 data);
+      break;
     default:
-      return NULL;
+      break;
     }
-  
-  return NULL;
 }
 
 static void
 button_data_free (ButtonData *button_data)
 {
+  if (button_data->handle)
+    gtk_file_system_cancel_operation (button_data->handle);
+
   gtk_file_path_free (button_data->path);
   g_free (button_data->dir_name);
   g_free (button_data);
@@ -1072,9 +1199,7 @@ gtk_path_bar_update_button_appearance (GtkPathBar *path_bar,
 
   if (button_data->image != NULL)
     {
-      GdkPixbuf *pixbuf;
-      pixbuf = get_button_image (path_bar, button_data->type);
-      gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), pixbuf);
+      set_button_image (path_bar, button_data);
     }
 
   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button)) != current_dir)
@@ -1125,7 +1250,7 @@ button_drag_data_get_cb (GtkWidget          *widget,
   gtk_selection_data_set (selection_data,
                          selection_data->target,
                          8,
-                         uri_list,
+                         (guchar *)uri_list,
                          strlen (uri_list));
   g_free (uri_list);
 }
@@ -1151,7 +1276,7 @@ make_directory_button (GtkPathBar  *path_bar,
 
   button_data->type = find_button_type (path_bar, path);
   button_data->button = gtk_toggle_button_new ();
-  gtk_button_set_focus_on_click (button_data->button, FALSE);
+  gtk_button_set_focus_on_click (GTK_BUTTON (button_data->button), FALSE);
 
   switch (button_data->type)
     {
@@ -1272,126 +1397,174 @@ gtk_path_bar_check_parent_path (GtkPathBar         *path_bar,
   return FALSE;
 }
 
-gboolean
-_gtk_path_bar_set_path (GtkPathBar         *path_bar,
-                       const GtkFilePath  *file_path,
-                       const gboolean      keep_trail,     
-                       GError            **error)
+
+struct SetPathInfo
 {
   GtkFilePath *path;
-  gboolean first_directory = TRUE;
-  gboolean result;
-  GList *new_buttons = NULL;
-  GList *fake_root = NULL;
-
-  g_return_val_if_fail (GTK_IS_PATH_BAR (path_bar), FALSE);
-  g_return_val_if_fail (file_path != NULL, FALSE);
-
-  result = TRUE;
-
-  /* Check whether the new path is already present in the pathbar as buttons.
-   * This could be a parent directory or a previous selected subdirectory.
-   */
-  if (keep_trail &&
-      gtk_path_bar_check_parent_path (path_bar, file_path, path_bar->file_system))
-    return TRUE;
+  GtkFilePath *parent_path;
+  GtkPathBar *path_bar;
+  GList *new_buttons;
+  GList *fake_root;
+  gboolean first_directory;
+};
 
-  path = gtk_file_path_copy (file_path);
+static void
+gtk_path_bar_set_path_finish (struct SetPathInfo *info,
+                              gboolean result)
+{
+  if (result)
+    {
+      GList *l;
 
-  gtk_widget_push_composite_child ();
+      gtk_path_bar_clear_buttons (info->path_bar);
+      info->path_bar->button_list = g_list_reverse (info->new_buttons);
+      info->path_bar->fake_root = info->fake_root;
 
-  while (path != NULL)
-    {
-      GtkFilePath *parent_path = NULL;
-      ButtonData *button_data;
-      const gchar *display_name;
-      gboolean is_hidden;
-      GtkFileFolder *file_folder;
-      GtkFileInfo *file_info;
-      gboolean valid;
-
-      valid = gtk_file_system_get_parent (path_bar->file_system,
-                                         path,
-                                         &parent_path,
-                                         error);
-      if (!valid)
+      for (l = info->path_bar->button_list; l; l = l->next)
        {
-         result = FALSE;
-         gtk_file_path_free (path);
-         break;
+         GtkWidget *button = BUTTON_DATA (l->data)->button;
+         gtk_container_add (GTK_CONTAINER (info->path_bar), button);
        }
+    }
+  else
+    {
+      GList *l;
 
-      file_folder = gtk_file_system_get_folder (path_bar->file_system,
-                                               parent_path ? parent_path : path,
-                                               GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN,
-                                               NULL);
-      if (!file_folder)
+      for (l = info->new_buttons; l; l = l->next)
        {
-         result = FALSE;
-         gtk_file_path_free (parent_path);
-         gtk_file_path_free (path);
-         break;
-       }
-
-      file_info = gtk_file_folder_get_info (file_folder, parent_path ? path : NULL, error);
-      g_object_unref (file_folder);
+         ButtonData *button_data;
 
-      if (!file_info)
-       {
-         result = FALSE;
-         gtk_file_path_free (parent_path);
-         gtk_file_path_free (path);
-         break;
+         button_data = BUTTON_DATA (l->data);
+         gtk_widget_destroy (button_data->button);
        }
 
-      display_name = gtk_file_info_get_display_name (file_info);
-      is_hidden = gtk_file_info_get_is_hidden (file_info);
-
-      button_data = make_directory_button (path_bar, display_name, path, first_directory, is_hidden);
-      gtk_file_info_free (file_info);
-      gtk_file_path_free (path);
+      g_list_free (info->new_buttons);
+    }
 
-      new_buttons = g_list_prepend (new_buttons, button_data);
+  if (info->path)
+    gtk_file_path_free (info->path);
+  if (info->parent_path)
+    gtk_file_path_free (info->parent_path);
+  g_free (info);
+}
 
-      if (BUTTON_IS_FAKE_ROOT (button_data))
-       fake_root = new_buttons;
+static void
+gtk_path_bar_get_info_callback (GtkFileSystemHandle *handle,
+                               const GtkFileInfo   *file_info,
+                               const GError        *error,
+                               gpointer             data)
+{
+  gboolean cancelled = handle->cancelled;
+  struct SetPathInfo *path_info = data;
+  ButtonData *button_data;
+  const gchar *display_name;
+  gboolean is_hidden;
+  gboolean valid;
 
-      path = parent_path;
-      first_directory = FALSE;
+  if (handle != path_info->path_bar->set_path_handle)
+    {
+      gtk_path_bar_set_path_finish (path_info, FALSE);
+      g_object_unref (handle);
+      return;
     }
 
-  if (result)
+  g_object_unref (handle);
+  path_info->path_bar->set_path_handle = NULL;
+
+  if (cancelled || !file_info)
     {
-      GList *l;
+      gtk_path_bar_set_path_finish (path_info, FALSE);
+      return;
+    }
 
-      gtk_path_bar_clear_buttons (path_bar);
-      path_bar->button_list = g_list_reverse (new_buttons);
-      path_bar->fake_root = fake_root;
+  display_name = gtk_file_info_get_display_name (file_info);
+  is_hidden = gtk_file_info_get_is_hidden (file_info);
 
-      for (l = path_bar->button_list; l; l = l->next)
-       {
-         GtkWidget *button = BUTTON_DATA (l->data)->button;
-         gtk_container_add (GTK_CONTAINER (path_bar), button);
-       }
+  gtk_widget_push_composite_child ();
+  button_data = make_directory_button (path_info->path_bar, display_name,
+                                       path_info->path,
+                                      path_info->first_directory, is_hidden);
+  gtk_widget_pop_composite_child ();
+  gtk_file_path_free (path_info->path);
+
+  path_info->new_buttons = g_list_prepend (path_info->new_buttons, button_data);
+
+  if (BUTTON_IS_FAKE_ROOT (button_data))
+    path_info->fake_root = path_info->new_buttons;
+
+  path_info->path = path_info->parent_path;
+  path_info->first_directory = FALSE;
+
+  if (!path_info->path)
+    {
+      gtk_path_bar_set_path_finish (path_info, TRUE);
+      return;
     }
-  else
+
+  valid = gtk_file_system_get_parent (path_info->path_bar->file_system,
+                                     path_info->path,
+                                     &path_info->parent_path,
+                                     NULL);
+  if (!valid)
     {
-      GList *l;
+      gtk_path_bar_set_path_finish (path_info, FALSE);
+      return;
+    }
 
-      for (l = new_buttons; l; l = l->next)
-       {
-         ButtonData *button_data;
+  path_info->path_bar->set_path_handle =
+    gtk_file_system_get_info (handle->file_system,
+                             path_info->path,
+                             GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN,
+                             gtk_path_bar_get_info_callback,
+                             path_info);
+}
 
-         button_data = BUTTON_DATA (l->data);
-         gtk_widget_destroy (button_data->button);
-       }
+gboolean
+_gtk_path_bar_set_path (GtkPathBar         *path_bar,
+                       const GtkFilePath  *file_path,
+                       const gboolean      keep_trail,     
+                       GError            **error)
+{
+  struct SetPathInfo *info;
+  gboolean result;
+
+  g_return_val_if_fail (GTK_IS_PATH_BAR (path_bar), FALSE);
+  g_return_val_if_fail (file_path != NULL, FALSE);
+
+  result = TRUE;
+
+  /* Check whether the new path is already present in the pathbar as buttons.
+   * This could be a parent directory or a previous selected subdirectory.
+   */
+  if (keep_trail &&
+      gtk_path_bar_check_parent_path (path_bar, file_path, path_bar->file_system))
+    return TRUE;
+
+  info = g_new0 (struct SetPathInfo, 1);
+  info->path = gtk_file_path_copy (file_path);
+  info->path_bar = path_bar;
+  info->first_directory = TRUE;
 
-      g_list_free (new_buttons);
+  result = gtk_file_system_get_parent (path_bar->file_system,
+                                      info->path, &info->parent_path, error);
+  if (!result)
+    {
+      gtk_file_path_free (info->path);
+      g_free (info);
+      return result;
     }
 
-  gtk_widget_pop_composite_child ();
+  if (path_bar->set_path_handle)
+    gtk_file_system_cancel_operation (path_bar->set_path_handle);
+
+  path_bar->set_path_handle =
+    gtk_file_system_get_info (path_bar->file_system,
+                             info->path,
+                             GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN,
+                             gtk_path_bar_get_info_callback,
+                             info);
 
-  return result;
+  return TRUE;
 }
 
 /* FIXME: This should be a construct-only property */