[Xfce4-commits] <xfce4-panel:devel> Backport the tray widget object, GtkFixed was not suitable for the job.

Nick Schermer nick at xfce.org
Tue Aug 11 20:30:55 CEST 2009


Updating branch refs/heads/devel
         to 21f6d5b3e90305c98bd026d715319c68c495493b (commit)
       from aa799ce3908a38df74c05e53494ddfdd2b62d9d1 (commit)

commit 21f6d5b3e90305c98bd026d715319c68c495493b
Author: Nick Schermer <nick at xfce.org>
Date:   Thu Mar 26 20:25:49 2009 +0100

    Backport the tray widget object, GtkFixed was not suitable for the job.
    
    Positioning with a GtkFixed was not suitable for positioning icons,
    because the request -> allocation part is very important here. Rename
    the object from XfceTrayWidget to SystrayBox.

 plugins/systray/Makefile.am   |    2 +
 plugins/systray/systray-box.c |  837 +++++++++++++++++++++++++++++++++++++++++
 plugins/systray/systray-box.h |   67 ++++
 plugins/systray/systray.c     |  306 +++++++---------
 4 files changed, 1036 insertions(+), 176 deletions(-)

diff --git a/plugins/systray/Makefile.am b/plugins/systray/Makefile.am
index f907bbb..4b3ac13 100644
--- a/plugins/systray/Makefile.am
+++ b/plugins/systray/Makefile.am
@@ -21,6 +21,8 @@ libsystray_la_SOURCES = \
 	$(libsystray_built_sources) \
 	systray.c \
 	systray.h \
+	systray-box.c \
+	systray-box.h \
 	systray-manager.c \
 	systray-manager.h
 
