~ubuntu-branches/ubuntu/oneiric/gdm3/oneiric

« back to all changes in this revision

Viewing changes to gui/simple-greeter/libnotificationarea/na-tray.c

  • Committer: Bazaar Package Importer
  • Author(s): Josselin Mouette
  • Date: 2010-03-25 20:02:20 UTC
  • Revision ID: james.westby@ubuntu.com-20100325200220-12cap62s6p304nuh
Tags: upstream-2.29.92
ImportĀ upstreamĀ versionĀ 2.29.92

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2002 Red Hat, Inc.
 
3
 * Copyright (C) 2003-2006 Vincent Untz
 
4
 * Copyright (C) 2007 Christian Persch
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU General Public License as
 
8
 * published by the Free Software Foundation; either version 2 of the
 
9
 * License, or (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful, but
 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * General Public License for more details.
 
15
 * 
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
19
 * 02111-1307, USA.
 
20
 */
 
21
 
 
22
#include <config.h>
 
23
#include <string.h>
 
24
 
 
25
#include <gtk/gtk.h>
 
26
 
 
27
#include "na-tray-manager.h"
 
28
#include "fixedtip.h"
 
29
 
 
30
#include "na-tray.h"
 
31
 
 
32
#define ICON_SPACING 1
 
33
#define MIN_BOX_SIZE 3
 
34
 
 
35
typedef struct
 
36
{
 
37
  NaTrayManager *tray_manager;
 
38
  GSList        *all_trays;
 
39
  GHashTable    *icon_table;
 
40
  GHashTable    *tip_table;
 
41
} TraysScreen;
 
42
 
 
43
struct _NaTrayPrivate
 
44
{
 
45
  GdkScreen   *screen;
 
46
  TraysScreen *trays_screen;
 
47
 
 
48
  GtkWidget *box;
 
49
  GtkWidget *frame;
 
50
 
 
51
  guint idle_redraw_id;
 
52
 
 
53
  GtkOrientation orientation;
 
54
};
 
55
 
 
56
typedef struct
 
57
{
 
58
  char  *text;
 
59
  glong  id;
 
60
  glong  timeout;
 
61
} IconTipBuffer;
 
62
 
 
63
typedef struct
 
64
{
 
65
  NaTray *tray;      /* tray containing the tray icon */
 
66
  GtkWidget  *icon;      /* tray icon sending the message */
 
67
  GtkWidget  *fixedtip;
 
68
  guint       source_id;
 
69
  glong       id;        /* id of the current message */
 
70
  GSList     *buffer;    /* buffered messages */
 
71
} IconTip;
 
72
 
 
73
enum
 
74
{
 
75
  PROP_0,
 
76
  PROP_ORIENTATION,
 
77
  PROP_SCREEN
 
78
};
 
79
 
 
80
static gboolean     initialized   = FALSE;
 
81
static TraysScreen *trays_screens = NULL;
 
82
 
 
83
static void icon_tip_show_next (IconTip *icontip);
 
84
 
 
85
/* NaBox, an instantiable GtkBox */
 
86
 
 
87
typedef GtkBox      NaBox;
 
88
typedef GtkBoxClass NaBoxClass;
 
89
 
 
90
static GType na_box_get_type (void);
 
91
 
 
92
G_DEFINE_TYPE (NaBox, na_box, GTK_TYPE_BOX)
 
93
 
 
94
static void
 
95
na_box_init (NaBox *box)
 
96
{
 
97
}
 
98
 
 
99
static void
 
100
na_box_class_init (NaBoxClass *klass)
 
101
{
 
102
}
 
103
 
 
104
/* NaTray */
 
105
 
 
106
G_DEFINE_TYPE (NaTray, na_tray, GTK_TYPE_BIN)
 
107
 
 
108
static NaTray *
 
109
get_tray (TraysScreen *trays_screen)
 
110
{
 
111
  if (trays_screen->all_trays == NULL)
 
112
    return NULL;
 
113
  
 
114
  return trays_screen->all_trays->data;
 
115
}
 
116
 
 
117
const char *roles[] = {
 
118
  "keyboard",
 
119
  "volume",
 
120
  "bluetooth",
 
121
  "network",
 
122
  "battery",
 
123
  NULL
 
124
};
 
125
 
 
126
const char *wmclass_roles[] = {
 
127
  "Bluetooth-applet", "bluetooth",
 
128
  "Gnome-volume-control-applet", "volume",
 
129
  "Nm-applet", "network",
 
130
  "Gnome-power-manager", "battery",
 
131
  NULL,
 
132
};
 
133
 
 
134
static const char *
 
135
find_role (const char *wmclass)
 
136
{
 
137
  int i;
 
138
 
 
139
  for (i = 0; wmclass_roles[i]; i += 2)
 
140
    {
 
141
      if (strcmp (wmclass, wmclass_roles[i]) == 0)
 
142
        return wmclass_roles[i + 1];
 
143
    }
 
144
 
 
145
  return NULL;
 
146
}
 
147
 
 
148
static int
 
149
find_role_pos (const char *role)
 
150
{
 
151
  int i;
 
152
 
 
153
  for (i = 0; roles[i]; i++)
 
154
    {
 
155
      if (strcmp (role, roles[i]) == 0)
 
156
        break;
 
157
    }
 
158
 
 
159
  return i + 1;
 
160
}
 
161
 
 
162
static void
 
163
tray_added (NaTrayManager *manager,
 
164
            GtkWidget     *icon,
 
165
            TraysScreen   *trays_screen)
 
166
{
 
167
  NaTray *tray;
 
168
  NaTrayPrivate *priv;
 
169
  GList *l, *children;
 
170
  int position;
 
171
  char *class_a;
 
172
  const char *role;
 
173
  int role_position;
 
174
 
 
175
  tray = get_tray (trays_screen);
 
176
  if (tray == NULL)
 
177
    return;
 
178
 
 
179
  priv = tray->priv;
 
180
 
 
181
  g_assert (priv->trays_screen == trays_screen);
 
182
 
 
183
  g_hash_table_insert (trays_screen->icon_table, icon, tray);
 
184
 
 
185
  position = 0;
 
186
 
 
187
  class_a = NULL;
 
188
  na_tray_child_get_wm_class (NA_TRAY_CHILD (icon), NULL, &class_a);
 
189
  if (!class_a)
 
190
    goto insert;
 
191
 
 
192
  role = find_role (class_a);
 
193
  g_free (class_a);
 
194
  if (!role)
 
195
    goto insert;
 
196
 
 
197
  role_position = find_role_pos (role);
 
198
  g_object_set_data (G_OBJECT (icon), "role-position", GINT_TO_POINTER (role_position));
 
199
 
 
200
  children = gtk_container_get_children (GTK_CONTAINER (priv->box));
 
201
  for (l = g_list_last (children); l; l = l->prev)
 
202
    {
 
203
      GtkWidget *child = l->data;
 
204
      gint rp;
 
205
 
 
206
      rp = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (child), "role-position"));
 
207
      if (rp == 0 || rp < role_position)
 
208
        {
 
209
          position = g_list_index (children, child) + 1;
 
210
          break;
 
211
        }
 
212
    }
 
