~ubuntu-branches/ubuntu/natty/xfce4-panel/natty

« back to all changes in this revision

Viewing changes to plugins/systray/systray-box.c

  • Committer: Bazaar Package Importer
  • Author(s): Lionel Le Folgoc
  • Date: 2010-12-04 15:45:53 UTC
  • mfrom: (1.1.25 upstream)
  • Revision ID: james.westby@ubuntu.com-20101204154553-c1k0n2p2j83chld0
Tags: 4.7.5-0ubuntu1
Upload to natty (pkg-xfce svn r4611).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2007-2009 Nick Schermer <nick@xfce.org>
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify it
 
5
 * under the terms of the GNU General Public License as published by the Free
 
6
 * Software Foundation; either version 2 of the License, or (at your option)
 
7
 * any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful, but WITHOUT
 
10
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
11
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 
12
 * more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License along with
 
15
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 
16
 * Place, Suite 330, Boston, MA  02111-1307  USA
 
17
 */
 
18
 
 
19
#ifdef HAVE_CONFIG_H
 
20
#include <config.h>
 
21
#endif
 
22
 
 
23
#ifdef HAVE_STRING_H
 
24
#include <string.h>
 
25
#endif
 
26
 
 
27
#include <exo/exo.h>
 
28
#include <gtk/gtk.h>
 
29
#include <libxfce4panel/libxfce4panel.h>
 
30
#include <common/panel-private.h>
 
31
#include <common/panel-xfconf.h>
 
32
 
 
33
#include "systray-box.h"
 
34
#include "systray-socket.h"
 
35
 
 
36
#define BUTTON_SIZE        (16)
 
37
#define SPACING            (2)
 
38
#define OFFSCREEN          (-9999)
 
39
#define IS_HORIZONTAL(box) ((box)->arrow_type == GTK_ARROW_LEFT \
 
40
                            || (box)->arrow_type == GTK_ARROW_RIGHT)
 
41
 
 
42
 
 
43
 
 
44
static void     systray_box_get_property          (GObject         *object,
 
45
                                                   guint            prop_id,
 
46
                                                   GValue          *value,
 
47
                                                   GParamSpec      *pspec);
 
48
static void     systray_box_set_property          (GObject         *object,
 
49
                                                   guint            prop_id,
 
50
                                                   const GValue    *value,
 
51
                                                   GParamSpec      *pspec);
 
52
static void     systray_box_finalize              (GObject         *object);
 
53
static void     systray_box_size_request          (GtkWidget       *widget,
 
54
                                                   GtkRequisition  *requisition);
 
55
static void     systray_box_size_allocate         (GtkWidget       *widget,
 
56
                                                   GtkAllocation   *allocation);
 
57
static gboolean systray_box_expose_event          (GtkWidget       *widget,
 
58
                                                   GdkEventExpose  *event);
 
59
static void     systray_box_add                   (GtkContainer    *container,
 
60
                                                   GtkWidget       *child);
 
61
static void     systray_box_remove                (GtkContainer    *container,
 
62
                                                   GtkWidget       *child);
 
63
static void     systray_box_forall                (GtkContainer    *container,
 
64
                                                   gboolean         include_internals,
 
65
                                                   GtkCallback      callback,
 
66
                                                   gpointer         callback_data);
 
67
static GType    systray_box_child_type            (GtkContainer    *container);
 
68
static void     systray_box_names_collect_visible (gpointer         key,
 
69
                                                   gpointer         value,
 
70
                                                   gpointer         user_data);
 
71
static void     systray_box_names_collect_hidden  (gpointer         key,
 
72
                                                   gpointer         value,
 
73
                                                   gpointer         user_data);
 
74
static gboolean systray_box_names_remove          (gpointer         key,
 
75
                                                   gpointer         value,
 
76
                                                   gpointer         user_data);
 
77
static void     systray_box_button_set_arrow      (SystrayBox      *box);
 
78
static gboolean systray_box_button_press_event    (GtkWidget       *widget,
 
79
                                                   GdkEventButton  *event,
 
80
                                                   GtkWidget       *box);
 
81
static void     systray_box_button_clicked        (GtkToggleButton *button,
 
82
                                                   SystrayBox      *box);
 
83
static void     systray_box_update_hidden         (SystrayBox      *box);
 
84
 
 
85
 
 
86
 
 
87
struct _SystrayBoxClass
 
88
{
 
89
  GtkContainerClass __parent__;
 
90
};
 
91
 
 
92
struct _SystrayBox
 
93
{
 
94
  GtkContainer  __parent__;
 
95
 
 
96
  /* all the icons packed in this box */
 
97
  GSList       *childeren;
 
98
 
 
99
  /* table with names, value contains an uint
 
100
   * that represents the hidden bool */
 
101
  GHashTable   *names;
 
102
 
 
103
  /* expand button */
 
104
  GtkWidget    *button;
 
105
 
 
106
  /* position of the arrow button */
 
107
  GtkArrowType  arrow_type;
 
108
 
 
109
  /* hidden childeren counter */
 
110
  gint          n_hidden_childeren;
 
111
 
 
112
  /* whether hidden icons are visible */
 
113
  guint         show_hidden : 1;
 
114
 
 
115
  /* number of rows */
 
116
  gint          rows;
 
117
 
 
118
  /* guess size, this is a value used to reduce the tray flickering */
 
119
  gint          guess_size;
 
120
};
 
