2
* @file ui_tray.c tray icon handling
4
* Copyright (C) 2003-2009 Lars Lindner <lars.lindner@gmail.com>
5
* Copyright (C) 2004 Christophe Barbe <christophe.barbe@ufies.org>
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33
#include "eggtrayicon.h"
36
#include "ui/liferea_shell.h"
37
#include "ui/ui_common.h"
38
#include "ui/ui_popup.h"
39
#include "ui/ui_tray.h"
41
// FIXME: determine this from Pango or Cairo somehow...
42
#define FONT_CHAR_WIDTH 6
43
#define FONT_CHAR_HEIGHT 8
45
#define TRAY_ICON_WIDTH 16
46
#define TRAY_ICON_HEIGHT 16
48
static struct trayIcon_priv {
49
int trayCount; /**< reference counter */
50
GdkPixbuf *currentIcon; /**< currently displayed icon */
52
EggTrayIcon *widget; /**< the tray icon widget */
53
GtkWidget *image; /**< render widget in the notification area */
56
} *trayIcon_priv = NULL;
58
static void ui_tray_install(void);
61
ui_tray_tooltip_set (const gchar *message)
63
gtk_widget_set_tooltip_text(GTK_WIDGET(trayIcon_priv->eventBox), message);
66
static void ui_tray_expose_cb() {
70
GtkRequisition requisition;
72
gtk_widget_size_request(GTK_WIDGET(trayIcon_priv->widget), &requisition);
74
/* We expect currentIcon to be a 16x16 image and we will
75
render a colored area with height 10 in the middle of
76
the image with 8pt text inside */
78
gdk_draw_pixbuf(GDK_DRAWABLE(trayIcon_priv->image->window),
79
NULL, trayIcon_priv->currentIcon, 0, 0,
80
(requisition.width > TRAY_ICON_WIDTH)?(requisition.width - TRAY_ICON_WIDTH)/2 - 1:0,
81
(requisition.height > TRAY_ICON_HEIGHT)?(requisition.height - TRAY_ICON_HEIGHT)/2 - 1:0,
82
gdk_pixbuf_get_width(trayIcon_priv->currentIcon),
83
gdk_pixbuf_get_height(trayIcon_priv->currentIcon),
84
GDK_RGB_DITHER_NONE, 0, 0);
86
if(!conf_get_bool_value(SHOW_NEW_COUNT_IN_TRAY))
89
newItems = feedlist_get_new_item_count();
91
guint textWidth, textStart;
92
str = g_strdup_printf("%d", newItems);
93
textWidth = strlen(str) * FONT_CHAR_WIDTH;
95
if(textWidth + 2 > TRAY_ICON_WIDTH)
98
textStart = TRAY_ICON_WIDTH/2 - textWidth/2;
100
c = gdk_cairo_create(trayIcon_priv->image->window);
101
cairo_rectangle(c, textStart - 1, 3, textWidth + 1, FONT_CHAR_HEIGHT + 2);
102
cairo_set_source_rgb(c, 1, 0.50, 0.10); // orange
105
cairo_set_source_rgb(c, 1, 1, 1);
106
cairo_move_to(c, textStart - 1, 3 + FONT_CHAR_HEIGHT);
107
cairo_select_font_face(c, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
108
cairo_set_font_size(c, FONT_CHAR_HEIGHT + 2);
109
cairo_show_text(c, str);
117
ui_tray_icon_set (gint newItems, GdkPixbuf *icon)
121
g_assert (trayIcon_priv->widget);
123
/* Having two code branches here to have real transparency
124
at least with new count disabled... */
125
if (conf_get_bool_value (SHOW_NEW_COUNT_IN_TRAY)) {
126
width = ((guint) log10 (newItems) + 1) * FONT_CHAR_WIDTH;
127
width += 2; /* number color border */
128
width += 2; /* tray icon padding */;
132
trayIcon_priv->currentIcon = icon;
134
if (trayIcon_priv->image)
135
gtk_widget_destroy (trayIcon_priv->image);
137
if (trayIcon_priv->alignment)
138
gtk_widget_destroy (trayIcon_priv->alignment);
140
trayIcon_priv->alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
141
trayIcon_priv->image = gtk_drawing_area_new ();
142
gtk_widget_set_size_request (trayIcon_priv->image, width, 16);
143
g_signal_connect (G_OBJECT (trayIcon_priv->image), "expose_event",
144
G_CALLBACK (ui_tray_expose_cb), NULL);
146
gtk_container_add (GTK_CONTAINER (trayIcon_priv->eventBox), trayIcon_priv->alignment);
147
gtk_container_add (GTK_CONTAINER (trayIcon_priv->alignment), trayIcon_priv->image);
148
gtk_widget_show_all (GTK_WIDGET(trayIcon_priv->widget));
150
/* Skip loading icon if already displayed. */
151
if (icon == trayIcon_priv->currentIcon)
153
trayIcon_priv->currentIcon = icon;
155
if (trayIcon_priv->image)
156
gtk_widget_destroy (trayIcon_priv->image);
158
if (trayIcon_priv->alignment) {
159
gtk_widget_destroy (trayIcon_priv->alignment);
160
trayIcon_priv->alignment = NULL;
163
trayIcon_priv->alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
164
trayIcon_priv->image = gtk_image_new_from_pixbuf (icon);
165
gtk_container_add (GTK_CONTAINER (trayIcon_priv->eventBox), trayIcon_priv->alignment);
166
gtk_container_add (GTK_CONTAINER (trayIcon_priv->alignment), trayIcon_priv->image);
167
gtk_widget_show_all (GTK_WIDGET (trayIcon_priv->widget));
172
ui_tray_update (void)
174
gint newItems, unreadItems;
180
newItems = feedlist_get_new_item_count ();
181
unreadItems = feedlist_get_unread_item_count ();
184
if (network_is_online ())
185
ui_tray_icon_set (newItems, icons[ICON_AVAILABLE]);
187
ui_tray_icon_set (newItems, icons[ICON_AVAILABLE_OFFLINE]);
189
msg = g_strdup_printf (ngettext ("%d new item", "%d new items", newItems), newItems);
191
if (network_is_online ())
192
ui_tray_icon_set (newItems, icons[ICON_EMPTY]);
194
ui_tray_icon_set (newItems, icons[ICON_EMPTY_OFFLINE]);
196
msg = g_strdup (_("No new items"));
199
if (unreadItems != 0)
200
tmp = g_strdup_printf (ngettext("%s\n%d unread item", "%s\n%d unread items", unreadItems), msg, unreadItems);
202
tmp = g_strdup_printf (_("%s\nNo unread items"), msg);
204
ui_tray_tooltip_set (tmp);
209
/* a click on the systray icon should show the program window
210
if invisible or hide it if visible */
212
tray_icon_pressed (GtkWidget *button, GdkEventButton *event, EggTrayIcon *icon)
214
if ((event->type == GDK_2BUTTON_PRESS) || (event->type == GDK_3BUTTON_PRESS))
215
return; /* ignore double and triple clicks */
217
switch (event->button) {
219
liferea_shell_toggle_visibility ();
222
ui_popup_systray_menu (event->button, event->time);
227
static gboolean ui_tray_create_cb() {
231
return FALSE; /* for when we're called by the glib idle handler */
236
ui_tray_embedded_cb(GtkWidget *widget, void *data)
242
static void ui_tray_destroyed_cb(GtkWidget *widget, void *data) {
244
g_object_unref(G_OBJECT(trayIcon_priv->widget));
245
g_free(trayIcon_priv);
246
trayIcon_priv = NULL;
248
if (0 == ui_tray_get_count ())
249
liferea_shell_present ();
251
/* And make it re-appear when the notification area reappears */
252
g_idle_add(ui_tray_create_cb, NULL);
256
static void ui_tray_install(void) {
258
g_assert(!trayIcon_priv);
259
trayIcon_priv = g_new0(struct trayIcon_priv, 1);
261
trayIcon_priv->widget = egg_tray_icon_new(PACKAGE);
262
trayIcon_priv->eventBox = gtk_event_box_new();
264
g_signal_connect(trayIcon_priv->eventBox, "button_press_event",
265
G_CALLBACK(tray_icon_pressed), trayIcon_priv->widget);
266
g_signal_connect(G_OBJECT(trayIcon_priv->widget), "embedded",
267
G_CALLBACK(ui_tray_embedded_cb), NULL);
268
g_signal_connect(G_OBJECT(trayIcon_priv->widget), "destroy",
269
G_CALLBACK(ui_tray_destroyed_cb), NULL);
271
liferea_shell_setup_URL_receiver (trayIcon_priv->eventBox);
273
gtk_container_add(GTK_CONTAINER(trayIcon_priv->widget), trayIcon_priv->eventBox);
274
g_object_ref(G_OBJECT(trayIcon_priv->widget));
277
trayIcon_priv->trayCount++;
280
static void ui_tray_remove(void) {
282
g_assert(trayIcon_priv->widget);
284
g_signal_handlers_disconnect_by_func(G_OBJECT(trayIcon_priv->widget),
285
G_CALLBACK(ui_tray_destroyed_cb), NULL);
286
gtk_widget_destroy(trayIcon_priv->image);
287
g_object_unref(G_OBJECT(trayIcon_priv->widget));
288
gtk_object_destroy(GTK_OBJECT(trayIcon_priv->widget));
289
g_free(trayIcon_priv);
290
trayIcon_priv = NULL;
292
if (0 == ui_tray_get_count ())
293
liferea_shell_present ();
296
void ui_tray_enable(gboolean enabled) {
308
ui_tray_get_count(void)
310
return trayIcon_priv?trayIcon_priv->trayCount:0;
313
gboolean ui_tray_get_origin(gint *x, gint *y) {
318
gdk_window_get_origin(GTK_WIDGET(trayIcon_priv->widget)->window, x, y);
322
void ui_tray_size_request(GtkRequisition *requisition) {
327
gtk_widget_size_request(GTK_WIDGET(trayIcon_priv->widget), requisition);