diff --git a/plugins/systray/systray-box.c b/plugins/systray/systray-box.c
new file mode 100644
index 0000000..f54bd8d
--- /dev/null
+++ b/plugins/systray/systray-box.c
@@ -0,0 +1,837 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2007-2009 Nick Schermer <nick at xfce.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <libxfce4panel/libxfce4panel.h>
+#include <common/panel-private.h>
+
+#include "systray-box.h"
+
+#define BUTTON_SIZE        (16)
+#define SPACING            (2)
+#define OFFSCREEN          (-9999)
+#define IS_HORIZONTAL(box) ((box)->arrow_type == GTK_ARROW_LEFT \
+                            || (box)->arrow_type == GTK_ARROW_RIGHT)
+
+
+
+static void     systray_box_finalize           (GObject         *object);
+static void     systray_box_size_request       (GtkWidget       *widget,
+                                                GtkRequisition  *requisition);
+static void     systray_box_size_allocate      (GtkWidget       *widget,
+                                                GtkAllocation   *allocation);
+static void     systray_box_add                (GtkContainer    *container,
+                                                GtkWidget       *child);
+static void     systray_box_remove             (GtkContainer    *container,
+                                                GtkWidget       *child);
+static void     systray_box_forall             (GtkContainer    *container,
+                                                gboolean         include_internals,
+                                                GtkCallback      callback,
+                                                gpointer         callback_data);
+static GType    systray_box_child_type         (GtkContainer    *container);
+static void     systray_box_button_set_arrow   (SystrayBox      *box);
+static gboolean systray_box_button_press_event (GtkWidget       *widget,
+                                                GdkEventButton  *event,
+                                                GtkWidget       *box);
+static void     systray_box_button_clicked     (GtkToggleButton *button,
+                                                SystrayBox      *box);
+
+
+
+struct _SystrayBoxClass
+{
+  GtkContainerClass __parent__;
+};
+
+struct _SystrayBox
+{
+  GtkContainer  __parent__;
+
+  /* all the icons packed in this box */
+  GSList       *childeren;
+
+  /* table with names, value contains an uint
+   * that represents the hidden bool */
+  GHashTable   *names;
+
+  /* expand button */
+  GtkWidget    *button;
+
+  /* position of the arrow button */
+  GtkArrowType  arrow_type;
+
+  /* hidden childeren counter */
+  gint          n_hidden_childeren;
+
+  /* whether hidden icons are visible */
+  guint         show_hidden : 1;
+
+  /* number of rows */
+  gint          rows;
+};
+
+struct _SystrayBoxChild
+{
+  /* the child widget */
+  GtkWidget    *widget;
+
+  /* whether it could be hidden */
+  guint         hidden : 1;
+
+  /* whether the icon is invisible */
+  guint         invisible : 1;
+
+  /* the name of the applcation */
+  gchar        *name;
+};
+
+
+
+XFCE_PANEL_DEFINE_TYPE (SystrayBox, systray_box, GTK_TYPE_CONTAINER)
+
+
+
+static void
+systray_box_class_init (SystrayBoxClass *klass)
+{
+  GObjectClass      *gobject_class;
+  GtkWidgetClass    *gtkwidget_class;
+  GtkContainerClass *gtkcontainer_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = systray_box_finalize;
+
+  gtkwidget_class = GTK_WIDGET_CLASS (klass);
+  gtkwidget_class->size_request = systray_box_size_request;
+  gtkwidget_class->size_allocate = systray_box_size_allocate;
+
+  gtkcontainer_class = GTK_CONTAINER_CLASS (klass);
+  gtkcontainer_class->add = systray_box_add;
+  gtkcontainer_class->remove = systray_box_remove;
+  gtkcontainer_class->forall = systray_box_forall;
+  gtkcontainer_class->child_type = systray_box_child_type;
+}
+
+
+
+static void
+systray_box_init (SystrayBox *box)
+{
+  /* initialize the widget */
+  GTK_WIDGET_SET_FLAGS (box, GTK_NO_WINDOW);
+
+  /* initialize */
+  box->childeren = NULL;
+  box->button = NULL;
+  box->rows = 1;
+  box->n_hidden_childeren = 0;
+  box->arrow_type = GTK_ARROW_LEFT;
+  box->show_hidden = FALSE;
+
+  /* create hash table */
+  box->names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+  /* create arrow button */
+  box->button = xfce_arrow_button_new (box->arrow_type);
+  GTK_WIDGET_UNSET_FLAGS (box->button, GTK_CAN_DEFAULT | GTK_CAN_FOCUS);
+  gtk_button_set_focus_on_click (GTK_BUTTON (box->button), FALSE);
+  g_signal_connect (G_OBJECT (box->button), "clicked",
+      G_CALLBACK (systray_box_button_clicked), box);
+  g_signal_connect (G_OBJECT (box->button), "button-press-event",
+      G_CALLBACK (systray_box_button_press_event), box);
+  gtk_widget_set_parent (box->button, GTK_WIDGET (box));
+}
+
+
+
+static void
+systray_box_finalize (GObject *object)
+{
+  SystrayBox *box = XFCE_SYSTRAY_BOX (object);
+
+  /* check if we're leaking */
+  if (G_UNLIKELY (box->childeren != NULL))
+    {
+      /* free the child list */
+      g_slist_free (box->childeren);
+      g_debug ("Leaking memory, not all children have been removed");
+    }
+
+  /* destroy the hash table */
+  g_hash_table_destroy (box->names);
+
+  G_OBJECT_CLASS (systray_box_parent_class)->finalize (object);
+}
+
+
+
+static void
+systray_box_size_request (GtkWidget      *widget,
+                          GtkRequisition *requisition)
+{
+  SystrayBox      *box = XFCE_SYSTRAY_BOX (widget);
+  GSList          *li;
+  SystrayBoxChild *child_info;
+  gint             n_columns;
+  gint             child_size = -1;
+  GtkRequisition   child_req;
+  gint             n_visible_childeren = 0;
+  gint             swap;
+
+  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (widget));
+  panel_return_if_fail (requisition != NULL);
+
+  /* check if we need to hide or show any childeren */
+  for (li = box->childeren; li != NULL; li = li->next)
+    {
+      child_info = li->data;
+
+      /* get the icons size request */
+      gtk_widget_size_request (child_info->widget, &child_req);
+
+      if (G_UNLIKELY (child_req.width == 1 || child_req.height == 1))
+        {
+          /* icons that return a 1 by 1 requisition supposed to be hidden */
+          if (child_info->invisible == FALSE)
+            {
+              /* this icon should not be visible */
+              child_info->invisible = TRUE;
+
+              /* decrease the hidden counter if needed */
+              if (child_info->hidden)
+                box->n_hidden_childeren--;
+            }
+        }
+      else
+        {
+          /* restore icon if it was previously invisible */
+          if (G_UNLIKELY (child_info->invisible))
+            {
+              /* visible icon */
+              child_info->invisible = FALSE;
+
+              /* update counter */
+              if (child_info->hidden)
+                box->n_hidden_childeren++;
+            }
+
+          /* count the number of visible childeren */
+          if (child_info->hidden == FALSE || box->show_hidden == TRUE)
+            {
+              /* pick largest icon */
+              if (child_size == -1)
+                child_size = MAX (child_req.width, child_req.height);
+              else
+                child_size = MAX (child_size, MAX (child_req.width, child_req.height));
+
+              /* increase number of visible childeren */
+              n_visible_childeren++;
+            }
+        }
+    }
+
+  /* number of columns */
+  n_columns = n_visible_childeren / box->rows;
+  if (n_visible_childeren > (n_columns * box->rows))
+    n_columns++;
+
+  /* set the width and height needed for the icons */
+  if (n_visible_childeren > 0)
+    {
+      requisition->width = ((child_size + SPACING) * n_columns) - SPACING;
+      requisition->height = ((child_size + SPACING) * box->rows) - SPACING;
+    }
+  else
+    {
+      requisition->width = requisition->height = 0;
+    }
+
+  /* add the button size if there are hidden icons */
+  if (box->n_hidden_childeren > 0)
+    {
+      /* add the button size */
+      requisition->width += BUTTON_SIZE;
+
+      /* add space */
+      if (n_visible_childeren > 0)
+        requisition->width += SPACING;
+    }
+
+  /* swap the sizes if the orientation is vertical */
+  if (!IS_HORIZONTAL (box))
+    {
+      swap = requisition->width;
+      requisition->width = requisition->height;
+      requisition->height = swap;
+    }
+
+  /* add container border */
+  requisition->width += GTK_CONTAINER (widget)->border_width * 2;
+  requisition->height += GTK_CONTAINER (widget)->border_width * 2;
+}
+
+
+
+static void
+systray_box_size_allocate (GtkWidget     *widget,
+                           GtkAllocation *allocation)
+{
+  SystrayBox      *box = XFCE_SYSTRAY_BOX (widget);
+  SystrayBoxChild *child_info;
+  GSList          *li;
+  gint             n;
+  gint             x, y;
+  gint             width, height;
+  gint             offset = 0;
+  gint             child_size;
+  GtkAllocation    child_allocation;
+  gint             swap;
+
+  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (widget));
+  panel_return_if_fail (allocation != NULL);
+
+  /* set widget allocation */
+  widget->allocation = *allocation;
+
+  /* get root coordinates */
+  x = allocation->x + GTK_CONTAINER (widget)->border_width;
+  y = allocation->y + GTK_CONTAINER (widget)->border_width;
+
+  /* get real size */
+  width = allocation->width - 2 * GTK_CONTAINER (widget)->border_width;
+  height = allocation->height - 2 * GTK_CONTAINER (widget)->border_width;
+
+  /* child size */
+  child_size = IS_HORIZONTAL (box) ? height : width;
+  child_size -= SPACING * (box->rows - 1);
+  child_size /= box->rows;
+
+  /* don't allocate zero width icon */
+  if (child_size < 1)
+    child_size = 1;
+
+  /* position arrow button */
+  if (box->n_hidden_childeren > 0)
+    {
+      /* initialize allocation */
+      child_allocation.x = x;
+      child_allocation.y = y;
+
+      /* set the width and height */
+      if (IS_HORIZONTAL (box))
+        {
+          child_allocation.width = BUTTON_SIZE;
+          child_allocation.height = height;
+        }
+      else
+        {
+          child_allocation.width = width;
+          child_allocation.height = BUTTON_SIZE;
+        }
+
+      /* position the button on the other side of the box */
+      if (box->arrow_type == GTK_ARROW_RIGHT)
+        child_allocation.x += width - child_allocation.width;
+      else if (box->arrow_type == GTK_ARROW_DOWN)
+        child_allocation.y += height - child_allocation.height;
+
+      /* set the offset for the icons */
+      offset = BUTTON_SIZE + SPACING;
+
+      /* position the arrow button */
+      gtk_widget_size_allocate (box->button, &child_allocation);
+
+      /* show button if not already visible */
+      if (!GTK_WIDGET_VISIBLE (box->button))
+        gtk_widget_show (box->button);
+    }
+  else if (GTK_WIDGET_VISIBLE (box->button))
+    {
+      /* hide the button */
+      gtk_widget_hide (box->button);
+    }
+
+  /* position icons */
+  for (li = box->childeren, n = 0; li != NULL; li = li->next)
+    {
+      child_info = li->data;
+
+      if (child_info->invisible || (child_info->hidden && !box->show_hidden))
+        {
+          /* put icons offscreen */
+          child_allocation.x = child_allocation.y = OFFSCREEN;
+        }
+      else
+        {
+          /* set coordinates */
+          child_allocation.x = (child_size + SPACING) * (n / box->rows) + offset;
+          child_allocation.y = (child_size + SPACING) * (n % box->rows);
+
+          /* increase item counter */
+          n++;
+
+          /* swap coordinates on a vertical panel */
+          if (!IS_HORIZONTAL (box))
+            {
+              swap = child_allocation.x;
+              child_allocation.x = child_allocation.y;
+              child_allocation.y = swap;
+            }
+
+          /* invert the icon order if the arrow button position is right or down */
+          if (box->arrow_type == GTK_ARROW_RIGHT)
+            child_allocation.x = width - child_allocation.x - child_size;
+          else if (box->arrow_type == GTK_ARROW_DOWN)
+            child_allocation.y = height - child_allocation.y - child_size;
+
+          /* add root */
+          child_allocation.x += x;
+          child_allocation.y += y;
+        }
+
+      /* set child width and height */
+      child_allocation.width = child_size;
+      child_allocation.height = child_size;
+
+      /* allocate widget size */
+      gtk_widget_size_allocate (child_info->widget, &child_allocation);
+  }
+}
+
+
+
+static void
+systray_box_add (GtkContainer *container,
+                 GtkWidget    *child)
+{
+  SystrayBox *box = XFCE_SYSTRAY_BOX (container);
+
+  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
+
+  /* add the entry */
+  systray_box_add_with_name (box, child, NULL);
+}
+
+
+
+static void
+systray_box_remove (GtkContainer *container,
+                    GtkWidget    *child)
+{
+  SystrayBox      *box = XFCE_SYSTRAY_BOX (container);
+  SystrayBoxChild *child_info;
+  gboolean         need_resize;
+  GSList          *li;
+
+  /* search the child */
+  for (li = box->childeren; li != NULL; li = li->next)
+    {
+      child_info = li->data;
+
+      if (child_info->widget == child)
+        {
+          /* whether the need to redraw afterwards */
+          need_resize = !child_info->hidden;
+
+          /* update hidden counter */
+          if (child_info->hidden && !child_info->invisible)
+            box->n_hidden_childeren--;
+
+          /* remove from list */
+          box->childeren = g_slist_remove_link (box->childeren, li);
+
+          /* free name */
+          g_free (child_info->name);
+
+          /* free child info */
+          g_slice_free (SystrayBoxChild, child_info);
+
+          /* unparent the widget */
+          gtk_widget_unparent (child);
+
+          /* resize when the child was visible */
+          if (need_resize)
+            gtk_widget_queue_resize (GTK_WIDGET (container));
+
+          return;
+        }
+    }
+}
+
+
+
+static void
+systray_box_forall (GtkContainer *container,
+                    gboolean      include_internals,
+                    GtkCallback   callback,
+                    gpointer      callback_data)
+{
+  SystrayBox      *box = XFCE_SYSTRAY_BOX (container);
+  SystrayBoxChild *child_info;
+  GSList          *li;
+
+  /* for button */
+  (*callback) (GTK_WIDGET (box->button), callback_data);
+
+  /* run callback for all childeren */
+  for (li = box->childeren; li != NULL; li = li->next)
+    {
+      child_info = li->data;
+
+      (*callback) (GTK_WIDGET (child_info->widget), callback_data);
+    }
+}
+
+
+
+static GType
+systray_box_child_type (GtkContainer *container)
+
+{
+  return GTK_TYPE_WIDGET;
+}
+
+
+
+static void
+systray_box_button_set_arrow (SystrayBox *box)
+{
+  GtkArrowType arrow_type;
+
+  /* set arrow type */
+  arrow_type = box->arrow_type;
+
+  /* invert the arrow direction when the button is toggled */
+  if (box->show_hidden)
+    {
+      if (IS_HORIZONTAL (box))
+        arrow_type = (arrow_type == GTK_ARROW_LEFT ? GTK_ARROW_RIGHT : GTK_ARROW_LEFT);
+      else
+        arrow_type = (arrow_type == GTK_ARROW_UP ? GTK_ARROW_DOWN : GTK_ARROW_UP);
+    }
+
+  /* set the arrow type */
+  xfce_arrow_button_set_arrow_type (XFCE_ARROW_BUTTON (box->button), arrow_type);
+}
+
+
+
+static gboolean
+systray_box_button_press_event (GtkWidget      *widget,
+                                GdkEventButton *event,
+                                GtkWidget      *box)
+{
+  /* send the event to the box for the panel menu */
+  gtk_widget_event (box, (GdkEvent *) event);
+
+  return FALSE;
+}
+
+
+
+static void
+systray_box_button_clicked (GtkToggleButton *button,
+                            SystrayBox      *box)
+{
+  /* whether to show hidden icons */
+  box->show_hidden = gtk_toggle_button_get_active (button);
+
+  /* update the arrow */
+  systray_box_button_set_arrow (box);
+
+  /* queue a resize */
+  gtk_widget_queue_resize (GTK_WIDGET (box));
+}
+
+
+
+static gint
+systray_box_compare_function (gconstpointer a,
+                                   gconstpointer b)
+{
+  const SystrayBoxChild *child_a = a;
+  const SystrayBoxChild *child_b = b;
+
+  /* sort hidden icons before visible ones */
+  if (child_a->hidden != child_b->hidden)
+    return (child_a->hidden ? -1 : 1);
+
+  /* put icons without name after the hidden icons */
+  if (!IS_STRING (child_a->name) || !IS_STRING (child_b->name))
+    {
+      if (IS_STRING (child_a->name) == IS_STRING (child_b->name))
+        return 0;
+      else
+        return !IS_STRING (child_a->name) ? -1 : 1;
+    }
+
+  /* sort by name */
+  return strcmp (child_a->name, child_b->name);
+}
+
+
+
+GtkWidget *
+systray_box_new (void)
+{
+  return g_object_new (XFCE_TYPE_SYSTRAY_BOX, NULL);
+}
+
+
+
+void
+systray_box_set_arrow_type (SystrayBox   *box,
+                            GtkArrowType  arrow_type)
+{
+  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
+
+  if (G_LIKELY (arrow_type != box->arrow_type))
+    {
+      /* set new setting */
+      box->arrow_type = arrow_type;
+
+      /* update button arrow */
+      systray_box_button_set_arrow (box);
+
+      /* queue a resize */
+      if (box->childeren != NULL)
+        gtk_widget_queue_resize (GTK_WIDGET (box));
+    }
+}
+
+
+
+void
+systray_box_set_rows (SystrayBox *box,
+                      gint        rows)
+{
+  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
+
+  if (G_LIKELY (rows != box->rows))
+    {
+      /* set new setting */
+      box->rows = MAX (1, rows);
+
+      /* queue a resize */
+      if (box->childeren != NULL)
+        gtk_widget_queue_resize (GTK_WIDGET (box));
+    }
+}
+
+
+
+gint
+systray_box_get_rows (SystrayBox *box)
+{
+  panel_return_val_if_fail (XFCE_IS_SYSTRAY_BOX (box), 1);
+
+  return box->rows;
+}
+
+
+
+void
+systray_box_add_with_name (SystrayBox  *box,
+                           GtkWidget   *child,
+                           const gchar *name)
+{
+  SystrayBoxChild *child_info;
+
+  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
+  panel_return_if_fail (GTK_IS_WIDGET (child));
+  panel_return_if_fail (child->parent == NULL);
+  panel_return_if_fail (name == NULL || g_utf8_validate (name, -1, NULL));
+
+  /* create child info */
+  child_info = g_slice_new (SystrayBoxChild);
+  child_info->widget = child;
+  child_info->invisible = FALSE;
+  child_info->name = g_strdup (name);
+  child_info->hidden = systray_box_name_get_hidden (box, child_info->name);
+
+  /* update hidden counter */
+  if (child_info->hidden)
+      box->n_hidden_childeren++;
+
+  /* insert sorted */
+  box->childeren = g_slist_insert_sorted (box->childeren, child_info,
+                                          systray_box_compare_function);
+
+  /* set parent widget */
+  gtk_widget_set_parent (child, GTK_WIDGET (box));
+}
+
+
+
+void
+systray_box_name_add (SystrayBox  *box,
+                      const gchar *name,
+                      gboolean     hidden)
+{
+  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
+  panel_return_if_fail (IS_STRING (name));
+
+  /* insert the application */
+  g_hash_table_insert (box->names, g_strdup (name),
+                       GUINT_TO_POINTER (hidden ? 1 : 0));
+}
+
+
+
+void
+systray_box_name_set_hidden (SystrayBox  *box,
+                             const gchar *name,
+                             gboolean     hidden)
+{
+  SystrayBoxChild *child_info;
+  GSList          *li;
+  gint             n_hidden_childeren;
+
+  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
+  panel_return_if_fail (IS_STRING (name));
+
+  /* replace the old name */
+  g_hash_table_replace (box->names, g_strdup (name),
+      GUINT_TO_POINTER (hidden ? 1 : 0));
+
+  /* reset counter */
+  n_hidden_childeren = 0;
+
+  /* update the icons */
+  for (li = box->childeren; li != NULL; li = li->next)
+    {
+      child_info = li->data;
+
+      /* update the hidden state */
+      child_info->hidden = systray_box_name_get_hidden (box, child_info->name);
+
+      /* increase counter if needed */
+      if (child_info->hidden && !child_info->invisible)
+        n_hidden_childeren++;
+    }
+
+  if (box->n_hidden_childeren != n_hidden_childeren)
+    {
+      /* set value */
+      box->n_hidden_childeren = n_hidden_childeren;
+
+      /* sort the list again */
+      box->childeren = g_slist_sort (box->childeren,
+          systray_box_compare_function);
+
+      /* update the box */
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+    }
+}
+
+
+
+gboolean
+systray_box_name_get_hidden (SystrayBox  *box,
+                             const gchar *name)
+{
+  gpointer p;
+
+  /* do not hide icons without name */
+  if (G_UNLIKELY (name == NULL))
+    return FALSE;
+
+  /* lookup the name in the table */
+  p = g_hash_table_lookup (box->names, name);
+
+  /* check the pointer */
+  if (G_UNLIKELY (p == NULL))
+    {
+      /* add the name */
+      systray_box_name_add (box, name, FALSE);
+
+      /* do not hide the icon */
+      return FALSE;
+    }
+  else
+    {
+      return (GPOINTER_TO_UINT (p) == 1 ? TRUE : FALSE);
+    }
+}
+
+
+
+GList *
+systray_box_name_list (SystrayBox *box)
+{
+  GList *keys;
+
+  /* get the hash table keys */
+  keys = g_hash_table_get_keys (box->names);
+
+  /* sort the list */
+  keys = g_list_sort (keys, (GCompareFunc) strcmp);
+
+  return keys;
+}
+
+
+
+void
+systray_box_name_clear (SystrayBox *box)
+{
+  SystrayBoxChild *child_info;
+  GSList          *li;
+  gint             n_hidden_childeren = 0;
+
+  /* remove all the entries from the list */
+  g_hash_table_remove_all (box->names);
+
+  /* remove hidden flags from all childeren */
+  for (li = box->childeren; li != NULL; li = li->next)
+    {
+      child_info = li->data;
+
+      /* update the hidden state */
+      if (child_info->hidden)
+        {
+          n_hidden_childeren++;
+
+          child_info->hidden = FALSE;
+        }
+    }
+
+  /* reset */
+  box->n_hidden_childeren = 0;
+
+  /* update box if needed */
+  if (n_hidden_childeren > 0)
+    {
+      /* sort the list again */
+      box->childeren = g_slist_sort (box->childeren,
+          systray_box_compare_function);
+
+      /* update the box */
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+    }
+}
+
diff --git a/plugins/systray/systray-box.h b/plugins/systray/systray-box.h
new file mode 100644
index 0000000..a153173
--- /dev/null
+++ b/plugins/systray/systray-box.h
@@ -0,0 +1,67 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2007-2009 Nick Schermer <nick at xfce.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __SYSTRAY_BOX_H__
+#define __SYSTRAY_BOX_H__
+
+typedef struct _SystrayBoxClass SystrayBoxClass;
+typedef struct _SystrayBox      SystrayBox;
+typedef struct _SystrayBoxChild SystrayBoxChild;
+
+#define XFCE_TYPE_SYSTRAY_BOX            (systray_box_get_type ())
+#define XFCE_SYSTRAY_BOX(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), XFCE_TYPE_SYSTRAY_BOX, SystrayBox))
+#define XFCE_SYSTRAY_BOX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), XFCE_TYPE_SYSTRAY_BOX, SystrayBoxClass))
+#define XFCE_IS_SYSTRAY_BOX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XFCE_TYPE_SYSTRAY_BOX))
+#define XFCE_IS_SYSTRAY_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XFCE_TYPE_SYSTRAY_BOX))
+#define XFCE_SYSTRAY_BOX_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), XFCE_TYPE_SYSTRAY_BOX, SystrayBoxClass))
+
+GType      systray_box_get_type        (void) G_GNUC_CONST;
+
+void       systray_box_register_type   (GTypeModule  *module);
+
+GtkWidget *systray_box_new             (void) G_GNUC_MALLOC;
+
+void       systray_box_set_arrow_type  (SystrayBox   *tray,
+                                        GtkArrowType  arrow_type);
+
+void       systray_box_set_rows        (SystrayBox   *tray,
+                                        gint          rows);
+
+gint       systray_box_get_rows        (SystrayBox   *tray);
+
+void       systray_box_add_with_name   (SystrayBox   *box,
+                                        GtkWidget    *child,
+                                        const gchar  *name);
+
+void       systray_box_name_add        (SystrayBox   *box,
+                                        const gchar  *name,
+                                        gboolean      hidden);
+
+void       systray_box_name_set_hidden (SystrayBox   *box,
+                                        const gchar  *name,
+                                        gboolean      hidden);
+
+gboolean   systray_box_name_get_hidden (SystrayBox   *box,
+                                        const gchar  *name);
+
+GList     *systray_box_name_list       (SystrayBox   *box);
+
+void       systray_box_name_clear      (SystrayBox   *box);
+
+#endif /* !__SYSTRAY_BOX_H__ */
diff --git a/plugins/systray/systray.c b/plugins/systray/systray.c
index 9368dd6..0463608 100644
--- a/plugins/systray/systray.c
+++ b/plugins/systray/systray.c
@@ -29,25 +29,37 @@
 #include <exo/exo.h>
 
 #include "systray.h"