121
 
 
122
typedef struct
 
123
{
 
124
  /* the child widget */
 
125
  GtkWidget    *widget;
 
126
 
 
127
  /* whether it could be hidden */
 
128
  guint         auto_hide : 1;
 
129
 
 
130
  /* invisible icon because of invalid requisition */
 
131
  guint         invalid : 1;
 
132
 
 
133
  /* the name of the applcation */
 
134
  gchar        *name;
 
135
}
 
136
SystrayBoxChild;
 
137
 
 
138
enum
 
139
{
 
140
  PROP_0,
 
141
  PROP_NAMES_HIDDEN,
 
142
  PROP_NAMES_VISIBLE
 
143
};
 
144
 
 
145
 
 
146
 
 
147
XFCE_PANEL_DEFINE_TYPE (SystrayBox, systray_box, GTK_TYPE_CONTAINER)
 
148
 
 
149
 
 
150
 
 
151
static void
 
152
systray_box_class_init (SystrayBoxClass *klass)
 
153
{
 
154
  GObjectClass      *gobject_class;
 
155
  GtkWidgetClass    *gtkwidget_class;
 
156
  GtkContainerClass *gtkcontainer_class;
 
157
 
 
158
  gobject_class = G_OBJECT_CLASS (klass);
 
159
  gobject_class->get_property = systray_box_get_property;
 
160
  gobject_class->set_property = systray_box_set_property;
 
161
  gobject_class->finalize = systray_box_finalize;
 
162
 
 
163
  gtkwidget_class = GTK_WIDGET_CLASS (klass);
 
164
  gtkwidget_class->size_request = systray_box_size_request;
 
165
  gtkwidget_class->size_allocate = systray_box_size_allocate;
 
166
  gtkwidget_class->expose_event = systray_box_expose_event;
 
167
 
 
168
  gtkcontainer_class = GTK_CONTAINER_CLASS (klass);
 
169
  gtkcontainer_class->add = systray_box_add;
 
170
  gtkcontainer_class->remove = systray_box_remove;
 
171
  gtkcontainer_class->forall = systray_box_forall;
 
172
  gtkcontainer_class->child_type = systray_box_child_type;
 
173
 
 
174
  g_object_class_install_property (gobject_class,
 
175
                                   PROP_NAMES_HIDDEN,
 
176
                                   g_param_spec_boxed ("names-hidden",
 
177
                                                       NULL, NULL,
 
178
                                                       PANEL_PROPERTIES_TYPE_VALUE_ARRAY,
 
179
                                                       EXO_PARAM_READWRITE));
 
180
 
 
181
  g_object_class_install_property (gobject_class,
 
182
                                   PROP_NAMES_VISIBLE,
 
183
                                   g_param_spec_boxed ("names-visible",
 
184
                                                       NULL, NULL,
 
185
                                                       PANEL_PROPERTIES_TYPE_VALUE_ARRAY,
 
186
                                                       EXO_PARAM_READWRITE));
 
187
}
 
188
 
 
189
 
 
190
 
 
191
static void
 
192
systray_box_init (SystrayBox *box)
 
193
{
 
194
  GTK_WIDGET_SET_FLAGS (box, GTK_NO_WINDOW);
 
195
 
 
196
  box->childeren = NULL;
 
197
  box->button = NULL;
 
198
  box->rows = 1;
 
199
  box->n_hidden_childeren = 0;
 
200
  box->arrow_type = GTK_ARROW_LEFT;
 
201
  box->show_hidden = FALSE;
 
202
  box->guess_size = 128;
 
203
  box->names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
204
 
 
205
  /* create arrow button */
 
206
  box->button = xfce_arrow_button_new (box->arrow_type);
 
207
  GTK_WIDGET_UNSET_FLAGS (box->button, GTK_CAN_DEFAULT | GTK_CAN_FOCUS);
 
208
  gtk_button_set_focus_on_click (GTK_BUTTON (box->button), FALSE);
 
209
  g_signal_connect (G_OBJECT (box->button), "clicked",
 
210
      G_CALLBACK (systray_box_button_clicked), box);
 
211
  g_signal_connect (G_OBJECT (box->button), "button-press-event",
 
212
      G_CALLBACK (systray_box_button_press_event), box);
 
213
  gtk_widget_set_parent (box->button, GTK_WIDGET (box));
 
214
}
 
215
 
 
216
 
 
217
 
 
218
static void
 
219
systray_box_get_property (GObject      *object,
 
220
                          guint         prop_id,
 
221
                          GValue       *value,
 
222
                          GParamSpec   *pspec)
 
223
{
 
224
  SystrayBox *box = XFCE_SYSTRAY_BOX (object);
 
225
  GPtrArray  *array;
 
226
 
 
227
  switch (prop_id)
 
228
    {
 
229
    case PROP_NAMES_VISIBLE:
 
230
      array = g_ptr_array_new ();
 
231
      g_hash_table_foreach (box->names, systray_box_names_collect_visible, array);
 
232
      g_value_set_boxed (value, array);
 
233
      xfconf_array_free (array);
 
234
      break;
 
235
 
 
236
    case PROP_NAMES_HIDDEN:
 
237
      array = g_ptr_array_new ();
 
238
      g_hash_table_foreach (box->names, systray_box_names_collect_hidden, array);
 
239
      g_value_set_boxed (value, array);
 
240
      xfconf_array_free (array);
 
241
      break;
 
242
 
 
243
    default:
 
244
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
245
      break;
 
246
    }
 
247
}
 
