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 }