+#include "systray-box.h"
 #include "systray-manager.h"
 #include "systray-dialog_glade.h"
 
 
 
-static void     systray_plugin_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
-static void     systray_plugin_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
-static void     systray_plugin_construct (XfcePanelPlugin *panel_plugin);
-static void     systray_plugin_free_data (XfcePanelPlugin *panel_plugin);
-static void     systray_plugin_screen_position_changed (XfcePanelPlugin *panel_plugin, gint screen_position);
-static void     systray_plugin_orientation_changed (XfcePanelPlugin *panel_plugin, GtkOrientation   orientation);
-static gboolean systray_plugin_size_changed (XfcePanelPlugin *panel_plugin, gint size);
-static void     systray_plugin_configure_plugin (XfcePanelPlugin *panel_plugin);
-
-static void systray_plugin_reallocate (SystrayPlugin *plugin);
-
-static void systray_plugin_icon_added (SystrayManager *manager,  GtkWidget *icon, SystrayPlugin *plugin);
-static void systray_plugin_icon_removed (SystrayManager *manager, GtkWidget *icon, SystrayPlugin *plugin);
-static void systray_plugin_lost_selection (SystrayManager *manager,  SystrayPlugin  *plugin);
+static void     systray_plugin_get_property            (GObject         *object,
+                                                        guint            prop_id,
+                                                        GValue          *value,
+                                                        GParamSpec      *pspec);
+static void     systray_plugin_set_property            (GObject         *object,
+                                                        guint            prop_id,
+                                                        const GValue    *value,
+                                                        GParamSpec      *pspec);
+static void     systray_plugin_construct               (XfcePanelPlugin *panel_plugin);
+static void     systray_plugin_free_data               (XfcePanelPlugin *panel_plugin);
+static void     systray_plugin_screen_position_changed (XfcePanelPlugin *panel_plugin,
+                                                        gint             screen_position);
+static void     systray_plugin_orientation_changed     (XfcePanelPlugin *panel_plugin,
+                                                        GtkOrientation   orientation);
+static gboolean systray_plugin_size_changed            (XfcePanelPlugin *panel_plugin,
+                                                        gint size);
+static void     systray_plugin_configure_plugin        (XfcePanelPlugin *panel_plugin);
+static void     systray_plugin_icon_added              (SystrayManager  *manager,
+                                                        GtkWidget       *icon,
+                                                        SystrayPlugin   *plugin);
+static void     systray_plugin_icon_removed            (SystrayManager  *manager,
+                                                        GtkWidget       *icon,
+                                                        SystrayPlugin   *plugin);
+static void     systray_plugin_lost_selection          (SystrayManager  *manager,
+                                                        SystrayPlugin   *plugin);
 
 
 
