]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkpathbar.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkpathbar.c
index d5447e8cd068171ac07f396830a2f4fbf0d8800f..03ac4c9479728bde5d6c8751115fdfdf16fce991 100644 (file)
@@ -13,9 +13,7 @@
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
 
 #include <string.h>
 
-#include "gtktogglebutton.h"
-#include "gtkalignment.h"
 #include "gtkarrow.h"
+#include "gtkbox.h"
 #include "gtkdnd.h"
+#include "gtkiconfactory.h"
+#include "gtkicontheme.h"
 #include "gtkimage.h"
 #include "gtkintl.h"
-#include "gtkicontheme.h"
-#include "gtkiconfactory.h"
 #include "gtklabel.h"
-#include "gtkhbox.h"
 #include "gtkmain.h"
 #include "gtkmarshalers.h"
+#include "gtksettings.h"
+#include "gtktogglebutton.h"
+#include "gtkwidgetpath.h"
+#include "gtkwidgetprivate.h"
 
 
 enum {
@@ -103,6 +103,8 @@ static void gtk_path_bar_forall                   (GtkContainer     *container,
                                                   gboolean          include_internals,
                                                   GtkCallback       callback,
                                                   gpointer          callback_data);
+static GtkWidgetPath *gtk_path_bar_get_path_for_child (GtkContainer *container,
+                                                       GtkWidget    *child);
 static gboolean gtk_path_bar_scroll               (GtkWidget        *widget,
                                                   GdkEventScroll   *event);
 static void gtk_path_bar_scroll_up                (GtkPathBar       *path_bar);
@@ -124,8 +126,7 @@ static void gtk_path_bar_grab_notify              (GtkWidget        *widget,
                                                   gboolean          was_grabbed);
 static void gtk_path_bar_state_changed            (GtkWidget        *widget,
                                                   GtkStateType      previous_state);
-static void gtk_path_bar_style_set                (GtkWidget        *widget,
-                                                  GtkStyle         *previous_style);
+static void gtk_path_bar_style_updated            (GtkWidget        *widget);
 static void gtk_path_bar_screen_changed           (GtkWidget        *widget,
                                                   GdkScreen        *previous_screen);
 static void gtk_path_bar_check_icon_theme         (GtkPathBar       *path_bar);
@@ -160,6 +161,7 @@ get_slider_button (GtkPathBar  *path_bar,
     atk_object_set_name (atk_obj, _("Down Path"));
 
   gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
+  gtk_widget_add_events (button, GDK_SCROLL_MASK);
   gtk_container_add (GTK_CONTAINER (button),
                      gtk_arrow_new (arrow_type, GTK_SHADOW_OUT));
   gtk_container_add (GTK_CONTAINER (path_bar), button);
@@ -176,6 +178,8 @@ get_slider_button (GtkPathBar  *path_bar,
 static void
 gtk_path_bar_init (GtkPathBar *path_bar)
 {
+  GtkStyleContext *context;
+
   gtk_widget_set_has_window (GTK_WIDGET (path_bar), FALSE);
   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (path_bar), FALSE);
 
@@ -204,6 +208,9 @@ gtk_path_bar_init (GtkPathBar *path_bar)
                     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);
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (path_bar));
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
 }
 
 static void
@@ -227,7 +234,7 @@ gtk_path_bar_class_init (GtkPathBarClass *path_bar_class)
   widget_class->map = gtk_path_bar_map;
   widget_class->unmap = gtk_path_bar_unmap;
   widget_class->size_allocate = gtk_path_bar_size_allocate;
-  widget_class->style_set = gtk_path_bar_style_set;
+  widget_class->style_updated = gtk_path_bar_style_updated;
   widget_class->screen_changed = gtk_path_bar_screen_changed;
   widget_class->grab_notify = gtk_path_bar_grab_notify;
   widget_class->state_changed = gtk_path_bar_state_changed;
@@ -236,6 +243,7 @@ gtk_path_bar_class_init (GtkPathBarClass *path_bar_class)
   container_class->add = gtk_path_bar_add;
   container_class->forall = gtk_path_bar_forall;
   container_class->remove = gtk_path_bar_remove;
+  container_class->get_path_for_child = gtk_path_bar_get_path_for_child;
   gtk_container_class_handle_border_width (container_class);
   /* FIXME: */
   /*  container_class->child_type = gtk_path_bar_child_type;*/
@@ -464,9 +472,7 @@ gtk_path_bar_realize (GtkWidget *widget)
 
   path_bar->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
                                            &attributes, attributes_mask);
-  gdk_window_set_user_data (path_bar->event_window, widget);
-
-  gtk_widget_style_attach (widget);
+  gtk_widget_register_window (widget, path_bar->event_window);
 }
 
 static void
@@ -476,13 +482,34 @@ gtk_path_bar_unrealize (GtkWidget *widget)
 
   path_bar = GTK_PATH_BAR (widget);
 