248
 
 
249
 
 
250
 
 
251
static void
 
252
systray_box_set_property (GObject      *object,
 
253
                          guint         prop_id,
 
254
                          const GValue *value,
 
255
                          GParamSpec   *pspec)
 
256
{
 
257
  SystrayBox   *box = XFCE_SYSTRAY_BOX (object);
 
258
  GPtrArray    *array;
 
259
  guint         i;
 
260
  const GValue *tmp;
 
261
  gchar        *name;
 
262
  gboolean      hidden = TRUE;
 
263
 
 
264
  switch (prop_id)
 
265
    {
 
266
    case PROP_NAMES_VISIBLE:
 
267
      hidden = FALSE;
 
268
      /* fall-though */
 
269
 
 
270
    case PROP_NAMES_HIDDEN:
 
271
      /* remove old names with this state */
 
272
      g_hash_table_foreach_remove (box->names,
 
273
                                   systray_box_names_remove,
 
274
                                   GUINT_TO_POINTER (hidden));
 
275
 
 
276
      /* add new values */
 
277
      array = g_value_get_boxed (value);
 
278
      if (G_LIKELY (array != NULL))
 
279
        {
 
280
          for (i = 0; i < array->len; i++)
 
281
            {
 
282
              tmp = g_ptr_array_index (array, i);
 
283
              panel_assert (G_VALUE_HOLDS_STRING (tmp));
 
284
              name = g_value_dup_string (tmp);
 
285
              g_hash_table_replace (box->names, name, GUINT_TO_POINTER (hidden));
 
286
            }
 
287
        }
 
288
 
 
289
      /* update icons in the box */
 
290
      systray_box_update_hidden (box);
 
291
      break;
 
292
 
 
293
    default:
 
294
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
295
      break;
 
296
    }
 
297
}
 
298
 
 
299
 
 
300
 
 
301
static void
 
302
systray_box_finalize (GObject *object)
 
303
{
 
304
  SystrayBox *box = XFCE_SYSTRAY_BOX (object);
 
305
 
 
306
  /* check if we're leaking */
 
307
  if (G_UNLIKELY (box->childeren != NULL))
 
308
    {
 
309
      /* free the child list */
 
310
      g_slist_free (box->childeren);
 
311
      g_debug ("Leaking memory, not all children have been removed");
 
312
    }
 
313
 
 
314
  /* destroy the hash table */
 
315
  g_hash_table_destroy (box->names);
 
316
 
 
317
  G_OBJECT_CLASS (systray_box_parent_class)->finalize (object);
 
318
}
 
319
 
 
320
 
 
321
 
 
322
static void
 
323
systray_box_size_request (GtkWidget      *widget,
 
324
                          GtkRequisition *requisition)
 
325
{
 
326
  SystrayBox      *box = XFCE_SYSTRAY_BOX (widget);
 
327
  GSList          *li;
 
328
  SystrayBoxChild *child_info;
 
329
  gint             n_columns;
 
330
  gint             child_size = -1;
 
331
  GtkRequisition   child_req;
 
332
  gint             n_visible_childeren = 0;
 
333
  gint             swap;
 
334
  gint             guess_size, icon_size;
 
335
 
 
336
  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (widget));
 
337
  panel_return_if_fail (requisition != NULL);
 
338
 
 
339
  /* get the guess size */
 
340
  guess_size = box->guess_size - (SPACING * (box->rows - 1));
 
341
  guess_size /= box->rows;
 
342
 
 
343
  /* check if we need to hide or show any childeren */
 
344
  for (li = box->childeren; li != NULL; li = li->next)
 
345
    {
 
346
      child_info = li->data;
 
347
 
 
348
      /* get the icons size request */
 
349
      gtk_widget_size_request (child_info->widget, &child_req);
 
350
 
 
351
      if (G_UNLIKELY (child_req.width == 1 || child_req.height == 1))
 
352
        {
 
353
          /* icons that return a 1 by 1 requisition supposed to be hidden */
 
354
          if (!child_info->invalid)
 
355
            {
 
356
              /* this icon should not be visible */
 
357
              child_info->invalid = TRUE;
 
358
 
 
359
              /* decrease the hidden counter if needed */
 
360
              if (child_info->auto_hide)
 
361
                box->n_hidden_childeren--;
 
362
            }
 
363
        }
 
364
      else
 
365
        {
 
366
          /* restore icon if it was previously invisible */
 
367
          if (G_UNLIKELY (child_info->invalid))
 
368
            {
 
369
              /* visible icon */
 
370
              child_info->invalid = FALSE;
 
371
 
 
372
              /* update counter */
 
373
              if (child_info->auto_hide)
 
374
                box->n_hidden_childeren++;
 
375
            }
 
376
 
 
377
          /* count the number of visible childeren */
 
378
          if (!child_info->auto_hide || box->show_hidden)
 
379
            {
 
380
              /* get the icon size */
 
381
              icon_size = MIN (guess_size, MAX (child_req.width, child_req.height));
 
382
 
 
383
              /* pick largest icon */
 
384
              if (G_UNLIKELY (child_size == -1))
 
385
                child_size = icon_size;
 
386
              else
 
387
                child_size = MAX (child_size, icon_size);
 
388
 
 
389
              /* increase number of visible childeren */
 
390
              n_visible_childeren++;
 
391
            }
 
392
        }
 
393
    }
 