213
  g_list_free (children);
 
214
 
 
215
  if (position < 0)
 
216
    position = 0;
 
217
 
 
218
insert:
 
219
  gtk_box_pack_start (GTK_BOX (priv->box), icon, FALSE, FALSE, 0);
 
220
  gtk_box_reorder_child (GTK_BOX (priv->box), icon, position);
 
221
 
 
222
  gtk_widget_show (icon);
 
223
}
 
224
 
 
225
static void
 
226
tray_removed (NaTrayManager *manager,
 
227
              GtkWidget     *icon,
 
228
              TraysScreen   *trays_screen)
 
229
{
 
230
  NaTray *tray;
 
231
 
 
232
  tray = g_hash_table_lookup (trays_screen->icon_table, icon);
 
233
  if (tray == NULL)
 
234
    return;
 
235
 
 
236
  g_assert (tray->priv->trays_screen == trays_screen);
 
237
 
 
238
  g_hash_table_remove (trays_screen->icon_table, icon);
 
239
  /* this will also destroy the tip associated to this icon */
 
240
  g_hash_table_remove (trays_screen->tip_table, icon);
 
241
}
 
242
 
 
243
static void
 
244
icon_tip_buffer_free (gpointer data,
 
245
                      gpointer userdata)
 
246
{
 
247
  IconTipBuffer *buffer;
 
248
 
 
249
  buffer = data;
 
250
 
 
251
  g_free (buffer->text);
 
252
  buffer->text = NULL;
 
253
 
 
254
  g_free (buffer);
 
255
}
 