@@ -68,37 +80,12 @@ struct _SystrayPlugin
 
   /* widgets */
   GtkWidget      *frame;
-  GtkWidget      *fixed;
-  GtkWidget      *button;
-
-  GSList         *children;
-
-  guint           show_hidden : 1;
+  GtkWidget      *box;
 
   /* settings */
-  guint           rows;
   guint           show_frame : 1;
 };
 
-enum _SystrayChildState
-{
-  CHILD_VISIBLE,   /* always visible */
-  CHILD_AUTO_HIDE, /* hidden when tray is collapsed */
-  CHILD_DISABLED   /* never show this icon */
-};
-
-struct _SystrayChild
-{
-  /* the status icon */
-  GtkWidget         *icon;
-
-  /* application name */
-  gchar             *name;
-
-  /* child state */
-  SystrayChildState  state;
-};
-
 enum
 {
   PROP_0,
@@ -110,6 +97,7 @@ enum
 
 /* define the plugin */
 XFCE_PANEL_DEFINE_PLUGIN (SystrayPlugin, systray_plugin,
+    systray_box_register_type,
     systray_manager_register_type)
 
 
@@ -134,10 +122,10 @@ systray_plugin_class_init (SystrayPluginClass *klass)
 
   g_object_class_install_property (gobject_class,
                                    PROP_ROWS,
-                                   g_param_spec_uint ("rows",
-                                                      NULL, NULL,
-                                                      1, 10, 1,
-                                                      EXO_PARAM_READWRITE));
+                                   g_param_spec_int ("rows",
+                                                     NULL, NULL,
+                                                     1, 10, 1,
+                                                     EXO_PARAM_READWRITE));
 
   g_object_class_install_property (gobject_class,
                                    PROP_SHOW_FRAME,
@@ -153,7 +141,6 @@ static void
 systray_plugin_init (SystrayPlugin *plugin)
 {
   plugin->manager = NULL;
-  plugin->rows = 1;
   plugin->show_frame = FALSE;
 
   /* initialize xfconf */
@@ -168,14 +155,10 @@ systray_plugin_init (SystrayPlugin *plugin)
   gtk_frame_set_shadow_type (GTK_FRAME (plugin->frame), GTK_SHADOW_NONE);
   gtk_widget_show (plugin->frame);
 
-  plugin->fixed = gtk_fixed_new ();
-  gtk_container_add (GTK_CONTAINER (plugin->frame), plugin->fixed);
-  xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), plugin->fixed);
-  gtk_widget_show (plugin->fixed);
-
-  plugin->button = xfce_arrow_button_new (GTK_ARROW_NONE);
-  gtk_container_add (GTK_CONTAINER (plugin->fixed), plugin->button);
-  xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), plugin->button);
+  plugin->box = systray_box_new ();
+  gtk_container_add (GTK_CONTAINER (plugin->frame), plugin->box);
+  xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), plugin->box);
+  gtk_widget_show (plugin->box);
 }
 
 