394
 
 
395
  /* number of columns */
 
396
  n_columns = n_visible_childeren / box->rows;
 
397
  if (n_visible_childeren > (n_columns * box->rows))
 
398
    n_columns++;
 
399
 
 
400
  /* set the width and height needed for the icons */
 
401
  if (n_visible_childeren > 0)
 
402
    {
 
403
      requisition->width = ((child_size + SPACING) * n_columns) - SPACING;
 
404
      requisition->height = ((child_size + SPACING) * box->rows) - SPACING;
 
405
    }
 
406
  else
 
407
    {
 
408
      requisition->width = requisition->height = 0;
 
409
    }
 
410
 
 
411
  /* add the button size if there are hidden icons */
 
412
  if (box->n_hidden_childeren > 0)
 
413
    {
 
414
      /* add the button size */
 
415
      requisition->width += BUTTON_SIZE;
 
416
 
 
417
      /* add space */
 
418
      if (n_visible_childeren > 0)
 
419
        requisition->width += SPACING;
 
420
    }
 
421
 
 
422
  /* swap the sizes if the orientation is vertical */
 
423
  if (!IS_HORIZONTAL (box))
 
424
    {
 
425
      swap = requisition->width;
 
426
      requisition->width = requisition->height;
 
427
      requisition->height = swap;
 
428
    }
 
429
 
 
430
  /* add container border */
 
431
  requisition->width += GTK_CONTAINER (widget)->border_width * 2;
 
432
  requisition->height += GTK_CONTAINER (widget)->border_width * 2;
 
433
}
 
434
 
 
435
 
 
436
 
 
437
static void
 
438
systray_box_size_allocate (GtkWidget     *widget,
 
439
                           GtkAllocation *allocation)
 
440
{
 
441
  SystrayBox      *box = XFCE_SYSTRAY_BOX (widget);
 
442
  SystrayBoxChild *child_info;
 
443
  GSList          *li;
 
444
  gint             n;
 
445
  gint             x, y;
 
446
  gint             width, height;
 
447
  gint             offset = 0;
 
448
  gint             child_size;
 
449
  GtkAllocation    child_allocation;
 
450
  gint             swap;
 
451
  gint             n_children;
 
452
 
 
453
  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (widget));
 
454
  panel_return_if_fail (allocation != NULL);
 
455
 
 
456
  /* set widget allocation */
 
457
  widget->allocation = *allocation;
 
458
 
 
459
  n_children = g_slist_length (box->childeren);
 
460
  if (n_children == 0)
 
461
    return;
 
462
 
 
463
  /* get root coordinates */
 
464
  x = allocation->x + GTK_CONTAINER (widget)->border_width;
 
465
  y = allocation->y + GTK_CONTAINER (widget)->border_width;
 
466
 
 
467
  /* get real size */
 
468
  width = allocation->width - 2 * GTK_CONTAINER (widget)->border_width;
 
469
  height = allocation->height - 2 * GTK_CONTAINER (widget)->border_width;
 
470
 
 
471
  /* child size */
 
472
  if (box->rows == 1)
 
473
    {
 
474
      child_size = IS_HORIZONTAL (box) ? width : height;
 
475
      if (box->n_hidden_childeren > 0)
 
476
         child_size -= BUTTON_SIZE + SPACING;
 
477
      n = n_children - (box->show_hidden ? 0 : box->n_hidden_childeren);
 
478
      child_size -= SPACING * MAX (n - 1, 0);
 
479
      if (n > 1)
 
480
        child_size /= n;
 
481
 
 
482
      if (IS_HORIZONTAL (box))
 
483
        y += MAX (height - child_size, 0) / 2;
 
484
      else
 
485
        x += MAX (width - child_size, 0) / 2;
 
486
    }
 
487
  else
 
488
    {
 
489
      child_size = IS_HORIZONTAL (box) ? height : width;
 
490
      child_size -= SPACING * (box->rows - 1);
 
491
      child_size /= box->rows;
 
492
    }
 
493
 
 
494
  /* don't allocate zero width icon */
 
495
  if (child_size < 1)
 
496
    child_size = 1;
 
497
 
 
498
  /* position arrow button */
 
499
  if (box->n_hidden_childeren > 0)
 