256
 
 
257
static void
 
258
icon_tip_free (gpointer data)
 
259
{
 
260
  IconTip *icontip;
 
261
 
 
262
  if (data == NULL)
 
263
    return;
 
264
 
 
265
  icontip = data;
 
266
 
 
267
  if (icontip->fixedtip != NULL)
 
268
    gtk_widget_destroy (GTK_WIDGET (icontip->fixedtip));
 
269
  icontip->fixedtip = NULL;
 
270
 
 
271
  if (icontip->source_id != 0)
 
272
    g_source_remove (icontip->source_id);
 
273
  icontip->source_id = 0;
 
274
 
 
275
  if (icontip->buffer != NULL)
 
276
    {
 
277
      g_slist_foreach (icontip->buffer, icon_tip_buffer_free, NULL);
 
278
      g_slist_free (icontip->buffer);
 
279
    }
 
280
  icontip->buffer = NULL;
 
281
 
 
282
  g_free (icontip);
 
283
}
 
284
 
 
285
static int
 
286
icon_tip_buffer_compare (gconstpointer a,
 
287
                         gconstpointer b)
 
288
{
 
289
  const IconTipBuffer *buffer_a = a;
 
290
  const IconTipBuffer *buffer_b = b;
 
291
 
 
292
  if (buffer_a == NULL || buffer_b == NULL)
 
293
    return !(buffer_a == buffer_b);
 
294
 
 
295
  return buffer_a->id - buffer_b->id;
 
296
}
 
297
 
 
298
static void
 
299
icon_tip_show_next_clicked (GtkWidget *widget,
 
300
                            gpointer   data)
 
301
{
 
302
  icon_tip_show_next ((IconTip *) data);
 
303
}
 
304
 
 
305
static gboolean
 
306
icon_tip_show_next_timeout (gpointer data)
 
307
{
 
308
  IconTip *icontip = (IconTip *) data;
 
309
 
 
310
  icon_tip_show_next (icontip);
 
311
 
 
312
  return FALSE;
 
313
}
 
314
 
 
315
static void
 
316
icon_tip_show_next (IconTip *icontip)
 
317
{
 
318
  IconTipBuffer *buffer;
 
319
 
 
320
  if (icontip->buffer == NULL)
 
321
    {
 
322
      /* this will also destroy the tip window */
 
323
      g_hash_table_remove (icontip->tray->priv->trays_screen->tip_table,
 
324
                           icontip->icon);
 
325
      return;
 
326
    }
 
327
 
 
328
  if (icontip->source_id != 0)
 
329
    g_source_remove (icontip->source_id);
 
330
  icontip->source_id = 0;
 
331
 
 
332
  buffer = icontip->buffer->data;
 
333
  icontip->buffer = g_slist_remove (icontip->buffer, buffer);
 
334
 
 
335
  if (icontip->fixedtip == NULL)
 
336
    {
 
337
      icontip->fixedtip = na_fixed_tip_new (icontip->icon,
 
338
                                            na_tray_get_orientation (icontip->tray));
 
339
 
 
340
      g_signal_connect (icontip->fixedtip, "clicked",
 
341
                        G_CALLBACK (icon_tip_show_next_clicked), icontip);
 
342
    }
 
343
 
 
344
  na_fixed_tip_set_markup (icontip->fixedtip, buffer->text);
 
345
 
 
346
  if (!GTK_WIDGET_MAPPED (icontip->fixedtip))
 
347
    gtk_widget_show (icontip->fixedtip);
 
348
 
 
349
  icontip->id = buffer->id;
 
350
 
 
351
  if (buffer->timeout > 0)
 
352
    icontip->source_id = g_timeout_add_seconds (buffer->timeout,
 
353
                                                icon_tip_show_next_timeout,
 
354
                                                icontip);
 
355
 
 
356
  icon_tip_buffer_free (buffer, NULL);
 
357
}
 
