Use accessor functions instead direct access. (only stable GTK+ functions)
[modest] / src / widgets / modest-vbox-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
34 #include <gtk/gtk.h>
35
36 #include <modest-text-utils.h>
37 #include <modest-vbox-cell-renderer.h>
38
39 #define RENDERER_EXPAND_ATTRIBUTE "box-expand"
40
41 static GObjectClass *parent_class = NULL;
42
43 /* /\* signals *\/ */
44 /* enum { */
45 /*      LAST_SIGNAL */
46 /* }; */
47
48 typedef struct _ModestVBoxCellRendererPrivate ModestVBoxCellRendererPrivate;
49
50 struct _ModestVBoxCellRendererPrivate
51 {
52         GList *renderers_list;
53 };
54
55 #define MODEST_VBOX_CELL_RENDERER_GET_PRIVATE(o)        \
56         (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_VBOX_CELL_RENDERER, ModestVBoxCellRendererPrivate))
57
58 /* static guint signals[LAST_SIGNAL] = {0}; */
59
60 /* static functions: GObject */
61 static void modest_vbox_cell_renderer_instance_init (GTypeInstance *instance, gpointer g_class);
62 static void modest_vbox_cell_renderer_finalize (GObject *object);
63 static void modest_vbox_cell_renderer_class_init (ModestVBoxCellRendererClass *klass);
64
65 /* static functions: GtkCellRenderer */
66 static void modest_vbox_cell_renderer_get_size     (GtkCellRenderer       *cell,
67                                                     GtkWidget             *widget,
68                                                     GdkRectangle          *rectangle,
69                                                     gint                  *x_offset,
70                                                     gint                  *y_offset,
71                                                     gint                  *width,
72                                                     gint                  *height);
73 static void modest_vbox_cell_renderer_render       (GtkCellRenderer       *cell,
74                                                     GdkDrawable           *window,
75                                                     GtkWidget             *widget,
76                                                     GdkRectangle          *background_area,
77                                                     GdkRectangle          *cell_area,
78                                                     GdkRectangle          *expose_area,
79                                                     GtkCellRendererState  flags);
80                                                 
81
82 /**
83  * modest_vbox_cell_renderer_new:
84  *
85  * Return value: a new #ModestVBoxCellRenderer instance implemented for Gtk+
86  **/
87 GtkCellRenderer*
88 modest_vbox_cell_renderer_new (void)
89 {
90         ModestVBoxCellRenderer *self = g_object_new (MODEST_TYPE_VBOX_CELL_RENDERER, NULL);
91
92         return GTK_CELL_RENDERER (self);
93 }
94
95 static void
96 modest_vbox_cell_renderer_instance_init (GTypeInstance *instance, gpointer g_class)
97 {
98         ModestVBoxCellRendererPrivate *priv = MODEST_VBOX_CELL_RENDERER_GET_PRIVATE (instance);
99
100         priv->renderers_list = NULL;
101         
102         return;
103 }
104
105 static void
106 modest_vbox_cell_renderer_finalize (GObject *object)
107 {
108         ModestVBoxCellRendererPrivate *priv = MODEST_VBOX_CELL_RENDERER_GET_PRIVATE (object);
109
110         if (priv->renderers_list != NULL) {
111                 g_list_foreach (priv->renderers_list, (GFunc) g_object_unref, NULL);
112                 g_list_free (priv->renderers_list);
113                 priv->renderers_list = NULL;
114         }
115
116         (*parent_class->finalize) (object);
117
118         return;
119 }
120
121 static void 
122 modest_vbox_cell_renderer_class_init (ModestVBoxCellRendererClass *klass)
123 {
124         GObjectClass *object_class;
125         GtkCellRendererClass *renderer_class;
126
127         parent_class = g_type_class_peek_parent (klass);
128         object_class = (GObjectClass*) klass;
129         renderer_class = (GtkCellRendererClass*) klass;
130
131         object_class->finalize = modest_vbox_cell_renderer_finalize;
132         renderer_class->get_size = modest_vbox_cell_renderer_get_size;
133         renderer_class->render = modest_vbox_cell_renderer_render;
134
135         g_type_class_add_private (object_class, sizeof (ModestVBoxCellRendererPrivate));
136
137         return;
138 }
139
140 GType 
141 modest_vbox_cell_renderer_get_type (void)
142 {
143         static GType type = 0;
144
145         if (G_UNLIKELY(type == 0))
146         {
147                 static const GTypeInfo info = 
148                 {
149                   sizeof (ModestVBoxCellRendererClass),
150                   NULL,   /* base_init */
151                   NULL,   /* base_finalize */
152                   (GClassInitFunc) modest_vbox_cell_renderer_class_init,   /* class_init */
153                   NULL,   /* class_finalize */
154                   NULL,   /* class_data */
155                   sizeof (ModestVBoxCellRenderer),
156                   0,      /* n_preallocs */
157                   modest_vbox_cell_renderer_instance_init    /* instance_init */
158                 };
159
160                 type = g_type_register_static (GTK_TYPE_CELL_RENDERER,
161                         "ModestVBoxCellRenderer",
162                         &info, 0);
163
164         }
165
166         return type;
167 }
168
169
170 /**
171  * modest_vbox_cell_renderer_append:
172  * @vbox_renderer: a #ModestVBoxCellRenderer
173  * @cell: a #GtkCellRenderer
174  *
175  * Appends @cell to the end of the list of renderers shown in @vbox_renderer
176  */
177 void 
178 modest_vbox_cell_renderer_append (ModestVBoxCellRenderer *vbox_renderer, 
179                                   GtkCellRenderer *cell,
180                                   gboolean expand)
181 {
182         ModestVBoxCellRendererPrivate *priv = MODEST_VBOX_CELL_RENDERER_GET_PRIVATE (vbox_renderer);
183         
184         priv->renderers_list = g_list_append (priv->renderers_list, cell);
185         g_object_set_data (G_OBJECT (cell), RENDERER_EXPAND_ATTRIBUTE, GINT_TO_POINTER (expand));
186
187 #if GLIB_CHECK_VERSION(2, 10, 0) /* g_object_ref_sink() was added in glib 2.10: */
188         g_object_ref_sink (G_OBJECT (cell));
189 #else
190         g_object_ref (G_OBJECT (cell));
191         gtk_object_sink (GTK_OBJECT (cell));
192 #endif
193 }
194
195 static void 
196 modest_vbox_cell_renderer_get_size     (GtkCellRenderer       *cell,
197                                         GtkWidget             *widget,
198                                         GdkRectangle          *rectangle,
199                                         gint                  *x_offset,
200                                         gint                  *y_offset,
201                                         gint                  *width,
202                                         gint                  *height)
203 {
204         gint calc_width, calc_height;
205         gint full_width, full_height;
206         guint xpad, ypad;
207         gfloat xalign, yalign;
208         GList *node;
209         ModestVBoxCellRendererPrivate *priv = MODEST_VBOX_CELL_RENDERER_GET_PRIVATE (cell);
210
211         calc_width = 0;
212         calc_height = 0;
213
214         for (node = priv->renderers_list; node != NULL; node = g_list_next (node)) {
215                 gint renderer_width, renderer_height;
216                 GtkCellRenderer *renderer = (GtkCellRenderer *) node->data;
217
218                 gtk_cell_renderer_get_size (renderer, widget, NULL, NULL, NULL,
219                                             &renderer_width, &renderer_height);
220                 if ((renderer_width > 0)&&(renderer_height > 0)) {
221                         calc_width = MAX (calc_width, renderer_width);
222                         calc_height += renderer_height;
223                 }
224         }
225
226         g_object_get (cell,
227                       "xpad", &xpad,
228                       "ypad", &ypad,
229                       "xalign", &xalign,
230                       "yalign", &yalign,
231                       NULL);
232
233         full_width = (gint) xpad * 2 + calc_width;
234         full_height = (gint) ypad * 2 + calc_height;
235
236         if (rectangle && calc_width > 0 && calc_height > 0) {
237                 if (x_offset) {
238                         *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
239                                       (1.0 - xalign) : xalign) *
240                                      (rectangle->width - full_width));
241                         *x_offset = MAX (*x_offset, 0);
242                 }
243                 if (y_offset) {
244                         *y_offset = ((yalign) *
245                                      (rectangle->height - full_height));
246                         *y_offset = MAX (*y_offset, 0);
247                 }
248         } else {
249                 if (x_offset)
250                         *x_offset = 0;
251                 if (y_offset)
252                         *y_offset = 0;
253         }
254
255         if (width)
256                 *width = full_width;
257         if (height)
258                 *height = full_height;
259 }
260
261 static void 
262 modest_vbox_cell_renderer_render       (GtkCellRenderer       *cell,
263                                         GdkDrawable           *window,
264                                         GtkWidget             *widget,
265                                         GdkRectangle          *background_area,
266                                         GdkRectangle          *cell_area,
267                                         GdkRectangle          *expose_area,
268                                         GtkCellRendererState  flags)
269 {
270         ModestVBoxCellRendererPrivate *priv = MODEST_VBOX_CELL_RENDERER_GET_PRIVATE (cell);
271         gint nvis_children = 0;
272         gint nexpand_children = 0;
273         GtkTextDirection direction;
274         GList *node = NULL;
275         GtkCellRenderer *child;
276         gint height, extra;
277         
278         direction = gtk_widget_get_direction (widget);
279         nvis_children = 0;
280         nexpand_children = 0;
281
282
283         /* Counts visible and expandable children cell renderers */
284         for (node = priv->renderers_list; node != NULL; node = g_list_next (node)) {
285                 gboolean visible, expand;
286                 child = (GtkCellRenderer *) node->data;
287                 g_object_get (G_OBJECT (child), "visible", &visible, NULL);
288                 expand = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (child), RENDERER_EXPAND_ATTRIBUTE));
289                 
290                 if (visible) {
291                         nvis_children += 1;
292                         if (expand)
293                                 nexpand_children += 1;
294                 }
295         }
296
297         if (nvis_children > 0) {
298                 gint x_pad, y_pad;
299                 gint y;
300                 GdkRectangle child_alloc;
301
302                 if (nexpand_children > 0) {
303                         GtkRequisition req;
304                         
305                         modest_vbox_cell_renderer_get_size (cell, widget, NULL, NULL, NULL, &(req.width), &(req.height));
306                         height = cell_area->height - req.height;
307                         extra = height / nexpand_children;
308                 } else {
309                         height = 0;
310                         extra = 0;
311                 }
312
313                 g_object_get (cell, "xpad", &x_pad, "ypad", &y_pad, NULL);
314                 y = cell_area->y + y_pad;
315                 child_alloc.x = cell_area->x + x_pad;
316                 child_alloc.width = MAX (1, cell_area->width - x_pad * 2);
317
318                 for (node = priv->renderers_list; node != NULL; node = g_list_next (node)) {
319                         gboolean visible, expand;
320
321                         child = (GtkCellRenderer *) node->data;
322                         g_object_get (G_OBJECT (child), "visible", &visible, NULL);
323                         expand = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (child), RENDERER_EXPAND_ATTRIBUTE));
324
325                         if (visible) {
326                                 GtkRequisition child_req;
327                                 gint child_xpad, child_ypad;
328                                 GdkRectangle child_expose_area;
329
330                                 gtk_cell_renderer_get_size (child, widget, NULL, NULL, NULL, &(child_req.width), &(child_req.height));
331                                 g_object_get (child, "xpad", &child_xpad, "ypad", &child_ypad, NULL);
332
333                                 if (expand) {
334                                         if (nexpand_children == 1)
335                                                 child_req.height += height;
336                                         else
337                                                 child_req.height += extra;
338                                         nexpand_children -= 1;
339                                         height -= extra;
340                                 }
341
342                                 child_alloc.height = MAX (1, child_req.height);
343                                 child_alloc.y = y;
344
345                                 if (gdk_rectangle_intersect (&child_alloc, expose_area, &child_expose_area))
346                                         gtk_cell_renderer_render (child, window, widget, background_area, &child_alloc, &child_expose_area, flags);
347                                 y += child_req.height;
348                         }
349                 }
350         }
351         
352 }