Adding upstream version 0.9.0.
[debian/lxpanel.git] / plugins / netstatus / netstatus-icon.c
CommitLineData
6cc5e1a6
DB
1/*
2 * Copyright (C) 2003 Sun Microsystems, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
18 *
19 * Authors:
20 * Mark McLoughlin <mark@skynet.ie>
21 */
22
23#include <config.h>
24
25#include "netstatus-icon.h"
26
27#include <gtk/gtk.h>
28#include <glib/gi18n.h>
29
30#include "netstatus-util.h"
31#include "netstatus-enums.h"
32#include "netstatus-fallback-pixbuf.h"
33
f7ecd6ce
AG
34#include "gtk-compat.h"
35
6cc5e1a6
DB
36typedef enum
37{
38 NETSTATUS_SIGNAL_0_24 = 0,
39 NETSTATUS_SIGNAL_25_49,
40 NETSTATUS_SIGNAL_50_74,
41 NETSTATUS_SIGNAL_75_100,
42 NETSTATUS_SIGNAL_LAST
43} NetstatusSignal;
44
45struct _NetstatusIconPrivate
46{
47 GtkWidget *image;
48 GtkWidget *signal_image;
49 GtkWidget *error_dialog;
50
51 NetstatusIface *iface;
52 NetstatusState state;
53 NetstatusSignal signal_strength;
54
55 GtkIconTheme *icon_theme;
56 GdkPixbuf *icons [NETSTATUS_STATE_LAST];
57 GdkPixbuf *scaled_icons [NETSTATUS_STATE_LAST];
58
59 GdkPixbuf *signal_icons [NETSTATUS_SIGNAL_LAST];
60 GdkPixbuf *rotated_signal_icons [NETSTATUS_SIGNAL_LAST];
61 GdkPixbuf *scaled_signal_icons [NETSTATUS_SIGNAL_LAST];
62
63 GtkOrientation orientation;
64 int size;
65
cb7f7ba8 66 GtkTooltip *tooltips;
6cc5e1a6
DB
67
68 gulong state_changed_id;
69 gulong name_changed_id;
70 gulong wireless_changed_id;
71 gulong signal_changed_id;
72
73 guint tooltips_enabled : 1;
74 guint show_signal : 1;
75};
76
77enum {
78 INVOKED,
79 LAST_SIGNAL
80};
81
82enum {
83 PROP_0,
84 PROP_IFACE,
85 PROP_ORIENTATION,
86 PROP_TOOLTIPS_ENABLED,
87 PROP_SHOW_SIGNAL
88};
89
90static void netstatus_icon_init_pixbufs (NetstatusIcon *icon);
91static void netstatus_icon_scale_icons (NetstatusIcon *icon,
92 int height);
93
94static GObjectClass *parent_class;
95static guint icon_signals [LAST_SIGNAL] = { 0 };
96
97static GdkPixbuf *
7486d297 98netstatus_icon_get_default_pixbuf (NetstatusIcon *icon __attribute__((unused)))
6cc5e1a6
DB
99{
100 static GdkPixbuf *fallback_pixbuf = NULL;
101
102 if (!fallback_pixbuf)
103 {
104 fallback_pixbuf = gdk_pixbuf_new_from_inline (-1,
105 fallback_icon_data,
106 FALSE,
107 NULL);
108 g_object_add_weak_pointer (G_OBJECT (fallback_pixbuf),
109 (gpointer) &fallback_pixbuf);
110
111 return fallback_pixbuf;
112 }
113
114 return g_object_ref (fallback_pixbuf);
115}
116
117static void
118netstatus_icon_theme_changed (NetstatusIcon *icon,
7486d297 119 GtkIconTheme *icon_theme __attribute__((unused)))
6cc5e1a6
DB
120{
121 int i;
122
123 for (i = 0; i < NETSTATUS_STATE_LAST; i++)
124 {
125 g_object_unref (icon->priv->scaled_icons [i]);
126 icon->priv->scaled_icons [i] = NULL;
127
128 g_object_unref (icon->priv->icons [i]);
129 icon->priv->icons [i] = NULL;
130 }
131
132 for (i = 0; i < NETSTATUS_SIGNAL_LAST; i++)
133 {
134 g_object_unref (icon->priv->scaled_signal_icons [i]);
135 icon->priv->scaled_signal_icons [i] = NULL;
136
137 g_object_unref (icon->priv->signal_icons [i]);
138 icon->priv->signal_icons [i] = NULL;
139 }
140
141 netstatus_icon_init_pixbufs (icon);
142
143 if (icon->priv->size)
144 {
145 netstatus_icon_scale_icons (icon, icon->priv->size);
146 }
147}
148
149static GtkIconTheme *
150netstatus_icon_get_icon_theme (NetstatusIcon *icon)
151{
152 if (!icon->priv->icon_theme)
153 {
154 GdkScreen *screen;
155
156 screen = gtk_widget_get_screen (GTK_WIDGET (icon));
157 icon->priv->icon_theme = gtk_icon_theme_get_for_screen (screen);
158
159 g_signal_connect_object (icon->priv->icon_theme, "changed",
160 G_CALLBACK (netstatus_icon_theme_changed),
161 icon,
162 G_CONNECT_SWAPPED);
163 }
164
165 return icon->priv->icon_theme;
166}
167
168static char *
169netstatus_icon_lookup_icon_theme (NetstatusIcon *icon,
170 const char *icon_name)
171{
172 GtkIconTheme *icon_theme;
173 GtkIconInfo *icon_info;
174 char *filename = NULL;
175
176 icon_theme = netstatus_icon_get_icon_theme (icon);
177 if ((icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, 1000, 0)))
178 {
179 filename = g_strdup (gtk_icon_info_get_filename (icon_info));
180
181 gtk_icon_info_free (icon_info);
182 }
183
184 return filename;
185}
186
187static void
188netstatus_icon_init_pixbuf (NetstatusIcon *icon,
189 GdkPixbuf **pixbuf,
190 const char *icon_name)
191{
192 char *filename;
193
194 g_assert (*pixbuf == NULL);
195
196 if ((filename = netstatus_icon_lookup_icon_theme (icon, icon_name)))
197 {
198 GError *error;
199
200 error = NULL;
201 *pixbuf = gdk_pixbuf_new_from_file (filename, &error);
202 if (error)
203 {
204 netstatus_adopt_error (error, NETSTATUS_ERROR_ICONS);
205 netstatus_iface_set_error (icon->priv->iface, error);
206 g_error_free (error);
207
208 }
209
210 g_free (filename);
211 }
212
213 if (!*pixbuf)
214 {
215 *pixbuf = netstatus_icon_get_default_pixbuf (icon);
216 g_assert (*pixbuf != NULL);
217 }
218}
219
220static GdkPixbuf *
221rotate_pixbuf (GdkPixbuf *pixbuf)
222{
223 GdkPixbuf *freeme;
224 GdkPixbuf *retval;
225 guint32 *dest;
226 guint32 *src;
227 int width;
228 int height;
229 int x;
230 int y;
231
232 freeme = NULL;
233 if (!gdk_pixbuf_get_has_alpha (pixbuf))
234 pixbuf = freeme = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
235
236 width = gdk_pixbuf_get_width (pixbuf);
237 height = gdk_pixbuf_get_height (pixbuf);
238
239 retval = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, height, width);
240
241 dest = (guint32 *) gdk_pixbuf_get_pixels (retval);
242 src = (guint32 *) gdk_pixbuf_get_pixels (pixbuf);
243
244 for (y = 0; y < height; y++)
245 for (x = 0; x < width; x++)
246 dest [(height - y - 1) + height * (width - x - 1)] = src [y * width + x];
247
248 if (freeme)
249 g_object_unref (freeme);
250
251 return retval;
252}
253
254static void
255netstatus_icon_rotate_signal_icons (NetstatusIcon *icon,
256 GtkOrientation orientation)
257{
258 int i;
259
260 if (!icon->priv->signal_icons [0])
261 return;
262
263 for (i = 0; i < NETSTATUS_SIGNAL_LAST; i++)
264 {
265 GdkPixbuf *pixbuf;
266
267 if (orientation == GTK_ORIENTATION_HORIZONTAL)
268 {
269 pixbuf = g_object_ref (icon->priv->signal_icons [i]);
270 }
271 else /* if (orientation == GTK_ORIENTATION_VERTICAL) */
272 {
273 pixbuf = rotate_pixbuf (icon->priv->signal_icons [i]);
274 }
275
276 if (icon->priv->rotated_signal_icons [i])
277 g_object_unref (icon->priv->rotated_signal_icons [i]);
278 icon->priv->rotated_signal_icons [i] = pixbuf;
279
280 if (icon->priv->scaled_signal_icons [i])
281 g_object_unref (icon->priv->scaled_signal_icons [i]);
282 icon->priv->scaled_signal_icons [i] = NULL;
283 }
284}
285
286static void
287netstatus_icon_init_pixbufs (NetstatusIcon *icon)
288{
289 netstatus_icon_init_pixbuf (icon,
290 &icon->priv->icons [NETSTATUS_STATE_DISCONNECTED],
291 "gnome-netstatus-disconn");
292 icon->priv->scaled_icons [NETSTATUS_STATE_DISCONNECTED] = NULL;
293
294 netstatus_icon_init_pixbuf (icon,
295 &icon->priv->icons [NETSTATUS_STATE_IDLE],
296 "gnome-netstatus-idle");
297 icon->priv->scaled_icons [NETSTATUS_STATE_IDLE] = NULL;
298
299 netstatus_icon_init_pixbuf (icon,
300 &icon->priv->icons [NETSTATUS_STATE_TX],
301 "gnome-netstatus-tx");
302 icon->priv->scaled_icons [NETSTATUS_STATE_TX] = NULL;
303
304 netstatus_icon_init_pixbuf (icon,
305 &icon->priv->icons [NETSTATUS_STATE_RX],
306 "gnome-netstatus-rx");
307 icon->priv->scaled_icons [NETSTATUS_STATE_RX] = NULL;
308
309 netstatus_icon_init_pixbuf (icon,
310 &icon->priv->icons [NETSTATUS_STATE_TX_RX],
311 "gnome-netstatus-txrx");
312 icon->priv->scaled_icons [NETSTATUS_STATE_TX_RX] = NULL;
313
314 netstatus_icon_init_pixbuf (icon,
315 &icon->priv->icons [NETSTATUS_STATE_ERROR],
316 "gnome-netstatus-error");
317 icon->priv->scaled_icons [NETSTATUS_STATE_ERROR] = NULL;
318
319 netstatus_icon_init_pixbuf (icon,
320 &icon->priv->signal_icons [NETSTATUS_SIGNAL_0_24],
321 "gnome-netstatus-0-24");
322 icon->priv->rotated_signal_icons [NETSTATUS_SIGNAL_0_24] = NULL;
323 icon->priv->scaled_signal_icons [NETSTATUS_SIGNAL_0_24] = NULL;
324
325 netstatus_icon_init_pixbuf (icon,
326 &icon->priv->signal_icons [NETSTATUS_SIGNAL_25_49],
327 "gnome-netstatus-25-49");
328 icon->priv->rotated_signal_icons [NETSTATUS_SIGNAL_25_49] = NULL;
329 icon->priv->scaled_signal_icons [NETSTATUS_SIGNAL_25_49] = NULL;
330
331 netstatus_icon_init_pixbuf (icon,
332 &icon->priv->signal_icons [NETSTATUS_SIGNAL_50_74],
333 "gnome-netstatus-50-74");
334 icon->priv->rotated_signal_icons [NETSTATUS_SIGNAL_50_74] = NULL;
335 icon->priv->scaled_signal_icons [NETSTATUS_SIGNAL_50_74] = NULL;
336
337 netstatus_icon_init_pixbuf (icon,
338 &icon->priv->signal_icons [NETSTATUS_SIGNAL_75_100],
339 "gnome-netstatus-75-100");
340 icon->priv->rotated_signal_icons [NETSTATUS_SIGNAL_75_100] = NULL;
341 icon->priv->scaled_signal_icons [NETSTATUS_SIGNAL_75_100] = NULL;
342
343 netstatus_icon_rotate_signal_icons (icon, icon->priv->orientation);
344}
345
346static void
347netstatus_icon_update_image (NetstatusIcon *icon)
348{
349 GdkPixbuf *pixbuf;
350
351 if (!icon->priv->icons [icon->priv->state])
352 netstatus_icon_init_pixbufs (icon);
353
354 pixbuf = icon->priv->scaled_icons [icon->priv->state];
355 if (!pixbuf)
356 pixbuf = icon->priv->icons [icon->priv->state];
357
358 if (gtk_image_get_pixbuf (GTK_IMAGE (icon->priv->image)) != pixbuf)
359 gtk_image_set_from_pixbuf (GTK_IMAGE (icon->priv->image), pixbuf);
360
361 pixbuf = icon->priv->scaled_signal_icons [icon->priv->signal_strength];
362 if (!pixbuf)
363 pixbuf = icon->priv->rotated_signal_icons [icon->priv->signal_strength];
364
365 if (gtk_image_get_pixbuf (GTK_IMAGE (icon->priv->signal_image)) != pixbuf)
366 gtk_image_set_from_pixbuf (GTK_IMAGE (icon->priv->signal_image), pixbuf);
367}
368
369static void
7486d297
DB
370netstatus_icon_name_changed (NetstatusIface *iface __attribute__((unused)),
371 GParamSpec *pspec __attribute__((unused)),
6cc5e1a6
DB
372 NetstatusIcon *icon)
373{
cb7f7ba8
DB
374 const gchar *iface_name;
375 const gchar *tip;
376 gchar *freeme = NULL;
6cc5e1a6
DB
377
378 iface_name = netstatus_iface_get_name (icon->priv->iface);
379 if (iface_name)
380 {
381 freeme = g_strdup_printf (_("Network Connection: %s"), iface_name);
382 tip = freeme;
383 }
384 else
385 {
386 tip = _("Network Connection");
387 }
388
cb7f7ba8 389 gtk_widget_set_tooltip_text(GTK_WIDGET (icon), tip);
6cc5e1a6
DB
390
391 g_free (freeme);
392}
393
394static void
395netstatus_icon_state_changed (NetstatusIface *iface,
7486d297 396 GParamSpec *pspec __attribute__((unused)),
6cc5e1a6
DB
397 NetstatusIcon *icon)
398{
399 NetstatusState state;
400
401 g_return_if_fail (NETSTATUS_IS_ICON (icon));
402
403 state = netstatus_iface_get_state (iface);
404
405 if (icon->priv->state != state)
406 {
407 icon->priv->state = state;
408
409 netstatus_icon_update_image (icon);
410 }
411}
412
413static void
414netstatus_icon_is_wireless_changed (NetstatusIface *iface,
7486d297 415 GParamSpec *pspec __attribute__((unused)),
6cc5e1a6
DB
416 NetstatusIcon *icon)
417{
cb7f7ba8 418 if (netstatus_iface_get_is_wireless (iface) && netstatus_icon_get_show_signal(icon))
6cc5e1a6
DB
419 gtk_widget_show (icon->priv->signal_image);
420 else
421 gtk_widget_hide (icon->priv->signal_image);
422}
423
424static void
425netstatus_icon_signal_changed (NetstatusIface *iface,
7486d297 426 GParamSpec *pspec __attribute__((unused)),
6cc5e1a6
DB
427 NetstatusIcon *icon)
428{
429 NetstatusSignal signal_strength;
430 int percent;
431
432 percent = netstatus_iface_get_signal_strength (iface);
433
434 if (percent < 25)
435 signal_strength = NETSTATUS_SIGNAL_0_24;
436 else if (percent < 50)
437 signal_strength = NETSTATUS_SIGNAL_25_49;
438 else if (percent < 75)
439 signal_strength = NETSTATUS_SIGNAL_50_74;
440 else
441 signal_strength = NETSTATUS_SIGNAL_75_100;
442
443 if (icon->priv->signal_strength != signal_strength)
444 {
445 icon->priv->signal_strength = signal_strength;
446
447 netstatus_icon_update_image (icon);
448 }
449}
450
451static void
7a1c5048
AG
452#if GTK_CHECK_VERSION(3, 0, 0)
453netstatus_icon_destroy (GtkWidget *widget)
454#else
6cc5e1a6 455netstatus_icon_destroy (GtkObject *widget)
7a1c5048 456#endif
6cc5e1a6
DB
457{
458 NetstatusIcon *icon = (NetstatusIcon *) widget;
459
460 if (icon->priv->error_dialog)
461 gtk_widget_destroy (icon->priv->error_dialog);
462 icon->priv->error_dialog = NULL;
463
464 if (icon->priv->state_changed_id)
465 {
466 g_assert (icon->priv->iface != NULL);
467 g_assert (icon->priv->name_changed_id != 0);
468 g_signal_handler_disconnect (icon->priv->iface,
469 icon->priv->state_changed_id);
470 g_signal_handler_disconnect (icon->priv->iface,
471 icon->priv->name_changed_id);
472 g_signal_handler_disconnect (icon->priv->iface,
473 icon->priv->wireless_changed_id);
474 g_signal_handler_disconnect (icon->priv->iface,
475 icon->priv->signal_changed_id);
476 }
477 icon->priv->state_changed_id = 0;
478 icon->priv->name_changed_id = 0;
479 icon->priv->wireless_changed_id = 0;
480 icon->priv->signal_changed_id = 0;
481
6cc5e1a6
DB
482 icon->priv->image = NULL;
483
7a1c5048
AG
484#if GTK_CHECK_VERSION(3, 0, 0)
485 GTK_WIDGET_CLASS (parent_class)->destroy (widget);
486#else
6cc5e1a6 487 GTK_OBJECT_CLASS (parent_class)->destroy (widget);
7a1c5048 488#endif
6cc5e1a6
DB
489}
490
491static GdkPixbuf *
492scale_pixbuf (GdkPixbuf *pixbuf,
493 GtkOrientation orientation,
494 int size,
495 gboolean retain_aspect)
496{
497 GdkPixbuf *retval;
498 int orig_size;
499
500 if (orientation == GTK_ORIENTATION_HORIZONTAL)
501 {
502 orig_size = gdk_pixbuf_get_height (pixbuf);
503 }
504 else /* if (orientation == GTK_ORIENTATION_VERTICAL) */
505 {
506 orig_size = gdk_pixbuf_get_width (pixbuf);
507 }
508
509 if (size >= orig_size)
510 {
511 retval = g_object_ref (pixbuf);
512 }
513 else
514 {
515 int orig_width;
516 int orig_height;
517 int scaled_width;
518 int scaled_height;
519 double scale;
520
521 orig_width = gdk_pixbuf_get_width (pixbuf);
522 orig_height = gdk_pixbuf_get_height (pixbuf);
523
524 scale = (double) size / orig_size;
525
526 if (retain_aspect)
527 {
528 scaled_width = orig_width * scale;
529 scaled_height = orig_height * scale;
530 }
531 else
532 {
533 if (orientation == GTK_ORIENTATION_HORIZONTAL)
534 {
535 scaled_width = orig_width;
536 scaled_height = orig_height * scale;
537 }
538 else /* if (orientation == GTK_ORIENTATION_VERTICAL) */
539 {
540 scaled_width = orig_width * scale;
541 scaled_height = orig_height;
542 }
543 }
544
545 retval = gdk_pixbuf_scale_simple (pixbuf,
546 scaled_width,
547 scaled_height,
548 GDK_INTERP_BILINEAR);
549 }
550
551 return retval;
552}
553
554static void
555netstatus_icon_scale_icons (NetstatusIcon *icon,
556 int size)
557{
558 int i;
559
560 g_return_if_fail (size > 0);
561
562 if (!icon->priv->icons [0])
563 netstatus_icon_init_pixbufs (icon);
564
565 for (i = 0; i < NETSTATUS_STATE_LAST; i++)
566 {
567 if (icon->priv->scaled_icons [i])
568 g_object_unref (icon->priv->scaled_icons [i]);
569 icon->priv->scaled_icons [i] = scale_pixbuf (icon->priv->icons [i],
570 icon->priv->orientation,
571 size,
572 TRUE);
573 }
574
575 for (i = 0; i < NETSTATUS_SIGNAL_LAST; i++)
576 {
577 if (icon->priv->scaled_signal_icons [i])
578 g_object_unref (icon->priv->scaled_signal_icons [i]);
579 icon->priv->scaled_signal_icons [i] = scale_pixbuf (icon->priv->rotated_signal_icons [i],
580 icon->priv->orientation,
581 size,
582 TRUE);
583 }
584
585 netstatus_icon_update_image (icon);
586}
587
588static inline GObjectClass *
589get_box_class (GtkOrientation orientation)
590{
6b775dbb 591 return g_type_class_peek(orientation == GTK_ORIENTATION_HORIZONTAL ? GTK_TYPE_HBOX : GTK_TYPE_VBOX);
6cc5e1a6
DB
592}
593
7a1c5048 594#if !GTK_CHECK_VERSION(3, 0, 0)
6cc5e1a6
DB
595static void
596netstatus_icon_size_request (GtkWidget *widget,
597 GtkRequisition *requisition)
598{
599 NetstatusIcon *icon = NETSTATUS_ICON (widget);
600 GObjectClass *klass;
601
602 klass = get_box_class (icon->priv->orientation);
603
604 if (GTK_WIDGET_CLASS (klass)->size_request)
605 GTK_WIDGET_CLASS (klass)->size_request (widget, requisition);
606}
7a1c5048
AG
607// FIXME: for GTK+ 3.0 make get_preferred_{width,height} callbacks!
608#endif
6cc5e1a6
DB
609
610static void
611netstatus_icon_size_allocate (GtkWidget *widget,
612 GtkAllocation *allocation)
613{
614 NetstatusIcon *icon = (NetstatusIcon *) widget;
615 GtkAllocation child_allocation;
616 GObjectClass *klass;
617 int size;
cb7f7ba8
DB
618 GdkWindow *window = gtk_widget_get_window(widget);
619 guint border_width = gtk_container_get_border_width(GTK_CONTAINER (widget));
6cc5e1a6
DB
620
621 if (icon->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
622 size = allocation->height;
623 else
624 size = allocation->width;
625
19ab5cea 626 if (icon->priv->size != size && size > 1)
6cc5e1a6
DB
627 {
628 icon->priv->size = size;
629
630 netstatus_icon_scale_icons (icon, size);
631 }
632
6b775dbb 633 if (gtk_widget_get_realized(widget))
6cc5e1a6 634 {
cb7f7ba8
DB
635 gdk_window_move_resize (window,
636 allocation->x + border_width,
637 allocation->y + border_width,
638 MAX (allocation->width - border_width * 2, 0),
639 MAX (allocation->height - border_width * 2, 0));
6cc5e1a6
DB
640 }
641
642 klass = get_box_class (icon->priv->orientation);
643
644 child_allocation.x = 0;
645 child_allocation.y = 0;
cb7f7ba8
DB
646 child_allocation.width = MAX (allocation->width - border_width * 2, 0);
647 child_allocation.height = MAX (allocation->height - border_width * 2, 0);
6cc5e1a6
DB
648
649 if (GTK_WIDGET_CLASS (klass)->size_allocate)
650 GTK_WIDGET_CLASS (klass)->size_allocate (widget, &child_allocation);
651
cb7f7ba8 652 gtk_widget_get_allocation(widget, allocation);
6cc5e1a6
DB
653}
654
655static void
656netstatus_icon_realize (GtkWidget *widget)
657{
658 GdkWindowAttr attributes;
659 int attributes_mask;
6b775dbb
AG
660 guint border_width;
661 GtkAllocation allocation;
662 GdkWindow *window;
663 GtkStyle *style;
6cc5e1a6 664
6b775dbb
AG
665#if GTK_CHECK_VERSION(2, 20, 0)
666 gtk_widget_set_realized(widget, TRUE);
667#else
6cc5e1a6 668 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
6b775dbb
AG
669#endif
670
671 gtk_widget_get_allocation (widget, &allocation);
6cc5e1a6 672
6b775dbb 673 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
6cc5e1a6 674
6b775dbb
AG
675 attributes.x = allocation.x + border_width;
676 attributes.y = allocation.y + border_width;
677 attributes.width = allocation.width - 2 * border_width;
678 attributes.height = allocation.height - 2 * border_width;
6cc5e1a6
DB
679 attributes.window_type = GDK_WINDOW_CHILD;
680 attributes.wclass = GDK_INPUT_OUTPUT;
681 attributes.visual = gtk_widget_get_visual (widget);
7a1c5048 682#if !GTK_CHECK_VERSION(3, 0, 0)
6cc5e1a6 683 attributes.colormap = gtk_widget_get_colormap (widget);
7a1c5048 684#endif
6cc5e1a6
DB
685 attributes.event_mask = gtk_widget_get_events (widget) |
686 GDK_BUTTON_MOTION_MASK |
687 GDK_BUTTON_PRESS_MASK |
688 GDK_BUTTON_RELEASE_MASK |
689 GDK_EXPOSURE_MASK |
690 GDK_ENTER_NOTIFY_MASK |
691 GDK_LEAVE_NOTIFY_MASK;
692
7a1c5048
AG
693 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
694#if !GTK_CHECK_VERSION(3, 0, 0)
695 attributes_mask |= GDK_WA_COLORMAP;
696#endif
6cc5e1a6 697
6b775dbb
AG
698 window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
699 gtk_widget_set_window (widget, window);
700 gdk_window_set_user_data (window, widget);
6cc5e1a6 701
6b775dbb
AG
702 gtk_widget_ensure_style (widget);
703 style = gtk_widget_get_style (widget);
704 gtk_style_set_background (style, window, GTK_STATE_NORMAL);
6cc5e1a6
DB
705}
706
707static gboolean
708netstatus_icon_button_press_event (GtkWidget *widget,
709 GdkEventButton *event)
710{
711 if (event->button == 1)
712 {
713 netstatus_icon_invoke (NETSTATUS_ICON (widget));
714 return TRUE;
715 }
716
717 return FALSE;
718}
719#if 0
720static void
721netstatus_icon_set_property (GObject *object,
722 guint prop_id,
723 const GValue *value,
724 GParamSpec *pspec)
725{
726 NetstatusIcon *icon;
727
728 g_return_if_fail (NETSTATUS_IS_ICON (object));
729
730 icon = NETSTATUS_ICON (object);
731
732 switch (prop_id)
733 {
734 case PROP_IFACE:
735 netstatus_icon_set_iface (icon, g_value_get_object (value));
736 break;
737 case PROP_ORIENTATION:
738 netstatus_icon_set_orientation (icon, g_value_get_enum (value));
739 break;
740 case PROP_TOOLTIPS_ENABLED:
741 netstatus_icon_set_tooltips_enabled (icon, g_value_get_boolean (value));
742 break;
743 case PROP_SHOW_SIGNAL:
744 netstatus_icon_set_show_signal (icon, g_value_get_boolean (value));
745 break;
746 default:
747 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
748 break;
749 }
750}
751
752static void
753netstatus_icon_get_property (GObject *object,
754 guint prop_id,
755 GValue *value,
756 GParamSpec *pspec)
757{
758 NetstatusIcon *icon;
759
760 g_return_if_fail (NETSTATUS_IS_ICON (object));
761
762 icon = NETSTATUS_ICON (object);
763
764 switch (prop_id)
765 {
766 case PROP_IFACE:
767 g_value_set_object (value, icon->priv->iface);
768 break;
769 case PROP_ORIENTATION:
770 g_value_set_enum (value, icon->priv->orientation);
771 break;
772 case PROP_TOOLTIPS_ENABLED:
773 g_value_set_boolean (value, icon->priv->tooltips_enabled);
774 break;
775 case PROP_SHOW_SIGNAL:
776 g_value_set_boolean (value, icon->priv->show_signal);
777 break;
778 default:
779 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
780 break;
781 }
782}
783#endif
784
785static void
786netstatus_icon_finalize (GObject *object)
787{
788 NetstatusIcon *icon = (NetstatusIcon *) object;
789 int i;
790
791 if (icon->priv->iface)
792 g_object_unref (icon->priv->iface);
793 icon->priv->iface = NULL;
794
795 for (i = 0; i < NETSTATUS_STATE_LAST; i++)
796 {
797 if (icon->priv->icons [i])
798 g_object_unref (icon->priv->icons [i]);
799 icon->priv->icons [i] = NULL;
800
801 if (icon->priv->scaled_icons [i])
802 g_object_unref (icon->priv->scaled_icons [i]);
803 icon->priv->scaled_icons [i] = NULL;
804 }
805
806 g_free (icon->priv);
807 icon->priv = NULL;
808
809 parent_class->finalize (object);
810}
811
812static void
813netstatus_icon_class_init (NetstatusIconClass *klass)
814{
815 GObjectClass *gobject_class = (GObjectClass *) klass;
7a1c5048 816#if !GTK_CHECK_VERSION(3, 0, 0)
6cc5e1a6 817 GtkObjectClass *gtkobject_class = (GtkObjectClass *) klass;
7a1c5048 818#endif
6cc5e1a6
DB
819 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
820
821 parent_class = g_type_class_peek_parent (klass);
822
823 /*
824 gobject_class->set_property = netstatus_icon_set_property;
825 gobject_class->get_property = netstatus_icon_get_property;
826 */
827
828 gobject_class->finalize = netstatus_icon_finalize;
829
7a1c5048
AG
830#if GTK_CHECK_VERSION(3, 0, 0)
831 widget_class->destroy = netstatus_icon_destroy;
832#else
6cc5e1a6 833 gtkobject_class->destroy = netstatus_icon_destroy;
7a1c5048 834#endif
6cc5e1a6 835
7a1c5048 836#if !GTK_CHECK_VERSION(3, 0, 0)
6cc5e1a6 837 widget_class->size_request = netstatus_icon_size_request;
7a1c5048
AG
838 // FIXME: for GTK+ 3.0 make get_preferred_{width,height} callbacks!
839#endif
6cc5e1a6
DB
840 widget_class->size_allocate = netstatus_icon_size_allocate;
841 widget_class->realize = netstatus_icon_realize;
842 widget_class->button_press_event = netstatus_icon_button_press_event;
843#if 0
844 g_object_class_install_property (gobject_class,
845 PROP_IFACE,
846 g_param_spec_object ("iface",
847 _("Interface"),
848 _("The current interface the icon is monitoring."),
849 NETSTATUS_TYPE_IFACE,
850 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
851
852 g_object_class_install_property (gobject_class,
853 PROP_ORIENTATION,
854 g_param_spec_enum ("orientation",
855 _("Orientation"),
856 _("The orientation of the tray."),
857 GTK_TYPE_ORIENTATION,
858 GTK_ORIENTATION_HORIZONTAL,
859 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
860
861 g_object_class_install_property (gobject_class,
862 PROP_TOOLTIPS_ENABLED,
863 g_param_spec_boolean ("tooltips-enabled",
864 _("Tooltips Enabled"),
865 _("Whether or not the icon's tooltips are enabled."),
866 TRUE,
867 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
868
869 g_object_class_install_property (gobject_class,
870 PROP_SHOW_SIGNAL,
871 g_param_spec_boolean ("show-signal",
872 _("Show Signal"),
873 _("Whether or not the signal strength should be displayed."),
874 TRUE,
875 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
876#endif
877
878 icon_signals [INVOKED] =
879 g_signal_new ("invoked",
880 G_OBJECT_CLASS_TYPE (gobject_class),
881 G_SIGNAL_RUN_FIRST,
882 G_STRUCT_OFFSET (NetstatusIconClass, invoked),
883 NULL, NULL,
884 g_cclosure_marshal_VOID__VOID,
885 G_TYPE_NONE, 0);
886}
887
888static void
889netstatus_icon_instance_init (NetstatusIcon *icon,
7486d297 890 NetstatusIconClass *klass __attribute__((unused)))
6cc5e1a6
DB
891{
892 icon->priv = g_new0 (NetstatusIconPrivate, 1);
893
6b775dbb 894 gtk_widget_set_has_window(GTK_WIDGET(icon), TRUE);
6cc5e1a6
DB
895
896 icon->priv->iface = NULL;
897 icon->priv->state = NETSTATUS_STATE_DISCONNECTED;
898 icon->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
899 icon->priv->size = 0;
900 icon->priv->state_changed_id = 0;
901
902 gtk_box_set_spacing (GTK_BOX (icon), 3);
903
904 icon->priv->image = gtk_image_new ();
905 gtk_container_add (GTK_CONTAINER (icon), icon->priv->image);
906 gtk_widget_show (icon->priv->image);
907
908 icon->priv->signal_image = gtk_image_new ();
909 gtk_container_add (GTK_CONTAINER (icon), icon->priv->signal_image);
910 gtk_widget_hide (icon->priv->signal_image);
911
6cc5e1a6
DB
912 gtk_widget_add_events (GTK_WIDGET (icon),
913 GDK_BUTTON_PRESS_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
914}
915
916GType
917netstatus_icon_get_type (void)
918{
919 static GType type = 0;
920 if (!type)
921 {
922 static const GTypeInfo info =
923 {
924 sizeof (NetstatusIconClass),
925 NULL,
926 NULL,
927 (GClassInitFunc) netstatus_icon_class_init,
928 NULL,
929 NULL,
930 sizeof (NetstatusIcon),
931 0,
932 (GInstanceInitFunc) netstatus_icon_instance_init,
933 NULL
934 };
935 type = g_type_register_static (GTK_TYPE_BOX, "NetstatusIcon", &info, 0);
936 }
937 return type;
938}
939
940GtkWidget *
941netstatus_icon_new (NetstatusIface *iface)
942{
943 GtkWidget* obj = g_object_new (NETSTATUS_TYPE_ICON,
944 /*"iface", iface,*/
945 NULL);
946 netstatus_icon_set_iface( NETSTATUS_ICON(obj), iface );
947 return obj;
948}
949
950void
951netstatus_icon_invoke (NetstatusIcon *icon)
952{
953 g_return_if_fail (NETSTATUS_IS_ICON (icon));
954
955 if (netstatus_iface_get_state (icon->priv->iface) != NETSTATUS_STATE_ERROR)
956 {
957 if (icon->priv->error_dialog)
958 gtk_widget_destroy (icon->priv->error_dialog);
959 icon->priv->error_dialog = NULL;
960
961 g_signal_emit (icon, icon_signals [INVOKED], 0);
962 }
963 else
964 {
965 const GError *error;
966
967 error = netstatus_iface_get_error (icon->priv->iface);
968 g_assert (error != NULL);
969
970 if (icon->priv->error_dialog)
971 {
972 gtk_window_set_screen (GTK_WINDOW (icon->priv->error_dialog),
973 gtk_widget_get_screen (GTK_WIDGET (icon)));
974 gtk_window_present (GTK_WINDOW (icon->priv->error_dialog));
975 return;
976 }
977
978 icon->priv->error_dialog =
979 gtk_message_dialog_new (NULL, 0,
980 GTK_MESSAGE_ERROR,
981 GTK_BUTTONS_CLOSE,
982 _("Please contact your system administrator to resolve the following problem:\n\n%s"),
983 error->message);
984
985 gtk_window_set_screen (GTK_WINDOW (icon->priv->error_dialog),
986 gtk_widget_get_screen (GTK_WIDGET (icon)));
987
988 g_signal_connect (icon->priv->error_dialog, "response",
989 G_CALLBACK (gtk_widget_destroy), NULL);
990 g_signal_connect (icon->priv->error_dialog, "destroy",
991 G_CALLBACK (gtk_widget_destroyed),
992 &icon->priv->error_dialog);
993
994 gtk_widget_show (icon->priv->error_dialog);
995 }
996}
997
998void
999netstatus_icon_set_iface (NetstatusIcon *icon,
1000 NetstatusIface *iface)
1001{
1002
1003 g_return_if_fail (NETSTATUS_IS_ICON (icon));
1004
1005 if (icon->priv->iface != iface)
1006 {
1007 NetstatusIface *old_iface;
1008
1009 old_iface = icon->priv->iface;
1010
1011 if (icon->priv->state_changed_id)
1012 {
1013 g_assert (icon->priv->iface != NULL);
1014 g_assert (icon->priv->name_changed_id != 0);
1015 g_signal_handler_disconnect (icon->priv->iface,
1016 icon->priv->state_changed_id);
1017 g_signal_handler_disconnect (icon->priv->iface,
1018 icon->priv->name_changed_id);
1019 g_signal_handler_disconnect (icon->priv->iface,
1020 icon->priv->wireless_changed_id);
1021 g_signal_handler_disconnect (icon->priv->iface,
1022 icon->priv->signal_changed_id);
1023 }
1024
1025 if (iface)
1026 g_object_ref (iface);
1027 icon->priv->iface = iface;
1028
1029 if (old_iface)
1030 g_object_unref (old_iface);
1031
1032 icon->priv->state_changed_id = g_signal_connect (icon->priv->iface, "notify::state",
1033 G_CALLBACK (netstatus_icon_state_changed), icon);
1034 icon->priv->name_changed_id = g_signal_connect (icon->priv->iface, "notify::name",
1035 G_CALLBACK (netstatus_icon_name_changed), icon);
1036 icon->priv->wireless_changed_id = g_signal_connect (icon->priv->iface, "notify::wireless",
1037 G_CALLBACK (netstatus_icon_is_wireless_changed), icon);
1038 icon->priv->signal_changed_id = g_signal_connect (icon->priv->iface, "notify::signal-strength",
1039 G_CALLBACK (netstatus_icon_signal_changed), icon);
1040
1041 netstatus_icon_state_changed (icon->priv->iface, NULL, icon);
1042 netstatus_icon_name_changed (icon->priv->iface, NULL, icon);
1043 netstatus_icon_is_wireless_changed (icon->priv->iface, NULL, icon);
1044 netstatus_icon_signal_changed (icon->priv->iface, NULL, icon);
1045
1046 /* g_object_notify (G_OBJECT (icon), "iface"); */
1047 }
1048}
1049
1050NetstatusIface *
1051netstatus_icon_get_iface (NetstatusIcon *icon)
1052{
1053 g_return_val_if_fail (NETSTATUS_IS_ICON (icon), 0);
1054
1055 return icon->priv->iface;
1056}
1057
1058void
1059netstatus_icon_set_orientation (NetstatusIcon *icon,
1060 GtkOrientation orientation)
1061{
1062 g_return_if_fail (NETSTATUS_IS_ICON (icon));
1063
1064 if (icon->priv->orientation != orientation)
1065 {
1066 icon->priv->orientation = orientation;
1067
1068 netstatus_icon_rotate_signal_icons (icon, orientation);
1069 netstatus_icon_update_image (icon);
1070
1071 icon->priv->size = -1;
1072
1073 gtk_widget_queue_resize (GTK_WIDGET (icon));
1074
1075 g_object_notify (G_OBJECT (icon), "orientation");
1076 }
1077}
1078
1079GtkOrientation
1080netstatus_icon_get_orientation (NetstatusIcon *icon)
1081{
1082 g_return_val_if_fail (NETSTATUS_IS_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
1083
1084 return icon->priv->orientation;
1085}
1086
1087void
1088netstatus_icon_set_tooltips_enabled (NetstatusIcon *icon,
1089 gboolean enabled)
1090{
1091 g_return_if_fail (NETSTATUS_IS_ICON (icon));
1092
1093 enabled = enabled != FALSE;
1094
1095 if (icon->priv->tooltips_enabled != enabled)
1096 {
1097 icon->priv->tooltips_enabled = enabled;
1098
6cc5e1a6
DB
1099 g_object_notify (G_OBJECT (icon), "tooltips-enabled");
1100 }
1101}
1102
1103gboolean
1104netstatus_icon_get_tooltips_enabled (NetstatusIcon *icon)
1105{
1106 g_return_val_if_fail (NETSTATUS_ICON (icon), TRUE);
1107
1108 return icon->priv->tooltips_enabled;
1109}
1110
1111void
1112netstatus_icon_set_show_signal (NetstatusIcon *icon,
1113 gboolean show_signal)
1114{
1115 g_return_if_fail (NETSTATUS_IS_ICON (icon));
1116
1117 show_signal = show_signal != FALSE;
1118
1119 if (icon->priv->show_signal != show_signal)
1120 {
1121 icon->priv->show_signal = show_signal;
1122
1123 if (show_signal && netstatus_iface_get_is_wireless (icon->priv->iface))
1124 gtk_widget_show (icon->priv->signal_image);
1125 else
1126 gtk_widget_hide (icon->priv->signal_image);
1127
7486d297 1128 /* g_object_notify (G_OBJECT (icon), "show-signal"); */
6cc5e1a6
DB
1129 }
1130}
1131
1132gboolean
1133netstatus_icon_get_show_signal (NetstatusIcon *icon)
1134{
1135 g_return_val_if_fail (NETSTATUS_ICON (icon), TRUE);
1136
1137 return icon->priv->show_signal;
1138}