358
 
 
359
static void
 
360
message_sent (NaTrayManager *manager,
 
361
              GtkWidget     *icon,
 
362
              const char    *text,
 
363
              glong          id,
 
364
              glong          timeout,
 
365
              TraysScreen   *trays_screen)
 
366
{
 
367
  IconTip       *icontip;
 
368
  IconTipBuffer  find_buffer;
 
369
  IconTipBuffer *buffer;
 
370
  gboolean       show_now;
 
371
 
 
372
  icontip = g_hash_table_lookup (trays_screen->tip_table, icon);
 
373
 
 
374
  find_buffer.id = id;
 
375
  if (icontip && 
 
376
      (icontip->id == id ||
 
377
       g_slist_find_custom (icontip->buffer, &find_buffer,
 
378
                            icon_tip_buffer_compare) != NULL))
 
379
    /* we already have this message, so ignore it */
 
380
    /* FIXME: in an ideal world, we'd remember all the past ids and ignore them
 
381
     * too */
 
382
    return;
 
383
 
 
384
  show_now = FALSE;
 
385
 
 
386
  if (icontip == NULL)
 
387
    {
 
388
      NaTray *tray;
 
389
 
 
390
      tray = g_hash_table_lookup (trays_screen->icon_table, icon);
 
391
      if (tray == NULL)
 
392
        {
 
393
          /* We don't know about the icon sending the message, so ignore it.
 
394
           * But this should never happen since NaTrayManager shouldn't send
 
395
           * us the message if there's no socket for it. */
 
396
          g_critical ("Ignoring a message sent by a tray icon "
 
397
                      "we don't know: \"%s\".\n", text);
 
398
          return;
 
399
        }
 
400
 
 
401
      icontip = g_new0 (IconTip, 1);
 
402
      icontip->tray = tray;
 
403
      icontip->icon = icon;
 
404
 
 
405
      g_hash_table_insert (trays_screen->tip_table, icon, icontip);
 
406
 
 
407
      show_now = TRUE;
 
408
    }
 
409
 
 
410
  buffer = g_new0 (IconTipBuffer, 1);
 
411
 
 
412
  buffer->text    = g_strdup (text);
 
413
  buffer->id      = id;
 
414
  buffer->timeout = timeout;
 
415
 
 
416
  icontip->buffer = g_slist_append (icontip->buffer, buffer);
 
417
 
 
418
  if (show_now)
 
419
    icon_tip_show_next (icontip);
 
420
}
 
421
 
 
422
static void
 
423
message_cancelled (NaTrayManager *manager,
 
424
                   GtkWidget     *icon,
 
425
                   glong          id,
 
426
                   TraysScreen   *trays_screen)
 