500
    {
 
501
      /* initialize allocation */
 
502
      child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width;
 
503
      child_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width;
 
504
 
 
505
      /* set the width and height */
 
506
      if (IS_HORIZONTAL (box))
 
507
        {
 
508
          child_allocation.width = BUTTON_SIZE;
 
509
          child_allocation.height = height;
 
510
        }
 
511
      else
 
512
        {
 
513
          child_allocation.width = width;
 
514
          child_allocation.height = BUTTON_SIZE;
 
515
        }
 
516
 
 
517
      /* position the button on the other side of the box */
 
518
      if (box->arrow_type == GTK_ARROW_RIGHT)
 
519
        child_allocation.x += width - child_allocation.width;
 
520
      else if (box->arrow_type == GTK_ARROW_DOWN)
 
521
        child_allocation.y += height - child_allocation.height;
 
522
 
 
523
      /* set the offset for the icons */
 
524
      offset = BUTTON_SIZE + SPACING;
 
525
 
 
526
      /* position the arrow button */
 
527
      gtk_widget_size_allocate (box->button, &child_allocation);
 
528
 
 
529
      /* show button if not already visible */
 
530
      if (!GTK_WIDGET_VISIBLE (box->button))
 
531
        gtk_widget_show (box->button);
 
532
    }
 
533
  else if (GTK_WIDGET_VISIBLE (box->button))
 
534
    {
 
535
      /* hide the button */
 
536
      gtk_widget_hide (box->button);
 
537
    }
 
538
 
 
539
  /* position icons */
 
540
  for (li = box->childeren, n = 0; li != NULL; li = li->next)
 
541
    {
 
542
      child_info = li->data;
 
543
 
 
544
      if (child_info->invalid || (child_info->auto_hide && !box->show_hidden))
 
545
        {
 
546
          /* put icons offscreen */
 
547
          child_allocation.x = child_allocation.y = OFFSCREEN;
 
548
        }
 
549
      else
 
550
        {
 
551
          /* set coordinates */
 
552
          child_allocation.x = (child_size + SPACING) * (n / box->rows) + offset;
 
553
          child_allocation.y = (child_size + SPACING) * (n % box->rows);
 
554
 
 
555
          /* increase item counter */
 
556
          n++;
 
557
 
 
558
          /* swap coordinates on a vertical panel */
 
559
          if (!IS_HORIZONTAL (box))
 
560
            {
 
561
              swap = child_allocation.x;
 
562
              child_allocation.x = child_allocation.y;
 
563
              child_allocation.y = swap;
 
564
            }
 
565
 
 
566
          /* invert the icon order if the arrow button position is right or down */
 
567
          if (box->arrow_type == GTK_ARROW_RIGHT)
 
568
            child_allocation.x = width - child_allocation.x - child_size;
 
569
          else if (box->arrow_type == GTK_ARROW_DOWN)
 
570
            child_allocation.y = height - child_allocation.y - child_size;
 
571
 
 
572
          /* add root */
 
573
          child_allocation.x += x;
 
574
          child_allocation.y += y;
 
575
        }
 
576
 
 
577
      /* set child width and height */
 
578
      child_allocation.width = child_size;
 
579
      child_allocation.height = child_size;
 
580
 
 
581
      /* allocate widget size */
 
582
      gtk_widget_size_allocate (child_info->widget, &child_allocation);
 
583
    }
 
584
}
 
585
 
 
586
 
 
587
 
 
588
static gboolean
 
589
systray_box_expose_event (GtkWidget      *widget,
 
590
                          GdkEventExpose *event)
 
591
{
 
592
  SystrayBox      *box = XFCE_SYSTRAY_BOX (widget);
 
593
  cairo_t         *cr;
 
594
  SystrayBoxChild *child_info;
 
595
  GSList          *li;
 
596
  gboolean         result;
 
597
 
 
598
  result = GTK_WIDGET_CLASS (systray_box_parent_class)->expose_event (widget, event);
 
599
 
 
600
  if (gtk_widget_is_composited (widget))
 
601
    {
 
602
      cr = gdk_cairo_create (widget->window);
 
603
      gdk_cairo_region (cr, event->region);
 
604
      cairo_clip (cr);
 
605
 
 
606
      for (li = box->childeren; li != NULL; li = li->next)
 
607
        {
 
608
          child_info = li->data;
 
609
 
 
610
          /* skip invisible or not composited children */
 
611
          if (child_info->invalid
 
612
              || (child_info->auto_hide && !box->show_hidden)
 
613
              || !systray_socket_is_composited (XFCE_SYSTRAY_SOCKET (child_info->widget)))
 
614
            continue;
 
615
 
 
616
          /* paint the child */
 
617
          gdk_cairo_set_source_pixmap (cr, child_info->widget->window,
 
618
                                       child_info->widget->allocation.x,
 
619
                                       child_info->widget->allocation.y);
 
620
          cairo_paint (cr);
 
621
        }
 
622
 
 
623
      cairo_destroy (cr);
 
624
    }
 
625
 
 
626
  return result;
 
627
}
 
628
 
 
629
 
 
630
 
 
631
static void
 
632
systray_box_add (GtkContainer *container,
 
633
                 GtkWidget    *child)
 
634
{
 
635
  SystrayBox *box = XFCE_SYSTRAY_BOX (container);
 
636
 
 
637
  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
 
638
 
 
639
  /* add the entry */
 
640
  systray_box_add_with_name (box, child, NULL);
 
641
}
 
642
 
 
643
 
 
644
 
 
645
static void
 
646
systray_box_remove (GtkContainer *container,
 
647
                    GtkWidget    *child)
 
