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-text-table.gob (7870B) - 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 %h{
     25 typedef struct _MNTextTableCell MNTextTableCell;
     26 %}
     27 
     28 %privateheader{
     29 typedef struct
     30 {
     31   GPtrArray	*cells;
     32   int		height;
     33 } Row;
     34 %}
     35 
     36 %{
     37 #include "mn-util.h"
     38 
     39 #define HORIZONTAL_SPACING	12
     40 #define VERTICAL_SPACING	0
     41 
     42 #define COLUMN_SIZE(self, column) \
     43   g_array_index((self)->_priv->column_sizes, int, (column))
     44 
     45 struct _MNTextTableCell
     46 {
     47   PangoLayout	*layout;	/* NULL for a blank cell */
     48   gboolean	dirty;
     49   int		width;
     50   int		height;
     51 };
     52 %}
     53 
     54 /*
     55  * This widget is used to display the mail summaries.
     56  *
     57  * We do not use a GtkBox (or GtkTable) containing labels because it
     58  * is extremely slow when we add a few hundred labels.
     59  *
     60  * A possible alternative would be to use a GtkTextView, which
     61  * provides acceptable performance. However, creating a table using a
     62  * GtkTextView is extremely cumbersome.
     63  */
     64 
     65 class MN:Text:Table from Gtk:Widget
     66 {
     67   private GPtrArray *rows = {g_ptr_array_new()}
     68     destroy { mn_g_ptr_array_free_deep_custom(VAR, (GFunc) self_row_free, NULL); };
     69 
     70   private GArray *column_sizes = {g_array_new(FALSE, TRUE, sizeof(int))}
     71     destroy { g_array_free(VAR, TRUE); };
     72 
     73   private Row *row;
     74 
     75   private gboolean dirty = TRUE;
     76   private int width;
     77   private int height;
     78 
     79   init (self)
     80   {
     81     GTK_WIDGET_SET_FLAGS(self, GTK_NO_WINDOW);
     82 
     83     g_object_connect(self,
     84 		     "swapped-signal::style-set", self_context_changed, self,
     85 		     "swapped-signal::direction-changed", self_context_changed, self,
     86 		     NULL);
     87   }
     88 
     89   private void
     90     row_free (Row *row (check null))
     91   {
     92     mn_g_ptr_array_free_deep_custom(row->cells, (GFunc) self_cell_free, NULL);
     93     g_free(row);
     94   }
     95 
     96   private void
     97     cell_free (MNTextTableCell *cell (check null))
     98   {
     99     if (cell->layout)
    100       g_object_unref(cell->layout);
    101     g_free(cell);
    102   }
    103 
    104   override (Gtk:Widget) void
    105     size_request (GtkWidget *widget, GtkRequisition *requisition)
    106   {
    107     Self *self = SELF(widget);
    108 
    109     self_relayout(self);
    110 
    111     requisition->width = selfp->width;
    112     requisition->height = selfp->height;
    113   }
    114 
    115   override (Gtk:Widget) gboolean
    116     expose_event (GtkWidget *widget, GdkEventExpose *event)
    117   {
    118     Self *self = SELF(widget);
    119     int i;
    120     int y = widget->allocation.y;
    121 
    122     if (! GTK_WIDGET_DRAWABLE(widget))
    123       return FALSE;
    124 
    125     self_relayout(self);
    126 
    127     MN_ARRAY_FOREACH(i, selfp->rows)
    128       {
    129 	Row *row = g_ptr_array_index(selfp->rows, i);
    130 	int j;
    131 	int x = widget->allocation.x;
    132 	int column = 0;
    133 
    134 	MN_ARRAY_FOREACH(j, row->cells)
    135 	  {
    136 	    MNTextTableCell *cell = g_ptr_array_index(row->cells, j);
    137 
    138 	    if (cell->layout)
    139 	      gtk_paint_layout(widget->style,
    140 			       widget->window,
    141 			       GTK_WIDGET_STATE(widget),
    142 			       FALSE,
    143 			       &event->area,
    144 			       widget,
    145 			       NULL,
    146 			       x,
    147 			       y + row->height - cell->height,
    148 			       cell->layout);
    149 
    150 	    x += COLUMN_SIZE(self, column);
    151 	    if (j < row->cells->len - 1)
    152 	      x += HORIZONTAL_SPACING;
    153 
    154 	    column++;
    155 	  }
    156 
    157 	y += row->height;
    158 	if (i < selfp->rows->len - 1)
    159 	  y += VERTICAL_SPACING;
    160       }
    161 
    162     return FALSE;
    163   }
    164 
    165   private void
    166     set_dirty (self)
    167   {
    168     if (! selfp->dirty)
    169       {
    170 	selfp->dirty = TRUE;
    171 	gtk_widget_queue_resize(GTK_WIDGET(self));
    172       }
    173   }
    174 
    175   private void
    176     context_changed (self)
    177   {
    178     int i;
    179 
    180     MN_ARRAY_FOREACH(i, selfp->rows)
    181       {
    182 	Row *row = g_ptr_array_index(selfp->rows, i);
    183 	int j;
    184 
    185 	MN_ARRAY_FOREACH(j, row->cells)
    186 	  {
    187 	    MNTextTableCell *cell = g_ptr_array_index(row->cells, j);
    188 
    189 	    if (cell->layout)
    190 	      {
    191 		pango_layout_context_changed(cell->layout);
    192 		cell->dirty = TRUE;
    193 	      }
    194 	  }
    195       }
    196 
    197     self_set_dirty(self);
    198   }
    199 
    200   private void
    201     relayout (self)
    202   {
    203     int i;
    204 
    205     if (! selfp->dirty)
    206       return;
    207 
    208     selfp->width = 0;
    209     selfp->height = 0;
    210 
    211     MN_ARRAY_FOREACH(i, selfp->column_sizes)
    212       COLUMN_SIZE(self, i) = 0;
    213 
    214     MN_ARRAY_FOREACH(i, selfp->rows)
    215       {
    216 	Row *row = g_ptr_array_index(selfp->rows, i);
    217 	int j;
    218 
    219 	row->height = 0;
    220 
    221 	MN_ARRAY_FOREACH(j, row->cells)
    222 	  {
    223 	    MNTextTableCell *cell = g_ptr_array_index(row->cells, j);
    224 	    int n_columns = j + 1;
    225 
    226 	    if (cell->dirty)
    227 	      {
    228 		g_assert(cell->layout != NULL);
    229 
    230 		pango_layout_get_pixel_size(cell->layout, &cell->width, &cell->height);
    231 		cell->dirty = FALSE;
    232 	      }
    233 
    234 	    if (n_columns > selfp->column_sizes->len)
    235 	      g_array_set_size(selfp->column_sizes, n_columns);
    236 
    237 	    if (cell->width > COLUMN_SIZE(self, j))
    238 	      COLUMN_SIZE(self, j) = cell->width;
    239 	    if (cell->height > row->height)
    240 	      row->height = cell->height;
    241 	  }
    242 
    243 	selfp->height += row->height;
    244       }
    245 
    246     MN_ARRAY_FOREACH(i, selfp->column_sizes)
    247       selfp->width += COLUMN_SIZE(self, i);
    248 
    249     if (selfp->column_sizes->len > 1)
    250       selfp->width += HORIZONTAL_SPACING * (selfp->column_sizes->len - 1);
    251     if (selfp->rows->len > 1)
    252       selfp->height += VERTICAL_SPACING * (selfp->rows->len - 1);
    253 
    254     selfp->dirty = FALSE;
    255   }
    256 
    257   virtual public void
    258     clear (self)
    259   {
    260     mn_g_ptr_array_free_deep_custom(selfp->rows, (GFunc) self_row_free, NULL);
    261     selfp->rows = g_ptr_array_new();
    262 
    263     g_array_set_size(selfp->column_sizes, 0);
    264 
    265     selfp->row = NULL;
    266 
    267     self_set_dirty(self);
    268   }
    269 
    270   public MNTextTableCell *
    271     append_text_cell (self, const char *text)
    272   {
    273     return self_append_text_cell_from_layout(self, gtk_widget_create_pango_layout(GTK_WIDGET(self), text));
    274   }
    275 
    276   public MNTextTableCell *
    277     append_text_cell_from_markup (self, const char *markup (check null))
    278   {
    279     PangoLayout *layout;
    280 
    281     layout = gtk_widget_create_pango_layout(GTK_WIDGET(self), NULL);
    282     pango_layout_set_markup(layout, markup, -1);
    283 
    284     return self_append_text_cell_from_layout(self, layout);
    285   }
    286 
    287   public MNTextTableCell *
    288     append_text_cell_from_layout (self, Pango:Layout *layout (check null))
    289   {
    290     MNTextTableCell *cell;
    291 
    292     cell = g_new(MNTextTableCell, 1);
    293     cell->layout = layout;
    294     cell->dirty = TRUE;
    295 
    296     self_append_cell_real(self, cell);
    297 
    298     return cell;
    299   }
    300 
    301   public MNTextTableCell *
    302     append_blank_cell (self, int width, int height)
    303   {
    304     MNTextTableCell *cell;
    305 
    306     cell = g_new(MNTextTableCell, 1);
    307     cell->layout = NULL;
    308     cell->dirty = FALSE;
    309     cell->width = width;
    310     cell->height = height;
    311 
    312     self_append_cell_real(self, cell);
    313 
    314     return cell;
    315   }
    316 
    317   private void
    318     append_cell_real (self, MNTextTableCell *cell)
    319   {
    320     if (! selfp->row)
    321       {
    322 	selfp->row = g_new(Row, 1);
    323 	selfp->row->cells = g_ptr_array_new();
    324 
    325 	g_ptr_array_add(selfp->rows, selfp->row);
    326       }
    327 
    328     g_ptr_array_add(selfp->row->cells, cell);
    329 
    330     self_set_dirty(self);
    331   }
    332 
    333   public void
    334     cell_set_text (self,
    335 		   MNTextTableCell *cell (check null),
    336 		   const char *text (check null))
    337   {
    338     const char *current_text;
    339 
    340     g_return_if_fail(cell->layout != NULL);
    341 
    342     current_text = pango_layout_get_text(cell->layout);
    343     if (! current_text || strcmp(current_text, text))
    344       {
    345 	pango_layout_set_text(cell->layout, text, -1);
    346 	cell->dirty = TRUE;
    347 	self_set_dirty(self);
    348       }
    349   }
    350 
    351   public void
    352     line_break (self)
    353   {
    354     selfp->row = NULL;
    355   }
    356 }