427
{
 
428
  IconTip       *icontip;
 
429
  IconTipBuffer  find_buffer;
 
430
  GSList        *cancel_buffer_l;
 
431
  IconTipBuffer *cancel_buffer;
 
432
 
 
433
  icontip = g_hash_table_lookup (trays_screen->tip_table, icon);
 
434
  if (icontip == NULL)
 
435
    return;
 
436
 
 
437
  if (icontip->id == id)
 
438
    {
 
439
      icon_tip_show_next (icontip);
 
440
      return;
 
441
    }
 
442
 
 
443
  find_buffer.id = id;
 
444
  cancel_buffer_l = g_slist_find_custom (icontip->buffer, &find_buffer,
 
445
                                         icon_tip_buffer_compare);
 
446
  if (cancel_buffer_l == NULL)
 
447
    return;
 
448
 
 
449
  cancel_buffer = cancel_buffer_l->data;
 
450
  icon_tip_buffer_free (cancel_buffer, NULL);
 
451
 
 
452
  icontip->buffer = g_slist_remove_link (icontip->buffer, cancel_buffer_l);
 
453
  g_slist_free_1 (cancel_buffer_l);
 
454
}
 
455
 
 
456
static void
 
457
update_orientation_for_messages (gpointer key,
 
458
                                 gpointer value,
 
459
                                 gpointer data)
 
460
{
 
461
  NaTray *tray;
 
462
  IconTip    *icontip;
 
463
 
 
464
  if (value == NULL)
 
465
    return;
 
466
 
 
467
  icontip = value;
 
468
  tray    = data;
 
469
  if (icontip->tray != tray)
 
470
    return;
 
471
 
 
472
  if (icontip->fixedtip)
 
473
    na_fixed_tip_set_orientation (icontip->fixedtip, tray->priv->orientation);
 
474
}
 
475
 
 
476
static void
 
477
update_size_and_orientation (NaTray *tray)
 
478
{
 
479
  NaTrayPrivate *priv = tray->priv;
 
480
 
 
481
  gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->box), priv->orientation);
 
482
 
 
483
  /* This only happens when setting the property during object construction */
 
484
  if (!priv->trays_screen)
 
485
    return;
 
486
 
 
487
  g_hash_table_foreach (priv->trays_screen->tip_table,
 
488
                        update_orientation_for_messages, tray);
 
489
 
 
490
  if (get_tray (priv->trays_screen) == tray)
 
491
    na_tray_manager_set_orientation (priv->trays_screen->tray_manager,
 
492
                                     priv->orientation);
 
493
 
 
494
  /* note, you want this larger if the frame has non-NONE relief by default. */
 
495
  switch (priv->orientation)
 
496
    {
 
497
    case GTK_ORIENTATION_VERTICAL:
 
498
      /* Give box a min size so the frame doesn't look dumb */
 
499
      gtk_widget_set_size_request (priv->box, MIN_BOX_SIZE, -1);
 
500
      break;
 
501
    case GTK_ORIENTATION_HORIZONTAL:
 
502
      gtk_widget_set_size_request (priv->box, -1, MIN_BOX_SIZE);
 
503
      break;
 
504
    }
 
505
}
 
506
 
 
507
/* Children with alpha channels have been set to be composited by calling
 
508
 * gdk_window_set_composited(). We need to paint these children ourselves.
 
509
 */
 
510
static void
 
511
na_tray_expose_icon (GtkWidget *widget,
 
512
                     gpointer   data)
 
513
{
 
514
  cairo_t *cr = data;
 
515
 
 
516
  if (na_tray_child_has_alpha (NA_TRAY_CHILD (widget)))
 
517
    {
 
518
      gdk_cairo_set_source_pixmap (cr, widget->window,
 
519
                                   widget->allocation.x,
 
520
                                   widget->allocation.y);
 
521
      cairo_paint (cr);
 
522
    }
 
523
}
 
524
 
 
525
static void
 
526
na_tray_expose_box (GtkWidget      *box,
 
527
                    GdkEventExpose *event)
 
528
{
 
529
  cairo_t *cr = gdk_cairo_create (box->window);
 
530
 
 
531
  gdk_cairo_region (cr, event->region);
 
532
  cairo_clip (cr);
 
533
 
 
534
  gtk_container_foreach (GTK_CONTAINER (box), na_tray_expose_icon, cr);
 
535
 
 
536
  cairo_destroy (cr);
 
537
}
 
