src/mn-mail-icon-widget.gob (7060B) - raw
1 /*
2 * Mail Notification
3 * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 %headertop{
21 #include <gtk/gtk.h>
22 %}
23
24 %{
25 #include <math.h>
26 #include "mn-util.h"
27
28 #define ICON_XALIGN 0.5
29 #define ICON_YALIGN 0.5
30
31 #define COUNT_BOX_XPAD 3
32 #define COUNT_BOX_YPAD 3
33
34 #define COUNT_BOX_XMARGIN 0
35 #define COUNT_BOX_YMARGIN 0
36 %}
37
38 class MN:Mail:Icon:Widget from Gtk:Widget
39 {
40 private gboolean blinking;
41 property BOOLEAN blinking (export)
42 set
43 {
44 gboolean blinking = g_value_get_boolean(VAL);
45 if (blinking != selfp->blinking)
46 {
47 selfp->blinking = blinking;
48 if (blinking)
49 {
50 g_assert(selfp->blink_timeout_id == 0);
51 selfp->blink_timeout_id = gdk_threads_add_timeout(500, self_blink_timeout_cb, self);
52 }
53 else
54 {
55 g_assert(selfp->blink_timeout_id != 0);
56 mn_source_clear(&selfp->blink_timeout_id);
57 selfp->is_on = TRUE;
58 gtk_widget_queue_draw(GTK_WIDGET(self));
59 }
60 }
61 }
62 get
63 {
64 g_value_set_boolean(VAL, selfp->blinking);
65 };
66
67 private PangoLayout *count_layout unrefwith g_object_unref;
68
69 private int count;
70 property INT count (export)
71 set
72 {
73 int count = g_value_get_int(VAL);
74 if (count != selfp->count)
75 {
76 selfp->count = count;
77 self_invalidate_count_layout(self);
78 gtk_widget_queue_draw(GTK_WIDGET(self));
79 }
80 }
81 get
82 {
83 g_value_set_int(VAL, selfp->count);
84 };
85
86 private unsigned int blink_timeout_id;
87 private gboolean is_on = TRUE; /* is currently displaying the image? */
88
89 private char *stock_id destroywith g_free;
90 private GtkIconSize icon_size;
91
92 init (self)
93 {
94 GTK_WIDGET_SET_FLAGS(self, GTK_NO_WINDOW);
95
96 gtk_widget_set_name(GTK_WIDGET(self), "mn-mail-icon");
97
98 g_object_connect(self,
99 "swapped-signal::size-allocate", self_invalidate_count_layout, self,
100 "swapped-signal::style-set", self_context_changed, self,
101 "swapped-signal::direction-changed", self_context_changed, self,
102 NULL);
103 }
104
105 dispose (self)
106 {
107 mn_source_clear(&selfp->blink_timeout_id);
108 }
109
110 override (Gtk:Widget) void
111 size_request (GtkWidget *widget, GtkRequisition *requisition)
112 {
113 Self *self = SELF(widget);
114 GdkPixbuf *pixbuf;
115
116 pixbuf = self_render_icon(self);
117
118 if (pixbuf)
119 {
120 requisition->width = gdk_pixbuf_get_width(pixbuf);
121 requisition->height = gdk_pixbuf_get_height(pixbuf);
122
123 g_object_unref(pixbuf);
124 }
125 else
126 {
127 requisition->width = 0;
128 requisition->height = 0;
129 }
130 }
131
132 override (Gtk:Widget) gboolean
133 expose_event (GtkWidget *widget, GdkEventExpose *event)
134 {
135 Self *self = SELF(widget);
136
137 if (! GTK_WIDGET_DRAWABLE(widget) || ! selfp->stock_id)
138 return FALSE;
139
140 if (selfp->is_on)
141 {
142 int x;
143 int y;
144 GdkRectangle image_area;
145
146 /* note: widget->requisition is the pixbuf size, see size_request() */
147
148 x = floor(widget->allocation.x + ((widget->allocation.width - widget->requisition.width) * ICON_XALIGN));
149 y = floor(widget->allocation.y + ((widget->allocation.height - widget->requisition.height) * ICON_YALIGN));
150
151 image_area.x = x;
152 image_area.y = y;
153 image_area.width = widget->requisition.width;
154 image_area.height = widget->requisition.height;
155
156 if (gdk_rectangle_intersect(&event->area, &image_area, &image_area))
157 {
158 GdkPixbuf *pixbuf;
159
160 pixbuf = self_render_icon(self);
161
162 gdk_draw_pixbuf(widget->window,
163 NULL,
164 pixbuf,
165 image_area.x - x,
166 image_area.y - y,
167 image_area.x,
168 image_area.y,
169 image_area.width,
170 image_area.height,
171 GDK_RGB_DITHER_NORMAL,
172 0,
173 0);
174
175 g_object_unref(pixbuf);
176 }
177 }
178
179 if (selfp->count)
180 {
181 PangoRectangle count_rect;
182 int count_x;
183 int count_y;
184 int box_x;
185 int box_y;
186 int box_width;
187 int box_height;
188
189 if (! selfp->count_layout)
190 {
191 const char *size;
192 char *markup;
193
194 if (widget->allocation.height < 32)
195 size = "small";
196 else
197 size = "medium";
198
199 selfp->count_layout = gtk_widget_create_pango_layout(widget, NULL);
200
201 markup = g_strdup_printf("<span size=\"%s\">%i</span>", size, selfp->count);
202 pango_layout_set_markup(selfp->count_layout, markup, -1);
203 g_free(markup);
204 }
205
206 pango_layout_get_pixel_extents(selfp->count_layout, &count_rect, NULL);
207
208 box_width = count_rect.width + COUNT_BOX_XPAD * 2;
209 box_height = count_rect.height + COUNT_BOX_YPAD * 2;
210
211 box_x = widget->allocation.x + widget->allocation.width - box_width - COUNT_BOX_XMARGIN;
212 box_y = widget->allocation.y + widget->allocation.height - box_height - COUNT_BOX_YMARGIN;
213
214 count_x = box_x - count_rect.x + COUNT_BOX_XPAD;
215 count_y = box_y - count_rect.y + COUNT_BOX_YPAD;
216
217 gtk_paint_box(widget->style,
218 widget->window,
219 GTK_WIDGET_STATE(widget),
220 GTK_SHADOW_OUT,
221 &event->area,
222 widget,
223 NULL,
224 box_x,
225 box_y,
226 box_width,
227 box_height);
228
229 gtk_paint_layout(widget->style,
230 widget->window,
231 GTK_WIDGET_STATE(widget),
232 FALSE,
233 &event->area,
234 widget,
235 NULL,
236 count_x,
237 count_y,
238 selfp->count_layout);
239 }
240
241 return FALSE;
242 }
243
244 private GdkPixbuf *
245 render_icon (self)
246 {
247 if (selfp->stock_id)
248 return gtk_widget_render_icon(GTK_WIDGET(self),
249 selfp->stock_id,
250 selfp->icon_size,
251 NULL);
252 else
253 return NULL;
254 }
255
256 private void
257 context_changed (self)
258 {
259 if (selfp->count_layout)
260 {
261 pango_layout_context_changed(selfp->count_layout);
262 gtk_widget_queue_draw(GTK_WIDGET(self));
263 }
264 }
265
266 private void
267 invalidate_count_layout (self)
268 {
269 if (selfp->count_layout)
270 {
271 g_object_unref(selfp->count_layout);
272 selfp->count_layout = NULL;
273 }
274 }
275
276 private gboolean
277 blink_timeout_cb (gpointer data)
278 {
279 Self *self = data;
280
281 selfp->is_on = ! selfp->is_on;
282
283 gtk_widget_queue_draw(GTK_WIDGET(self));
284
285 return TRUE;
286 }
287
288 public GtkWidget *
289 new (void)
290 {
291 return GTK_WIDGET(GET_NEW);
292 }
293
294 public void
295 set_from_stock (self,
296 const char *stock_id (check null),
297 GtkIconSize icon_size)
298 {
299 g_free(selfp->stock_id);
300 selfp->stock_id = g_strdup(stock_id);
301 selfp->icon_size = icon_size;
302
303 /* affects the size request: call queue_resize(), not queue_draw() */
304 gtk_widget_queue_resize(GTK_WIDGET(self));
305 }
306 }