648
{
 
649
  SystrayBox      *box = XFCE_SYSTRAY_BOX (container);
 
650
  SystrayBoxChild *child_info;
 
651
  gboolean         need_resize;
 
652
  GSList          *li;
 
653
 
 
654
  /* search the child */
 
655
  for (li = box->childeren; li != NULL; li = li->next)
 
656
    {
 
657
      child_info = li->data;
 
658
 
 
659
      if (child_info->widget == child)
 
660
        {
 
661
          /* whether the need to redraw afterwards */
 
662
          need_resize = !child_info->auto_hide;
 
663
 
 
664
          /* update hidden counter */
 
665
          if (child_info->auto_hide && !child_info->invalid)
 
666
            box->n_hidden_childeren--;
 
667
 
 
668
          /* remove from list */
 
669
          box->childeren = g_slist_remove_link (box->childeren, li);
 
670
 
 
671
          /* free name */
 
672
          g_free (child_info->name);
 
673
 
 
674
          /* free child info */
 
675
          g_slice_free (SystrayBoxChild, child_info);
 
676
 
 
677
          /* unparent the widget */
 
678
          gtk_widget_unparent (child);
 
679
 
 
680
          /* resize when the child was visible */
 
681
          if (need_resize)
 
682
            gtk_widget_queue_resize (GTK_WIDGET (container));
 
683
 
 
684
          return;
 
685
        }
 
686
    }
 
687
}
 
688
 
 
689
 
 
690
 
 
691
static void
 
692
systray_box_forall (GtkContainer *container,
 
693
                    gboolean      include_internals,
 
694
                    GtkCallback   callback,
 
695
                    gpointer      callback_data)
 
696
{
 
697
  SystrayBox      *box = XFCE_SYSTRAY_BOX (container);
 
698
  SystrayBoxChild *child_info;
 
699
  GSList          *li;
 
700
 
 
701
  /* for button */
 
702
  (*callback) (GTK_WIDGET (box->button), callback_data);
 
703
 
 
704
  /* run callback for all childeren */
 
705
  for (li = box->childeren; li != NULL; li = li->next)
 
706
    {
 
707
      child_info = li->data;
 
708
 
 
709
      (*callback) (GTK_WIDGET (child_info->widget), callback_data);
 
710
    }
 
711
}
 
712
 
 
713
 
 
714
 
 
715
static GType
 
716
systray_box_child_type (GtkContainer *container)
 
717
 
 
718
{
 
719
  return GTK_TYPE_WIDGET;
 
720
}
 
721
 
 
722
 
 
723
 
 
724
static inline void
 
725
systray_box_names_collect (GPtrArray   *array,
 
726
                           const gchar *name)
 
727
{
 
728
  GValue *tmp;
 
729
 
 
730
  tmp = g_new0 (GValue, 1);
 
731
  g_value_init (tmp, G_TYPE_STRING);
 
732
  g_value_set_string (tmp, name);
 
733
  g_ptr_array_add (array, tmp);
 
734
}
 
735
 
 
736
 
 
737
 
 
738
static void
 
739
systray_box_names_collect_visible (gpointer key,
 
740
                                   gpointer value,
 
741
                                   gpointer user_data)
 
742
{
 
743
  /* add all the visible names */
 
744
  if (!GPOINTER_TO_UINT (value))
 
745
    systray_box_names_collect (user_data, key);
 
746
}
 
747
 
 
748
 
 
749
 
 
750
static void
 
751
systray_box_names_collect_hidden  (gpointer key,
 
752
                                   gpointer value,
 
753
                                   gpointer user_data)
 
754
{
 
755
  /* add all the hidden names */
 
756
  if (GPOINTER_TO_UINT (value))
 
757
    systray_box_names_collect (user_data, key);
 
758
}
 
759
 
 
760
 
 
761
 
 
762
static gboolean
 
763
systray_box_names_remove (gpointer key,
 
764
                          gpointer value,
 
765
                          gpointer user_data)
 
766
{
 
767
  return GPOINTER_TO_UINT (value) == GPOINTER_TO_UINT (user_data);
 
768
}
 
769
 
 
770
 
 
771
 
 
772
static void
 
773
systray_box_button_set_arrow (SystrayBox *box)
 
774
{
 
775
  GtkArrowType arrow_type;
 
776
 
 
777
  /* set arrow type */
 
778
  arrow_type = box->arrow_type;
 
779
 
 
780
  /* invert the arrow direction when the button is toggled */
 
781
  if (box->show_hidden)
 
782
    {
 
783
      if (IS_HORIZONTAL (box))
 
784
        arrow_type = (arrow_type == GTK_ARROW_LEFT ? GTK_ARROW_RIGHT : GTK_ARROW_LEFT);
 
785
      else
 
786
        arrow_type = (arrow_type == GTK_ARROW_UP ? GTK_ARROW_DOWN : GTK_ARROW_UP);
 
787
    }
 
788
 
 
789
  /* set the arrow type */
 
790
  xfce_arrow_button_set_arrow_type (XFCE_ARROW_BUTTON (box->button), arrow_type);
 
791
}
 
792
 
 
793
 
 
794
 
 
795
static gboolean
 