538
 
 
539
static void
 
540
na_tray_init (NaTray *tray)
 
541
{
 
542
  NaTrayPrivate *priv;
 
543
 
 
544
  priv = tray->priv = G_TYPE_INSTANCE_GET_PRIVATE (tray, NA_TYPE_TRAY, NaTrayPrivate);
 
545
 
 
546
  priv->screen = NULL;
 
547
  priv->orientation = GTK_ORIENTATION_HORIZONTAL;
 
548
 
 
549
  priv->frame = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
 
550
  gtk_container_add (GTK_CONTAINER (tray), priv->frame);
 
551
  gtk_widget_show (priv->frame);
 
552
 
 
553
  priv->box = g_object_new (na_box_get_type (), NULL);
 
554
  g_signal_connect (priv->box, "expose-event",
 
555
                    G_CALLBACK (na_tray_expose_box), tray);
 
556
  gtk_box_set_spacing (GTK_BOX (priv->box), ICON_SPACING);
 
557
  gtk_container_add (GTK_CONTAINER (priv->frame), priv->box);
 
558
  gtk_widget_show (priv->box);
 
559
}
 
560
 
 
561
static GObject *
 
562
na_tray_constructor (GType type,
 
563
                     guint n_construct_properties,
 
564
                     GObjectConstructParam *construct_params)
 
565
{
 
566
  GObject *object;
 
567
  NaTray *tray;
 
568
  NaTrayPrivate *priv;
 
569
  int screen_number;
 
570
 
 
571
  object = G_OBJECT_CLASS (na_tray_parent_class)->constructor (type,
 
572
                                                               n_construct_properties,
 
573
                                                               construct_params);
 
574
  tray = NA_TRAY (object);
 
575
  priv = tray->priv;
 
576
 
 
577
  g_assert (priv->screen != NULL);
 
578
 
 
579
  if (!initialized)
 
580
    {
 
581
      GdkDisplay *display;
 
582
      int n_screens;
 
583
 
 
584
      display = gdk_display_get_default ();
 
585
      n_screens = gdk_display_get_n_screens (display);
 
586
      trays_screens = g_new0 (TraysScreen, n_screens);
 
587
      initialized = TRUE;
 
588
    }
 
589
 
 
590
  screen_number = gdk_screen_get_number (priv->screen);
 
591
 
 
592
  if (trays_screens [screen_number].tray_manager == NULL)
 
593
    {
 
594
      NaTrayManager *tray_manager;
 
595
 
 
596
      tray_manager = na_tray_manager_new ();
 
597
 
 
598
      if (na_tray_manager_manage_screen (tray_manager, priv->screen))
 
599
        {
 
600
          trays_screens [screen_number].tray_manager = tray_manager;
 
601
 
 
602
          na_tray_manager_set_padding (tray_manager, 0);
 
603
 
 
604
          g_signal_connect (tray_manager, "tray_icon_added",
 
605
                            G_CALLBACK (tray_added),
 
606
                            &trays_screens [screen_number]);
 
607
          g_signal_connect (tray_manager, "tray_icon_removed",
 
608
                            G_CALLBACK (tray_removed),
 
609
                            &trays_screens [screen_number]);
 
610
          g_signal_connect (tray_manager, "message_sent",
 
611
                            G_CALLBACK (message_sent),
 
612
                            &trays_screens [screen_number]);
 
613
          g_signal_connect (tray_manager, "message_cancelled",
 
614
                            G_CALLBACK (message_cancelled),
 
615
                            &trays_screens [screen_number]);
 
616
 
 
617
          trays_screens [screen_number].icon_table = g_hash_table_new (NULL,
 
618
                                                                       NULL);
 
619
          trays_screens [screen_number].tip_table = g_hash_table_new_full (
 
620
                                                                NULL,
 
621
                                                                NULL,
 
622
                                                                NULL,
 
623
                                                                icon_tip_free);
 
624
        }
 
625
      else
 
626
        {
 
627
          g_printerr ("System tray didn't get the system tray manager selection for screen %d\n",
 
628
                      screen_number);
 
629
          g_object_unref (tray_manager);
 
630
        }
 
631
    }
 
632
      
 
633
  priv->trays_screen = &trays_screens [screen_number];
 
634
  trays_screens [screen_number].all_trays = g_slist_append (trays_screens [screen_number].all_trays,
 
635
                                                            tray);
 
636
 
 
637
  update_size_and_orientation (tray);
 
638
 
 
639
  return object;
 
640
}
 
