* make modest_text_utils_get_display_date return a ptr to
[modest] / src / widgets / modest-hbox-cell-renderer.c
1 /* Copyright (c) 2007, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <config.h>
31
32 #include <glib/gi18n-lib.h>
33 #include <gtk/gtkwidget.h>
34
35 #include <modest-text-utils.h>
36 #include <modest-hbox-cell-renderer.h>
37
38 #define RENDERER_EXPAND_ATTRIBUTE "box-expand"
39
40 static GObjectClass *parent_class = NULL;
41
42 /* /\* signals *\/ */
43 /* enum { */
44 /*      LAST_SIGNAL */
45 /* }; */
46
47 typedef struct _ModestHBoxCellRendererPrivate ModestHBoxCellRendererPrivate;
48
49 struct _ModestHBoxCellRendererPrivate
50 {
51         GList *renderers_list;
52 };
53
54 #define MODEST_HBOX_CELL_RENDERER_GET_PRIVATE(o)        \
55         (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_HBOX_CELL_RENDERER, ModestHBoxCellRendererPrivate))
56
57 /* static guint signals[LAST_SIGNAL] = {0}; */
58
59 /* static functions: GObject */
60 static void modest_hbox_cell_renderer_instance_init (GTypeInstance *instance, gpointer g_class);
61 static void modest_hbox_cell_renderer_finalize (GObject *object);
62 static void modest_hbox_cell_renderer_class_init (ModestHBoxCellRendererClass *klass);
63
64 /* static functions: GtkCellRenderer */
65 static void modest_hbox_cell_renderer_get_size     (GtkCellRenderer       *cell,
66                                                     GtkWidget             *widget,
67                                                     GdkRectangle          *rectangle,
68                                                     gint                  *x_offset,
69                                                     gint                  *y_offset,
70                                                     gint                  *width,
71                                                     gint                  *height);
72 static void modest_hbox_cell_renderer_render       (GtkCellRenderer       *cell,
73                                                     GdkDrawable           *window,
74                                                     GtkWidget             *widget,
75                                                     GdkRectangle          *background_area,
76                                                     GdkRectangle          *cell_area,
77                                                     GdkRectangle          *expose_area,
78                                                     GtkCellRendererState  flags);
79                                                 
80
81 /**
82  * modest_hbox_cell_renderer_new:
83  *
84  * Return value: a new #ModestHBoxCellRenderer instance implemented for Gtk+
85  **/
86 GtkCellRenderer*
87 modest_hbox_cell_renderer_new (void)
88 {
89         ModestHBoxCellRenderer *self = g_object_new (MODEST_TYPE_HBOX_CELL_RENDERER, NULL);
90
91         return GTK_CELL_RENDERER (self);
92 }
93
94 static void
95 modest_hbox_cell_renderer_instance_init (GTypeInstance *instance, gpointer g_class)
96 {
97         ModestHBoxCellRendererPrivate *priv = MODEST_HBOX_CELL_RENDERER_GET_PRIVATE (instance);
98
99         priv->renderers_list = NULL;
100         
101         return;
102 }
103
104 static void
105 modest_hbox_cell_renderer_finalize (GObject *object)
106 {
107         ModestHBoxCellRendererPrivate *priv = MODEST_HBOX_CELL_RENDERER_GET_PRIVATE (object);
108
109         if (priv->renderers_list != NULL) {
110                 g_list_foreach (priv->renderers_list, (GFunc) g_object_unref, NULL);
111                 g_list_free (priv->renderers_list);
112                 priv->renderers_list = NULL;
113         }
114
115         (*parent_class->finalize) (object);
116
117         return;
118 }
119
120 static void 
121 modest_hbox_cell_renderer_class_init (ModestHBoxCellRendererClass *klass)
122 {
123         GObjectClass *object_class;
124         GtkCellRendererClass *renderer_class;
125
126         parent_class = g_type_class_peek_parent (klass);
127         object_class = (GObjectClass*) klass;
128         renderer_class = (GtkCellRendererClass*) klass;
129
130         object_class->finalize = modest_hbox_cell_renderer_finalize;
131         renderer_class->get_size = modest_hbox_cell_renderer_get_size;
132         renderer_class->render = modest_hbox_cell_renderer_render;
133
134         g_type_class_add_private (object_class, sizeof (ModestHBoxCellRendererPrivate));
135
136         return;
137 }
138
139 GType 
140 modest_hbox_cell_renderer_get_type (void)
141 {
142         static GType type = 0;
143
144         if (G_UNLIKELY(type == 0))
145         {
146                 static const GTypeInfo info = 
147                 {
148                   sizeof (ModestHBoxCellRendererClass),
149                   NULL,   /* base_init */
150                   NULL,   /* base_finalize */
151                   (GClassInitFunc) modest_hbox_cell_renderer_class_init,   /* class_init */
152                   NULL,   /* class_finalize */
153                   NULL,   /* class_data */
154                   sizeof (ModestHBoxCellRenderer),
155                   0,      /* n_preallocs */
156                   modest_hbox_cell_renderer_instance_init    /* instance_init */
157                 };
158
159                 type = g_type_register_static (GTK_TYPE_CELL_RENDERER,
160                         "ModestHBoxCellRenderer",
161                         &info, 0);
162
163         }
164
165         return type;
166 }
167
168
169 /**
170  * modest_hbox_cell_renderer_append:
171  * @hbox_renderer: a #ModestHBoxCellRenderer
172  * @cell: a #GtkCellRenderer
173  *
174  * Appends @cell to the end of the list of renderers shown in @hbox_renderer
175  */
176 void 
177 modest_hbox_cell_renderer_append (ModestHBoxCellRenderer *hbox_renderer, 
178                                   GtkCellRenderer *cell,
179                                   gboolean expand)
180 {
181         ModestHBoxCellRendererPrivate *priv = MODEST_HBOX_CELL_RENDERER_GET_PRIVATE (hbox_renderer);
182         
183         priv->renderers_list = g_list_append (priv->renderers_list, cell);
184         g_object_set_data (G_OBJECT (cell), RENDERER_EXPAND_ATTRIBUTE, GINT_TO_POINTER (expand));
185         
186 #if GLIB_CHECK_VERSION(2, 10, 0) /* g_object_ref_sink() was added in glib 2.10: */
187         g_object_ref_sink (G_OBJECT (cell));
188 #else
189         g_object_ref (G_OBJECT (cell));
190         gtk_object_sink (GTK_OBJECT (cell));
191 #endif
192 }
193
194 static void 
195 modest_hbox_cell_renderer_get_size     (GtkCellRenderer       *cell,
196                                         GtkWidget             *widget,
197                                         GdkRectangle          *rectangle,
198                                         gint                  *x_offset,
199                                         gint                  *y_offset,
200                                         gint                  *width,
201                                         gint                  *height)
202 {
203         gint calc_width, calc_height;
204         gint full_width, full_height;
205         GList *node;
206         ModestHBoxCellRendererPrivate *priv = MODEST_HBOX_CELL_RENDERER_GET_PRIVATE (cell);
207
208         calc_width = 0;
209         calc_height = 0;
210
211         for (node = priv->renderers_list; node != NULL; node = g_list_next (node)) {
212                 gint renderer_width, renderer_height;
213                 GtkCellRenderer *renderer = (GtkCellRenderer *) node->data;
214
215                 gtk_cell_renderer_get_size (renderer, widget, NULL, NULL, NULL,
216                                             &renderer_width, &renderer_height);
217                 if ((renderer_width > 0)&&(renderer_height > 0)) {
218                         calc_height = MAX (calc_height, renderer_height);
219                         calc_width += renderer_width;
220                 }
221         }
222
223         full_width = (gint) cell->xpad * 2 + calc_width;
224         full_height = (gint) cell->ypad * 2 + calc_height;
225
226         if (rectangle && calc_width > 0 && calc_height > 0) {
227                 if (x_offset) {
228                         *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
229                                       (1.0 - cell->xalign) : cell->xalign) *
230                                      (rectangle->width - full_width));
231                         *x_offset = MAX (*x_offset, 0);
232                 }
233                 if (y_offset) {
234                         *y_offset = ((cell->yalign) *
235                                      (rectangle->height - full_height));
236                         *y_offset = MAX (*y_offset, 0);
237                 }
238         } else {
239                 if (x_offset)
240                         *x_offset = 0;
241                 if (y_offset)
242                         *y_offset = 0;
243         }
244
245         if (width)
246                 *width = full_width;
247         if (height)
248                 *height = full_height;
249 }
250
251 static void 
252 modest_hbox_cell_renderer_render       (GtkCellRenderer       *cell,
253                                         GdkDrawable           *window,
254                                         GtkWidget             *widget,
255                                         GdkRectangle          *background_area,
256                                         GdkRectangle          *cell_area,
257                                         GdkRectangle          *expose_area,
258                                         GtkCellRendererState  flags)
259 {
260         ModestHBoxCellRendererPrivate *priv = MODEST_HBOX_CELL_RENDERER_GET_PRIVATE (cell);
261         gint nvis_children = 0;
262         gint nexpand_children = 0;
263         GtkTextDirection direction;
264         GList *node = NULL;
265         GtkCellRenderer *child;
266         gint width, extra;
267         GtkRequisition req;
268         
269         direction = gtk_widget_get_direction (widget);
270         nvis_children = 0;
271         nexpand_children = 0;
272
273         /* first, retrieve the requisition of the children cell renderers */
274         modest_hbox_cell_renderer_get_size (cell, widget, NULL, NULL, NULL, &(req.width), &(req.height));
275
276         /* Counts visible and expandable children cell renderers */
277         for (node = priv->renderers_list; node != NULL; node = g_list_next (node)) {
278                 gboolean visible, expand;
279                 child = (GtkCellRenderer *) node->data;
280                 g_object_get (G_OBJECT (child), "visible", &visible, NULL);
281                 expand = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (child), RENDERER_EXPAND_ATTRIBUTE));
282                 
283                 if (visible) {
284                         nvis_children += 1;
285                         if (expand)
286                                 nexpand_children += 1;
287                 }
288         }
289
290         if (nvis_children > 0) {
291                 gint x_pad, y_pad;
292                 gint x;
293                 GdkRectangle child_alloc;
294
295                 if (nexpand_children > 0) {
296                         width = cell_area->width - req.width;
297                         extra = width / nexpand_children;
298                 } else {
299                         width = 0;
300                         extra = 0;
301                 }
302
303                 g_object_get (cell, "xpad", &x_pad, "ypad", &y_pad, NULL);
304                 x = cell_area->x + x_pad;
305                 child_alloc.y = cell_area->y + y_pad;
306                 child_alloc.height = MAX (1, cell_area->height - y_pad * 2);
307
308                 for (node = priv->renderers_list; node != NULL; node = g_list_next (node)) {
309                         gboolean visible, expand;
310
311                         child = (GtkCellRenderer *) node->data;
312                         g_object_get (G_OBJECT (child), "visible", &visible, NULL);
313                         expand = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (child), RENDERER_EXPAND_ATTRIBUTE));
314
315                         if (visible) {
316                                 GtkRequisition child_req;
317                                 gint child_xpad, child_ypad;
318                                 GdkRectangle child_expose_area;
319
320                                 gtk_cell_renderer_get_size (child, widget, NULL, NULL, NULL, &(child_req.width), &(child_req.height));
321                                 g_object_get (child, "xpad", &child_xpad, "ypad", &child_ypad, NULL);
322
323                                 if (expand) {
324                                         if (nexpand_children == 1)
325                                                 child_req.width += width;
326                                         else
327                                                 child_req.width += extra;
328                                         nexpand_children -= 1;
329                                         width -= extra;
330                                 }
331
332                                 child_alloc.width = MAX (1, child_req.width);
333                                 child_alloc.x = x;
334
335                                 if (direction == GTK_TEXT_DIR_RTL)
336                                         child_alloc.x = cell_area->x + cell_area->width - (child_alloc.x - cell_area->x) - child_alloc.width;
337
338                                 if (gdk_rectangle_intersect (&child_alloc, expose_area, &child_expose_area))
339                                         gtk_cell_renderer_render (child, window, widget, background_area, &child_alloc, &child_expose_area, flags);
340                                 x += child_req.width;
341                         }
342                 }
343         }
344         
345 }