796
systray_box_button_press_event (GtkWidget      *widget,
 
797
                                GdkEventButton *event,
 
798
                                GtkWidget      *box)
 
799
{
 
800
  /* send the event to the box for the panel menu */
 
801
  gtk_widget_event (box, (GdkEvent *) event);
 
802
 
 
803
  return FALSE;
 
804
}
 
805
 
 
806
 
 
807
 
 
808
static void
 
809
systray_box_button_clicked (GtkToggleButton *button,
 
810
                            SystrayBox      *box)
 
811
{
 
812
  /* whether to show hidden icons */
 
813
  box->show_hidden = gtk_toggle_button_get_active (button);
 
814
 
 
815
  /* update the arrow */
 
816
  systray_box_button_set_arrow (box);
 
817
 
 
818
  /* queue a resize */
 
819
  gtk_widget_queue_resize (GTK_WIDGET (box));
 
820
}
 
821
 
 
822
 
 
823
 
 
824
static gint
 
825
systray_box_compare_function (gconstpointer a,
 
826
                              gconstpointer b)
 
827
{
 
828
  const SystrayBoxChild *child_a = a;
 
829
  const SystrayBoxChild *child_b = b;
 
830
 
 
831
  /* sort hidden icons before visible ones */
 
832
  if (child_a->auto_hide != child_b->auto_hide)
 
833
    return (child_a->auto_hide ? -1 : 1);
 
834
 
 
835
  /* put icons without name after the hidden icons */
 
836
  if (exo_str_is_empty (child_a->name) || exo_str_is_empty (child_b->name))
 
837
    {
 
838
      if (!exo_str_is_empty (child_a->name) == !exo_str_is_empty (child_b->name))
 
839
        return 0;
 
840
      else
 
841
        return exo_str_is_empty (child_a->name) ? -1 : 1;
 
842
    }
 
843
 
 
844
  /* sort by name */
 
845
  return strcmp (child_a->name, child_b->name);
 
846
}
 
847
 
 
848
 
 
849
 
 
850
static void
 
851
systray_box_update_hidden (SystrayBox *box)
 
852
{
 
853
  SystrayBoxChild *child_info;
 
854
  GSList          *li;
 
855
  gint             n_hidden_childeren;
 
856
 
 
857
  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
 
858
 
 
859
  /* reset counter */
 
860
  n_hidden_childeren = 0;
 
861
 
 
862
  /* update the icons */
 
863
  for (li = box->childeren; li != NULL; li = li->next)
 
864
    {
 
865
      child_info = li->data;
 
866
 
 
867
      /* update the hidden state */
 
868
      child_info->auto_hide = systray_box_name_get_hidden (box, child_info->name);
 
869
 
 
870
      /* increase counter if needed */
 
871
      if (child_info->auto_hide && !child_info->invalid)
 
872
        n_hidden_childeren++;
 
873
    }
 
874
 
 
875
  if (box->n_hidden_childeren != n_hidden_childeren)
 
876
    {
 
877
      /* set value */
 
878
      box->n_hidden_childeren = n_hidden_childeren;
 
879
 
 
880
      /* sort the list again */
 
881
      box->childeren = g_slist_sort (box->childeren,
 
882
          systray_box_compare_function);
 
883
 
 
884
      /* update the box */
 
885
      gtk_widget_queue_resize (GTK_WIDGET (box));
 
886
    }
 
887
}
 
888
 
 
889
 
 
890
 
 
891
GtkWidget *
 
892
systray_box_new (void)
 
893
{
 
894
  return g_object_new (XFCE_TYPE_SYSTRAY_BOX, NULL);
 
895
}
 
896
 
 
897
 
 
898
 
 
899
void
 
900
systray_box_set_guess_size (SystrayBox *box,
 
901
                            gint        guess_size)
 
902
{
 
903
  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
 
904
 
 
905
  /* set the systray guess size */
 
906
  box->guess_size = guess_size;
 
907
}
 
908
 
 
909
 
 
910
 
 
911
void
 
912
systray_box_set_arrow_type (SystrayBox   *box,
 
913
                            GtkArrowType  arrow_type)
 
914
{
 
915
  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
 
916
 
 
917
  if (G_LIKELY (arrow_type != box->arrow_type))
 
918
    {
 
919
      /* set new setting */
 
920
      box->arrow_type = arrow_type;
 
921
 
 
922
      /* update button arrow */
 
923
      systray_box_button_set_arrow (box);
 
924
 
 
925
      /* queue a resize */
 
926
      if (box->childeren != NULL)
 
927
        gtk_widget_queue_resize (GTK_WIDGET (box));
 
928
    }
 
929
}
 
930
 
 
931
 
 
932
 
 
933
void
 
934
systray_box_set_rows (SystrayBox *box,
 
935
                      gint        rows)
 
936
{
 
937
  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
 
938
 
 
939
  if (G_LIKELY (rows != box->rows))
 
940
    {
 
941
      /* set new setting */
 
942
      box->rows = MAX (1, rows);
 
943
 
 
944
      /* queue a resize */
 
945
      if (box->childeren != NULL)
 
946
        gtk_widget_queue_resize (GTK_WIDGET (box));
 
947
    }
 
948
}
 
949
 
 
950
 
 
951
 
 
952
gint
 