-  gdk_window_set_user_data (path_bar->event_window, NULL);
+  gtk_widget_unregister_window (widget, path_bar->event_window);
   gdk_window_destroy (path_bar->event_window);
   path_bar->event_window = NULL;
 
   GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->unrealize (widget);
 }
 
+static void
+child_ordering_changed (GtkPathBar *path_bar)
+{
+  GList *l;
+
+  if (path_bar->up_slider_button)
+    _gtk_widget_invalidate_style_context (path_bar->up_slider_button,
+                                          GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
+  if (path_bar->down_slider_button)
+    _gtk_widget_invalidate_style_context (path_bar->down_slider_button,
+                                          GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
+
+  for (l = path_bar->button_list; l; l = l->next)
+    {
+      ButtonData *data = l->data;
+
+      _gtk_widget_invalidate_style_context (data->button,
+                                            GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
+    }
+}
+
 /* This is a tad complicated
  */
 static void
@@ -499,6 +526,7 @@ gtk_path_bar_size_allocate (GtkWidget     *widget,
   gboolean need_sliders = FALSE;
   gint up_slider_offset = 0;
   GtkRequisition child_requisition;
+  gboolean needs_reorder = FALSE;
 
   gtk_widget_set_allocation (widget, allocation);
 
@@ -660,6 +688,7 @@ gtk_path_bar_size_allocate (GtkWidget     *widget,
       else if (gtk_widget_get_has_tooltip (child))
        gtk_widget_set_tooltip_text (child, NULL);
       
+      needs_reorder |= gtk_widget_get_child_visible (child) == FALSE;
       gtk_widget_set_child_visible (child, TRUE);
       gtk_widget_size_allocate (child, &child_allocation);
 
@@ -671,12 +700,16 @@ gtk_path_bar_size_allocate (GtkWidget     *widget,
   /* Now we go hide all the widgets that don't fit */
   while (list)
     {
-      gtk_widget_set_child_visible (BUTTON_DATA (list->data)->button, FALSE);
+      child = BUTTON_DATA (list->data)->button;
+      needs_reorder |= gtk_widget_get_child_visible (child) == TRUE;
+      gtk_widget_set_child_visible (child, FALSE);
       list = list->prev;
     }
   for (list = first_button->next; list; list = list->next)
     {
-      gtk_widget_set_child_visible (BUTTON_DATA (list->data)->button, FALSE);
+      child = BUTTON_DATA (list->data)->button;
+      needs_reorder |= gtk_widget_get_child_visible (child) == TRUE;
+      gtk_widget_set_child_visible (child, FALSE);
     }
 
   if (need_sliders || path_bar->fake_root)
@@ -685,11 +718,15 @@ gtk_path_bar_size_allocate (GtkWidget     *widget,
       child_allocation.x = up_slider_offset + allocation->x;
       gtk_widget_size_allocate (path_bar->up_slider_button, &child_allocation);
 
+      needs_reorder |= gtk_widget_get_child_visible (path_bar->up_slider_button) == FALSE;
       gtk_widget_set_child_visible (path_bar->up_slider_button, TRUE);
       gtk_widget_show_all (path_bar->up_slider_button);
     }
   else
-    gtk_widget_set_child_visible (path_bar->up_slider_button, FALSE);
+    {
+      needs_reorder |= gtk_widget_get_child_visible (path_bar->up_slider_button) == TRUE;
+      gtk_widget_set_child_visible (path_bar->up_slider_button, FALSE);
+    }
       
   if (need_sliders)
     {
@@ -704,19 +741,25 @@ gtk_path_bar_size_allocate (GtkWidget     *widget,
       
       gtk_widget_size_allocate (path_bar->down_slider_button, &child_allocation);
 
+      needs_reorder |= gtk_widget_get_child_visible (path_bar->down_slider_button) == FALSE;
       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
-    gtk_widget_set_child_visible (path_bar->down_slider_button, FALSE);
+    {
+      needs_reorder |= gtk_widget_get_child_visible (path_bar->down_slider_button) == TRUE;
+      gtk_widget_set_child_visible (path_bar->down_slider_button, FALSE);
+    }
+
+  if (needs_reorder)
+    child_ordering_changed (path_bar);
 }
 
 static void
-gtk_path_bar_style_set (GtkWidget *widget,
-                       GtkStyle  *previous_style)
+gtk_path_bar_style_updated (GtkWidget *widget)
 {
-  GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->style_set (widget, previous_style);
+  GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->style_updated (widget);
 
   gtk_path_bar_check_icon_theme (GTK_PATH_BAR (widget));
 }
@@ -749,6 +792,8 @@ gtk_path_bar_scroll (GtkWidget      *widget,
     case GDK_SCROLL_UP:
       gtk_path_bar_scroll_up (GTK_PATH_BAR (widget));
       break;
+    case GDK_SCROLL_SMOOTH:
+      break;
     }
 
   return TRUE;
@@ -757,6 +802,7 @@ gtk_path_bar_scroll (GtkWidget      *widget,
 static void
 gtk_path_bar_add (GtkContainer *container,
                  GtkWidget    *widget)
+
 {
   gtk_widget_set_parent (widget, GTK_WIDGET (container));
 }
@@ -838,6 +884,82 @@ gtk_path_bar_forall (GtkContainer *container,
     (* callback) (path_bar->down_slider_button, callback_data);
 }
 
+static GtkWidgetPath *
+gtk_path_bar_get_path_for_child (GtkContainer *container,
+                                 GtkWidget    *child)
+{
+  GtkPathBar *path_bar = GTK_PATH_BAR (container);
+  GtkWidgetPath *path;
+
+  path = _gtk_widget_create_path (GTK_WIDGET (path_bar));
+
+  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;
+}
+
 static void
 gtk_path_bar_scroll_down (GtkPathBar *path_bar)
 {
@@ -1030,7 +1152,7 @@ gtk_path_bar_slider_button_press (GtkWidget      *widget,
                                  GdkEventButton *event,
                                  GtkPathBar     *path_bar)
 {
-  if (event->type != GDK_BUTTON_PRESS || event->button != 1)
+  if (event->type != GDK_BUTTON_PRESS || event->button != GDK_BUTTON_PRIMARY)
     return FALSE;
 
   path_bar->ignore_click = FALSE;
@@ -1396,7 +1518,7 @@ get_dir_name (ButtonData *button_data)
  * or not the contents are bold
  */
 static void
-set_label_size_request (GtkWidget  *alignment,
+set_label_size_request (GtkWidget  *widget,
                        ButtonData *button_data)
 {
   const gchar *dir_name = get_dir_name (button_data);
@@ -1412,7 +1534,7 @@ set_label_size_request (GtkWidget  *alignment,
 
   pango_layout_get_pixel_size (layout, &bold_width, &bold_height);
 
-  gtk_widget_set_size_request (alignment,
+  gtk_widget_set_size_request (widget,
                               MAX (width, bold_width),
                               MAX (height, bold_height));
   g_object_unref (layout);
@@ -1472,24 +1594,23 @@ find_button_type (GtkPathBar  *path_bar,
 }
 
 static void
-button_drag_data_get_cb (GtkWidget          *widget,
-                        GdkDragContext     *context,
-                        GtkSelectionData   *selection_data,
-                        guint               info,
-                        guint               time_,
-                        gpointer            data)
+button_drag_data_get_cb (GtkWidget        *widget,
+                         GdkDragContext   *context,
+                         GtkSelectionData *selection_data,
+                         guint             info,
+                         guint             time_,
+                         gpointer          data)
 {
   ButtonData *button_data;
-  GtkPathBar *path_bar;
   char *uris[2];
 
   button_data = data;
-  path_bar = GTK_PATH_BAR (gtk_widget_get_parent (widget)); /* the button's parent *is* the path bar */
 
   uris[0] = g_file_get_uri (button_data->file);
   uris[1] = NULL;
 
   gtk_selection_data_set_uris (selection_data, uris);
+
   g_free (uris[0]);
 }
 
@@ -1502,7 +1623,6 @@ make_directory_button (GtkPathBar  *path_bar,
 {
   AtkObject *atk_obj;
   GtkWidget *child = NULL;
-  GtkWidget *label_alignment = NULL;
   ButtonData *button_data;
 
   file_is_hidden = !! file_is_hidden;
@@ -1513,6 +1633,7 @@ make_directory_button (GtkPathBar  *path_bar,
   button_data->button = gtk_toggle_button_new ();
   atk_obj = gtk_widget_get_accessible (button_data->button);
   gtk_button_set_focus_on_click (GTK_BUTTON (button_data->button), FALSE);
+  gtk_widget_add_events (button_data->button, GDK_SCROLL_MASK);
 
   switch (button_data->type)
     {
@@ -1526,19 +1647,15 @@ make_directory_button (GtkPathBar  *path_bar,
     case DESKTOP_BUTTON:
       button_data->image = gtk_image_new ();
       button_data->label = gtk_label_new (NULL);
-      label_alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
-      gtk_container_add (GTK_CONTAINER (label_alignment), button_data->label);
-      child = gtk_hbox_new (FALSE, 2);
+      child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
       gtk_box_pack_start (GTK_BOX (child), button_data->image, FALSE, FALSE, 0);
-      gtk_box_pack_start (GTK_BOX (child), label_alignment, FALSE, FALSE, 0);
+      gtk_box_pack_start (GTK_BOX (child), button_data->label, FALSE, FALSE, 0);
       break;
     case NORMAL_BUTTON:
     default:
       button_data->label = gtk_label_new (NULL);
       gtk_label_set_ellipsize (GTK_LABEL (button_data->label), PANGO_ELLIPSIZE_END);
-      label_alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
-      gtk_container_add (GTK_CONTAINER (label_alignment), button_data->label);
-      child = label_alignment;
+      child = button_data->label;
       button_data->image = NULL;
     }
 
@@ -1546,14 +1663,12 @@ make_directory_button (GtkPathBar  *path_bar,
   button_data->file = g_object_ref (file);
   button_data->file_is_hidden = file_is_hidden;
 
-  /* FIXME: Maybe we dont need this alignment at all and we can
-   * use GtkMisc aligments or even GtkWidget:halign/valign center.
-   *
+  /*
    * The following function ensures that the alignment will always
    * request the same size whether the button's text is bold or not.
    */
-  if (label_alignment)
-    set_label_size_request (label_alignment, button_data);
+  if (button_data->label)
+    set_label_size_request (button_data->label, button_data);
 
   gtk_container_add (GTK_CONTAINER (button_data->button), child);
   gtk_widget_show_all (button_data->button);
@@ -1579,8 +1694,7 @@ make_directory_button (GtkPathBar  *path_bar,
 
 static gboolean
 gtk_path_bar_check_parent_path (GtkPathBar         *path_bar,
-                               GFile              *file,
-                               GtkFileSystem      *file_system)
+                               GFile              *file)
 {
   GList *list;
   GList *current_path = NULL;
@@ -1664,6 +1778,8 @@ gtk_path_bar_set_file_finish (struct SetFileInfo *info,
          GtkWidget *button = BUTTON_DATA (l->data)->button;
          gtk_container_add (GTK_CONTAINER (info->path_bar), button);
        }
+
+      child_ordering_changed (info->path_bar);
     }
   else
     {
@@ -1684,6 +1800,7 @@ gtk_path_bar_set_file_finish (struct SetFileInfo *info,
     g_object_unref (info->file);
   if (info->parent_file)
     g_object_unref (info->parent_file);
+
   g_free (info);
 }
 
@@ -1730,17 +1847,23 @@ gtk_path_bar_get_info_callback (GCancellable *cancellable,
   if (BUTTON_IS_FAKE_ROOT (button_data))
     file_info->fake_root = file_info->new_buttons;
 
+  /* We have assigned the info for the innermost button, i.e. the deepest directory.
+   * Now, go on to fetch the info for this directory's parent.
+   */
+
   file_info->file = file_info->parent_file;
   file_info->first_directory = FALSE;
 
   if (!file_info->file)
     {
+      /* No parent?  Okay, we are done. */
       gtk_path_bar_set_file_finish (file_info, TRUE);
       return;
     }
 
   file_info->parent_file = g_file_get_parent (file_info->file);
 
+  /* Recurse asynchronously */
   file_info->path_bar->get_info_cancellable =
     _gtk_file_system_get_info (file_info->path_bar->file_system,
                               file_info->file,
@@ -1749,26 +1872,21 @@ gtk_path_bar_get_info_callback (GCancellable *cancellable,
                               file_info);
 }
 
-gboolean
-_gtk_path_bar_set_file (GtkPathBar         *path_bar,
-                       GFile              *file,
-                       const gboolean      keep_trail,
-                       GError            **error)
+void
+_gtk_path_bar_set_file (GtkPathBar      *path_bar,
+                        GFile           *file,
+                        const gboolean   keep_trail)
 {
   struct SetFileInfo *info;
-  gboolean result;
-
-  g_return_val_if_fail (GTK_IS_PATH_BAR (path_bar), FALSE);
-  g_return_val_if_fail (G_IS_FILE (file), FALSE);
 
-  result = TRUE;
+  g_return_if_fail (GTK_IS_PATH_BAR (path_bar));
+  g_return_if_fail (G_IS_FILE (file));
 
   /* 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_bar->file_system))
-    return TRUE;
+  if (keep_trail && gtk_path_bar_check_parent_path (path_bar, file))
+    return;
 
   info = g_new0 (struct SetFileInfo, 1);
   info->file = g_object_ref (file);
@@ -1781,12 +1899,10 @@ _gtk_path_bar_set_file (GtkPathBar         *path_bar,
 
   path_bar->get_info_cancellable =
     _gtk_file_system_get_info (path_bar->file_system,
-                              info->file,
-                              "standard::display-name,standard::is-hidden,standard::is-backup",
-                              gtk_path_bar_get_info_callback,
-                              info);
-
-  return TRUE;
+                               info->file,
+                               "standard::display-name,standard::is-hidden,standard::is-backup",
+                               gtk_path_bar_get_info_callback,
+                               info);
 }
 
 /* FIXME: This should be a construct-only property */