641
 
 
642
static void
 
643
na_tray_dispose (GObject *object)
 
644
{
 
645
  NaTray *tray = NA_TRAY (object);
 
646
  NaTrayPrivate *priv = tray->priv;
 
647
  TraysScreen *trays_screen = priv->trays_screen;
 
648
 
 
649
  if (trays_screen != NULL)
 
650
    {
 
651
      trays_screen->all_trays = g_slist_remove (trays_screen->all_trays, tray);
 
652
 
 
653
      if (trays_screen->all_trays == NULL)
 
654
        {
 
655
          /* Make sure we drop the manager selection */
 
656
          g_object_unref (trays_screen->tray_manager);
 
657
          trays_screen->tray_manager = NULL;
 
658
 
 
659
          g_hash_table_destroy (trays_screen->icon_table);
 
660
          trays_screen->icon_table = NULL;
 
661
 
 
662
          g_hash_table_destroy (trays_screen->tip_table);
 
663
          trays_screen->tip_table = NULL;
 
664
        }
 
665
      else
 
666
        {
 
667
          NaTray *new_tray;
 
668
 
 
669
          new_tray = get_tray (trays_screen);
 
670
          if (new_tray != NULL)
 
671
            na_tray_manager_set_orientation (trays_screen->tray_manager,
 
672
                                             na_tray_get_orientation (new_tray));
 
673
        }
 
674
    }
 
675
 
 
676
  priv->trays_screen = NULL;
 
677
 
 
678
  if (priv->idle_redraw_id != 0)
 
679
    {
 
680
      g_source_remove (priv->idle_redraw_id);
 
681
      priv->idle_redraw_id = 0;
 
682
    }
 
683
 
 
684
  G_OBJECT_CLASS (na_tray_parent_class)->dispose (object);
 
685
}
 
686
 
 
687
static void
 
688
na_tray_set_property (GObject      *object,
 
689
                      guint         prop_id,
 
690
                      const GValue *value,
 
691
                      GParamSpec   *pspec)
 
692
{
 
693
  NaTray *tray = NA_TRAY (object);
 
694
  NaTrayPrivate *priv = tray->priv;
 
695
 
 
696
  switch (prop_id)
 
697
    {
 
698
    case PROP_ORIENTATION:
 
699
      na_tray_set_orientation (tray, g_value_get_enum (value));
 
700
      break;
 
701
    case PROP_SCREEN:
 
702
      priv->screen = g_value_get_object (value);
 
703
      break;
 
704
    default:
 
705
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
706
      break;
 
707
    }
 
708
}
 
709
 
 
710
static void
 
711
na_tray_size_request (GtkWidget        *widget,
 
712
                      GtkRequisition   *requisition)
 
713
{
 
714
  gtk_widget_size_request (gtk_bin_get_child (GTK_BIN (widget)), requisition);
 
715
}
 
716
 
 
717
static void
 
718
na_tray_size_allocate (GtkWidget        *widget,
 
719
                       GtkAllocation    *allocation)
 
720
{
 
721
  gtk_widget_size_allocate (gtk_bin_get_child (GTK_BIN (widget)), allocation);
 
722
}
 
723
 
 
724
static void
 
725
na_tray_class_init (NaTrayClass *klass)
 
