mail-notification

Fork of Jean-Yves Lefort's mail-notification, a tray icon to notify of new mail
git clone https://code.djc.id.au/git/mail-notification/

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 }