@@ -187,11 +170,13 @@ systray_plugin_get_property (GObject    *object,
                              GParamSpec *pspec)
 {
   SystrayPlugin *plugin = XFCE_SYSTRAY_PLUGIN (object);
+  gint           rows;
 
   switch (prop_id)
     {
       case PROP_ROWS:
-        g_value_set_uint (value, plugin->rows);
+        rows = systray_box_get_rows (XFCE_SYSTRAY_BOX (plugin->box));
+        g_value_set_int (value, rows);
         break;
 
       case PROP_SHOW_FRAME:
@@ -213,12 +198,13 @@ systray_plugin_set_property (GObject      *object,
                              GParamSpec   *pspec)
 {
   SystrayPlugin *plugin = XFCE_SYSTRAY_PLUGIN (object);
+  gint           rows;
 
   switch (prop_id)
     {
       case PROP_ROWS:
-        plugin->rows = g_value_get_uint (value);
-        systray_plugin_reallocate (plugin);
+        rows = g_value_get_int (value);
+        systray_box_set_rows (XFCE_SYSTRAY_BOX (plugin->box), rows);
         break;
 
       case PROP_SHOW_FRAME:
@@ -270,7 +256,13 @@ systray_plugin_screen_changed (GtkWidget *widget,
       g_signal_connect (G_OBJECT (plugin->manager), "lost-selection",
           G_CALLBACK (systray_plugin_lost_selection), plugin);
 
-      if (!systray_manager_register (plugin->manager, screen, &error))
+      if (systray_manager_register (plugin->manager, screen, &error))
+        {
+          /* send the plugin orientation */
+          systray_plugin_orientation_changed (XFCE_PANEL_PLUGIN (plugin),
+              xfce_panel_plugin_get_orientation (XFCE_PANEL_PLUGIN (plugin)));
+        }
+      else
         {
           /* TODO handle error and leave the plugin */
           g_message ("Failed to register the systray manager %s", error->message);
@@ -337,7 +329,71 @@ static void
 systray_plugin_screen_position_changed (XfcePanelPlugin *panel_plugin,
                                         gint             screen_position)
 {
+  SystrayPlugin      *plugin = XFCE_SYSTRAY_PLUGIN (panel_plugin);
+  XfceScreenPosition  position;
+  GdkScreen          *screen;
+  GdkRectangle        geom;
+  gint                mon, x, y;
+  GtkArrowType        arrow_type;
+
+  panel_return_if_fail (GTK_WIDGET_REALIZED (panel_plugin));
 
+  /* get the plugin position */
+  position = xfce_panel_plugin_get_screen_position (panel_plugin);
+
+  /* get the button position */
+  switch (position)
+    {
+      /*    horizontal west */
+      case XFCE_SCREEN_POSITION_NW_H:
+      case XFCE_SCREEN_POSITION_SW_H:
+        arrow_type = GTK_ARROW_RIGHT;
+        break;
+
+      /* horizontal east */
+      case XFCE_SCREEN_POSITION_N:
+      case XFCE_SCREEN_POSITION_NE_H:
+      case XFCE_SCREEN_POSITION_S:
+      case XFCE_SCREEN_POSITION_SE_H:
+        arrow_type = GTK_ARROW_LEFT;
+        break;
+
+      /* vertical north */
+      case XFCE_SCREEN_POSITION_NW_V:
+      case XFCE_SCREEN_POSITION_NE_V:
+        arrow_type = GTK_ARROW_DOWN;
+        break;
+
+      /* vertical south */
+      case XFCE_SCREEN_POSITION_W:
+      case XFCE_SCREEN_POSITION_SW_V:
+      case XFCE_SCREEN_POSITION_E:
+      case XFCE_SCREEN_POSITION_SE_V:
+        arrow_type = GTK_ARROW_UP;
+        break;
+
+      /* floating */
+      default:
+        /* get the screen information */
+        screen = gtk_widget_get_screen (GTK_WIDGET (panel_plugin));
+        mon = gdk_screen_get_monitor_at_window (screen, GTK_WIDGET (panel_plugin)->window);
+        gdk_screen_get_monitor_geometry (screen, mon, &geom);
+        gdk_window_get_root_origin (GTK_WIDGET (panel_plugin)->window, &x, &y);
+
+        /* get the position based on the screen position */
+        if (position == XFCE_SCREEN_POSITION_FLOATING_H)
+            arrow_type = ((x < (geom.x + geom.width / 2)) ? GTK_ARROW_RIGHT : GTK_ARROW_LEFT);
+        else
+            arrow_type = ((y < (geom.y + geom.height / 2)) ? GTK_ARROW_DOWN : GTK_ARROW_UP);
+        break;
+    }
+
+  /* set the arrow type of the tray widget */
+  systray_box_set_arrow_type (XFCE_SYSTRAY_BOX (plugin->box), arrow_type);
+
+  /* update the manager orientation */
+  systray_plugin_orientation_changed (panel_plugin,
+      xfce_panel_plugin_get_orientation (panel_plugin));
 }
 
 
@@ -359,17 +415,12 @@ static gboolean
 systray_plugin_size_changed (XfcePanelPlugin *panel_plugin,
                              gint             size)
 {
-  panel_return_val_if_fail (XFCE_IS_SYSTRAY_PLUGIN (panel_plugin), FALSE);
-  
-  if (xfce_panel_plugin_get_orientation (panel_plugin)
-      == GTK_ORIENTATION_HORIZONTAL)
-    gtk_widget_set_size_request (GTK_WIDGET (panel_plugin), -1, size);
-  else
-    gtk_widget_set_size_request (GTK_WIDGET (panel_plugin), size, -1);
-  
-  /* reallocate all the children */
-  systray_plugin_reallocate (XFCE_SYSTRAY_PLUGIN (panel_plugin));
-  
+  SystrayPlugin *plugin = XFCE_SYSTRAY_PLUGIN (panel_plugin);
+
+  /* set border sizes */
+  gtk_container_set_border_width (GTK_CONTAINER (plugin->frame),
+    (size > 26 && plugin->show_frame) ? 1 : 0);
+
   return TRUE;
 }
 
@@ -425,98 +476,28 @@ systray_plugin_configure_plugin (XfcePanelPlugin *panel_plugin)
 
 
 static void
-systray_plugin_reallocate (SystrayPlugin *plugin)
-{
-  GSList       *li;
-  SystrayChild *child;
-  guint         n;
-  gint          x, y;
-  gint          size;
-  
-  panel_return_if_fail (XFCE_IS_SYSTRAY_PLUGIN (plugin));
-  
-  /* get the icon size from the last allocation of the fixed widget */
-  size = xfce_panel_plugin_get_size (XFCE_PANEL_PLUGIN (plugin));
-  size = (size - 4) / plugin->rows;
-  if (size < 1)
-    size = 1;
-
-  for (li = plugin->children, n = 0; li != NULL; li = li->next)
-    {
-      child = li->data;
-
-      /* set the size request of the widget */
-      x = size * (n / plugin->rows);
-      y = size * (n % plugin->rows);
-
-      gtk_fixed_move (GTK_FIXED (plugin->fixed), child->icon, x, y);
-      gtk_widget_set_size_request (child->icon, size, size);
-
-      /* increase counter */
-      n++;
-    }
-}
-
-
-
-static gint
-systray_plugin_child_compare (gconstpointer a,
-                              gconstpointer b)
-{
-  const SystrayChild *child_a = a;
-  const SystrayChild *child_b = b;
-
-  if (child_a->state == CHILD_DISABLED
-      || child_b->state == CHILD_DISABLED)
-    return 0;
-
-  /* sort auto hide icons before visible ones */
-  if ((child_a->state == CHILD_AUTO_HIDE)
-      != (child_b->state == CHILD_AUTO_HIDE))
-    return ((child_a->state == CHILD_AUTO_HIDE) ? -1 : 1);
-
-  if (!IS_STRING (child_a->name) || !IS_STRING (child_b->name))
-    {
-      if (IS_STRING (child_a->name) == IS_STRING (child_b->name))
-        return 0;
-      else
-        return !IS_STRING (child_a->name) ? -1 : 1;
-    }
-
-  /* sort by name */
-  return strcmp (child_a->name, child_b->name);
-}
-
-
-
-static void
 systray_plugin_icon_added (SystrayManager *manager,
                            GtkWidget      *icon,
                            SystrayPlugin  *plugin)
 {
-  SystrayChild *child;
+  gchar *name;
 
   panel_return_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager));
   panel_return_if_fail (XFCE_IS_SYSTRAY_PLUGIN (plugin));
   panel_return_if_fail (plugin->manager == manager);
   panel_return_if_fail (GTK_IS_WIDGET (icon));
 
-  /* allocate a new child */
-  child = g_slice_new0 (SystrayChild);
-  child->name = systray_manager_get_application_name (icon);
-  child->state = CHILD_VISIBLE;
-  child->icon = icon;
+  /* get the application name */
+  name = systray_manager_get_application_name (icon);
 
-  /* insert the child in the list */
-  plugin->children = g_slist_insert_sorted (plugin->children, child,
-      systray_plugin_child_compare);
+  /* add the icon to the widget */
+  systray_box_add_with_name (XFCE_SYSTRAY_BOX (plugin->box), icon, name);
 
-  /* put the icon in the widget, offscreen */
-  gtk_fixed_put (GTK_FIXED (plugin->fixed), icon, 0, 0);
-  gtk_widget_show (icon);
+  /* cleanup */
+  g_free (name);
 
-  /* allocate the children */
-  systray_plugin_reallocate (plugin);
+  /* show icon */
+  gtk_widget_show (icon);
 }
 
 
@@ -526,40 +507,13 @@ systray_plugin_icon_removed (SystrayManager *manager,
                              GtkWidget      *icon,
                              SystrayPlugin  *plugin)
 {
-  GSList       *li;
-  SystrayChild *child;
-
   panel_return_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager));
   panel_return_if_fail (XFCE_IS_SYSTRAY_PLUGIN (plugin));
   panel_return_if_fail (plugin->manager == manager);
   panel_return_if_fail (GTK_IS_WIDGET (icon));
 
-  for (li = plugin->children; li != NULL; li = li->next)
-    {
-      child = li->data;
-
-      if (child->icon == icon)
-        {
-          /* remove from the list */
-          plugin->children = g_slist_remove_link (plugin->children, li);
-
-          /* destroy the widget */
-          gtk_widget_destroy (icon);
-
-          /* cleanup */
-          g_free (child->name);
-          g_slice_free (SystrayChild, child);
-
-          /* reallocate the children */
-          systray_plugin_reallocate (plugin);
-
-          /* done */
-          return;
-        }
-    }
-
-  /* icon removed but not known? weird... */
-  panel_assert_not_reached ();
+  /* remove the icon from the box */
+  gtk_container_remove (GTK_CONTAINER (plugin->box), icon);
 }
 
 



More information about the Xfce4-commits mailing list