953
systray_box_get_rows (SystrayBox *box)
 
954
{
 
955
  panel_return_val_if_fail (XFCE_IS_SYSTRAY_BOX (box), 1);
 
956
 
 
957
  return box->rows;
 
958
}
 
959
 
 
960
 
 
961
 
 
962
void
 
963
systray_box_add_with_name (SystrayBox  *box,
 
964
                           GtkWidget   *child,
 
965
                           const gchar *name)
 
966
{
 
967
  SystrayBoxChild *child_info;
 
968
 
 
969
  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
 
970
  panel_return_if_fail (GTK_IS_WIDGET (child));
 
971
  panel_return_if_fail (child->parent == NULL);
 
972
  panel_return_if_fail (name == NULL || g_utf8_validate (name, -1, NULL));
 
973
 
 
974
  /* create child info */
 
975
  child_info = g_slice_new (SystrayBoxChild);
 
976
  child_info->widget = child;
 
977
  child_info->invalid = FALSE;
 
978
  child_info->name = g_strdup (name);
 
979
  child_info->auto_hide = systray_box_name_get_hidden (box, child_info->name);
 
980
 
 
981
  /* update hidden counter */
 
982
  if (child_info->auto_hide)
 
983
      box->n_hidden_childeren++;
 
984
 
 
985
  /* insert sorted */
 
986
  box->childeren = g_slist_insert_sorted (box->childeren, child_info,
 
987
                                          systray_box_compare_function);
 
988
 
 
989
  /* set parent widget */
 
990
  gtk_widget_set_parent (child, GTK_WIDGET (box));
 
991
}
 
992
 
 
993
 
 
994
 
 
995
void
 
996
systray_box_name_add (SystrayBox  *box,
 
997
                      const gchar *name,
 
998
                      gboolean     hidden)
 
999
{
 
1000
  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
 
1001
  panel_return_if_fail (!exo_str_is_empty (name));
 
1002
 
 
1003
  /* insert the application */
 
1004
  g_hash_table_insert (box->names, g_strdup (name),
 
1005
                       GUINT_TO_POINTER (hidden ? 1 : 0));
 
1006
 
 
1007
  g_object_notify (G_OBJECT (box), hidden ? "names-hidden"
 
1008
                   : "names-visible");
 
1009
}
 
1010
 
 
1011
 
 
1012
 
 
1013
void
 
1014
systray_box_name_set_hidden (SystrayBox  *box,
 
1015
                             const gchar *name,
 
1016
                             gboolean     hidden)
 
1017
{
 
1018
  panel_return_if_fail (XFCE_IS_SYSTRAY_BOX (box));
 
1019
  panel_return_if_fail (!exo_str_is_empty (name));
 
1020
 
 
1021
  /* replace the old name */
 
1022
  g_hash_table_replace (box->names, g_strdup (name),
 
1023
      GUINT_TO_POINTER (hidden ? 1 : 0));
 
1024
 
 
1025
  /* save new values */
 
1026
  g_object_notify (G_OBJECT (box), "names-hidden");
 
1027
  g_object_notify (G_OBJECT (box), "names-visible");
 
1028
 
 
1029
  /* update the box */
 
1030
  systray_box_update_hidden (box);
 
1031
}
 
1032
 
 
1033
 
 
1034
 
 
1035
gboolean
 
1036
systray_box_name_get_hidden (SystrayBox  *box,
 
1037
                             const gchar *name)
 
1038
{
 
1039
  gpointer p;
 
1040
 
 
1041
  /* do not hide icons without name */
 
1042
  if (G_UNLIKELY (name == NULL))
 
1043
    return FALSE;
 
1044
 
 
1045
  /* lookup the name in the table */
 
1046
  p = g_hash_table_lookup (box->names, name);
 
1047
  if (G_UNLIKELY (p == NULL))
 
1048
    {
 
1049
      /* add the name */
 
1050
      systray_box_name_add (box, name, FALSE);
 
1051
 
 
1052
      /* do not hide the icon */
 
1053
      return FALSE;
 
1054
    }
 
1055
  else
 
1056
    {
 
1057
      return (GPOINTER_TO_UINT (p) == 1 ? TRUE : FALSE);
 
1058
    }
 
1059
}
 
1060
 
 
1061
 
 
1062
 
 
1063
GList *
 
1064
systray_box_name_list (SystrayBox *box)
 
1065
{
 
1066
  GList *keys;
 
1067
 
 
1068
  /* get the hash table keys */
 
1069
  keys = g_hash_table_get_keys (box->names);
 
1070
 
 
1071
  /* sort the list */
 
1072
  keys = g_list_sort (keys, (GCompareFunc) strcmp);
 
1073
 
 
1074
  return keys;
 
1075
}
 
1076
 
 
1077
 
 
1078
 
 
1079
void
 
1080
systray_box_name_clear (SystrayBox *box)
 
1081
{
 
1082
  /* remove all the entries from the list */
 
1083
  g_hash_table_remove_all (box->names);
 
1084
 
 
1085
  g_object_notify (G_OBJECT (box), "names-hidden");
 
1086
  g_object_notify (G_OBJECT (box), "names-visible");
 
1087
 
 
1088
  systray_box_update_hidden (box);
 
1089
}
 
1090