726
{
 
727
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
728
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
729
 
 
730
  gobject_class->constructor = na_tray_constructor;
 
731
  gobject_class->set_property = na_tray_set_property;
 
732
  gobject_class->dispose = na_tray_dispose;
 
733
 
 
734
  widget_class->size_request = na_tray_size_request;
 
735
  widget_class->size_allocate = na_tray_size_allocate;
 
736
 
 
737
  g_object_class_install_property
 
738
    (gobject_class,
 
739
     PROP_ORIENTATION,
 
740
     g_param_spec_enum ("orientation", "orientation", "orientation",
 
741
                        GTK_TYPE_ORIENTATION,
 
742
                        GTK_ORIENTATION_HORIZONTAL,
 
743
                        G_PARAM_WRITABLE |
 
744
                        G_PARAM_CONSTRUCT_ONLY |
 
745
                        G_PARAM_STATIC_NAME |
 
746
                        G_PARAM_STATIC_NICK |
 
747
                        G_PARAM_STATIC_BLURB));
 
748
  
 
749
  g_object_class_install_property
 
750
    (gobject_class,
 
751
     PROP_SCREEN,
 
752
     g_param_spec_object ("screen", "screen", "screen",
 
753
                          GDK_TYPE_SCREEN,
 
754
                          G_PARAM_WRITABLE |
 
755
                          G_PARAM_CONSTRUCT_ONLY |
 
756
                          G_PARAM_STATIC_NAME |
 
757
                          G_PARAM_STATIC_NICK |
 
758
                          G_PARAM_STATIC_BLURB));
 
759
 
 
760
  g_type_class_add_private (gobject_class, sizeof (NaTrayPrivate));
 
761
}
 
762
 
 
763
NaTray *
 
764
na_tray_new_for_screen (GdkScreen      *screen,
 
765
                        GtkOrientation  orientation)
 
766
{
 
767
  return g_object_new (NA_TYPE_TRAY,
 
768
                       "screen", screen,
 
769
                       "orientation", orientation,
 
770
                       NULL);
 
771
}
 
772
 
 
773
void
 
774
na_tray_set_orientation (NaTray         *tray,
 
775
                         GtkOrientation  orientation)
 
776
{
 
777
  NaTrayPrivate *priv = tray->priv;
 
778
 
 
779
  if (orientation == priv->orientation)
 
780
    return;
 
781
  
 
782
  priv->orientation = orientation;
 
783
 
 
784
  update_size_and_orientation (tray);
 
785
}
 
786
 
 
787
GtkOrientation
 
788
na_tray_get_orientation (NaTray *tray)
 
789
{
 
790
  return tray->priv->orientation;
 
791
}
 
792
 
 
793
static gboolean
 
794
idle_redraw_cb (NaTray *tray)
 
795
{
 
796
  NaTrayPrivate *priv = tray->priv;
 
797
 
 
798
  gtk_container_foreach (GTK_CONTAINER (priv->box), (GtkCallback)na_tray_child_force_redraw, tray);
 
799
  
 
800
  priv->idle_redraw_id = 0;
 
801
 
 
802
  return FALSE;
 
803
}
 
804
 
 
805
void
 
806
na_tray_set_padding (NaTray *tray,
 
807
                     gint    padding)
 
808
{
 
809
  NaTrayPrivate *priv = tray->priv;
 
810
 
 
811
  if (get_tray (priv->trays_screen) == tray)
 
812
    na_tray_manager_set_padding (priv->trays_screen->tray_manager, padding);
 
813
}
 
814
 
 
815
void
 
816
na_tray_force_redraw (NaTray *tray)
 
817
{
 
818
  NaTrayPrivate *priv = tray->priv;
 
819
 
 
820
  /* Force the icons to redraw their backgrounds.
 
821
   */
 
822
  if (priv->idle_redraw_id == 0)
 
823
    priv->idle_redraw_id = g_idle_add ((GSourceFunc) idle_redraw_cb, tray);
 
824
}