bmalloc: Miscellaneous cleanup
[WebKit-https.git] / Tools / MiniBrowser / gtk / BrowserCellRendererVariant.c
1 /*
2  * Copyright (C) 2011 Igalia S.L.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "BrowserCellRendererVariant.h"
27 #include "BrowserMarshal.h"
28 #include <errno.h>
29
30 enum {
31     PROP_0,
32
33     PROP_VALUE,
34     PROP_ADJUSTMENT
35 };
36
37 enum {
38     CHANGED,
39
40     LAST_SIGNAL
41 };
42
43 struct _BrowserCellRendererVariant {
44     GtkCellRenderer parent;
45
46     GValue *value;
47
48     GtkCellRenderer *textRenderer;
49     GtkCellRenderer *toggleRenderer;
50     GtkCellRenderer *spinRenderer;
51 };
52
53 struct _BrowserCellRendererVariantClass {
54     GtkCellRendererClass parent;
55 };
56
57 static guint signals[LAST_SIGNAL] = { 0 };
58
59 G_DEFINE_TYPE(BrowserCellRendererVariant, browser_cell_renderer_variant, GTK_TYPE_CELL_RENDERER)
60
61 static void browserCellRendererVariantFinalize(GObject *object)
62 {
63     BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(object);
64
65     g_object_unref(renderer->toggleRenderer);
66     g_object_unref(renderer->spinRenderer);
67     g_object_unref(renderer->textRenderer);
68     if (renderer->value)
69         g_boxed_free(G_TYPE_VALUE, renderer->value);
70
71     G_OBJECT_CLASS(browser_cell_renderer_variant_parent_class)->finalize(object);
72 }
73
74 static void browserCellRendererVariantGetProperty(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
75 {
76     BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(object);
77
78     switch (propId) {
79     case PROP_VALUE:
80         g_value_set_boxed(value, renderer->value);
81         break;
82     case PROP_ADJUSTMENT: {
83         GtkAdjustment *adjustment = NULL;
84         g_object_get(G_OBJECT(renderer->spinRenderer), "adjustment", &adjustment, NULL);
85         if (adjustment) {
86             g_value_set_object(value, adjustment);
87             g_object_unref(adjustment);
88         }
89         break;
90     }
91     default:
92         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
93     }
94 }
95
96 static void browserCellRendererVariantSetModeForValue(BrowserCellRendererVariant *renderer)
97 {
98     if (!renderer->value)
99         return;
100
101     GtkCellRendererMode mode;
102     if (G_VALUE_HOLDS_BOOLEAN(renderer->value))
103         mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
104     else if (G_VALUE_HOLDS_STRING(renderer->value) || G_VALUE_HOLDS_UINT(renderer->value))
105         mode = GTK_CELL_RENDERER_MODE_EDITABLE;
106     else
107         return;
108
109     g_object_set(G_OBJECT(renderer), "mode", mode, NULL);
110 }
111
112 static void browserCellRendererVariantSetProperty(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
113 {
114     BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(object);
115
116     switch (propId) {
117     case PROP_VALUE:
118         if (renderer->value)
119             g_boxed_free(G_TYPE_VALUE, renderer->value);
120         renderer->value = g_value_dup_boxed(value);
121         browserCellRendererVariantSetModeForValue(renderer);
122         break;
123     case PROP_ADJUSTMENT:
124         g_object_set(G_OBJECT(renderer->spinRenderer), "adjustment", g_value_get_object(value), NULL);
125         break;
126     default:
127         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
128     }
129 }
130
131 static GtkCellRenderer *browserCellRendererVariantGetRendererForValue(BrowserCellRendererVariant *renderer)
132 {
133     if (!renderer->value)
134         return NULL;
135
136     if (G_VALUE_HOLDS_BOOLEAN(renderer->value)) {
137         g_object_set(G_OBJECT(renderer->toggleRenderer),
138                      "active", g_value_get_boolean(renderer->value),
139                      NULL);
140         return renderer->toggleRenderer;
141     }
142
143     if (G_VALUE_HOLDS_STRING(renderer->value)) {
144         g_object_set(G_OBJECT(renderer->textRenderer),
145                      "text", g_value_get_string(renderer->value),
146                      NULL);
147         return renderer->textRenderer;
148     }
149
150     if (G_VALUE_HOLDS_UINT(renderer->value)) {
151         gchar *text = g_strdup_printf("%u", g_value_get_uint(renderer->value));
152         g_object_set(G_OBJECT(renderer->spinRenderer), "text", text, NULL);
153         g_free(text);
154         return renderer->spinRenderer;
155     }
156
157     return NULL;
158 }
159
160 static void browserCellRendererVariantCellRendererTextEdited(BrowserCellRendererVariant *renderer, const gchar *path, const gchar *newText)
161 {
162     if (!renderer->value)
163         return;
164
165     if (!G_VALUE_HOLDS_STRING(renderer->value))
166         return;
167
168     g_value_set_string(renderer->value, newText);
169     g_signal_emit(renderer, signals[CHANGED], 0, path, renderer->value);
170 }
171
172 static void browserCellRendererVariantCellRendererSpinEdited(BrowserCellRendererVariant *renderer, const gchar *path, const gchar *newText)
173 {
174     if (!renderer->value)
175         return;
176
177     if (!G_VALUE_HOLDS_UINT(renderer->value))
178         return;
179
180     GtkAdjustment *adjustment;
181     g_object_get(G_OBJECT(renderer->spinRenderer), "adjustment", &adjustment, NULL);
182     if (!adjustment)
183         return;
184
185     errno = 0;
186     gchar *endPtr;
187     gdouble value = g_strtod(newText, &endPtr);
188     if (errno || value > gtk_adjustment_get_upper(adjustment) || value < gtk_adjustment_get_lower(adjustment) || endPtr == newText) {
189         g_warning("Invalid input for cell: %s\n", newText);
190         return;
191     }
192
193     g_value_set_uint(renderer->value, (guint)value);
194     g_signal_emit(renderer, signals[CHANGED], 0, path, renderer->value);
195 }
196
197 static gboolean browserCellRendererVariantCellRendererActivate(GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, const GdkRectangle *bgArea, const GdkRectangle *cellArea, GtkCellRendererState flags)
198 {
199     BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(cell);
200
201     if (!renderer->value)
202         return TRUE;
203
204     if (!G_VALUE_HOLDS_BOOLEAN(renderer->value))
205         return TRUE;
206
207     g_value_set_boolean(renderer->value, !g_value_get_boolean(renderer->value));
208     g_signal_emit(renderer, signals[CHANGED], 0, path, renderer->value);
209
210     return TRUE;
211 }
212
213 static void browserCellRendererVariantCellRendererRender(GtkCellRenderer *cell, cairo_t *cr, GtkWidget *widget, const GdkRectangle *bgArea, const GdkRectangle *cellArea, GtkCellRendererState flags)
214 {
215     GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
216     if (!renderer)
217         return;
218
219     GTK_CELL_RENDERER_GET_CLASS(renderer)->render(renderer, cr, widget, bgArea, cellArea, flags);
220 }
221
222 static GtkCellEditable *browserCellRendererVariantCellRendererStartEditing(GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, const GdkRectangle *bgArea, const GdkRectangle *cellArea, GtkCellRendererState flags)
223 {
224     GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
225     if (!renderer)
226         return NULL;
227
228     if (!GTK_CELL_RENDERER_GET_CLASS(renderer)->start_editing)
229         return NULL;
230
231     return GTK_CELL_RENDERER_GET_CLASS(renderer)->start_editing(renderer, event, widget, path, bgArea, cellArea, flags);
232 }
233
234 static void browserCellRendererVariantCellRendererGetPreferredWidth(GtkCellRenderer *cell, GtkWidget *widget, gint *minimumWidth, gint *naturalWidth)
235 {
236     GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
237     if (!renderer)
238         return;
239
240     GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_width(renderer, widget, minimumWidth, naturalWidth);
241 }
242
243 static void browserCellRendererVariantCellRendererGetPreferredHeight(GtkCellRenderer *cell, GtkWidget *widget, gint *minimumHeight, gint *naturalHeight)
244 {
245     GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
246     if (!renderer)
247         return;
248
249     GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_height(renderer, widget, minimumHeight, naturalHeight);
250 }
251
252 static void browserCellRendererVariantCellRendererGetPreferredWidthForHeight(GtkCellRenderer *cell, GtkWidget *widget, gint height, gint *minimumWidth, gint *naturalWidth)
253 {
254     GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
255     if (!renderer)
256         return;
257
258     GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_width_for_height(renderer, widget, height, minimumWidth, naturalWidth);
259 }
260
261 static void browserCellRendererVariantCellRendererGetPreferredHeightForWidth(GtkCellRenderer *cell, GtkWidget *widget, gint width, gint *minimumHeight, gint *naturalHeight)
262 {
263     GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
264     if (!renderer)
265         return;
266
267     GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_height_for_width(renderer, widget, width, minimumHeight, naturalHeight);
268 }
269
270 static void browserCellRendererVariantCellRendererGetAlignedArea(GtkCellRenderer *cell, GtkWidget *widget, GtkCellRendererState flags, const GdkRectangle *cellArea, GdkRectangle *alignedArea)
271 {
272     GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
273     if (!renderer)
274         return;
275
276     GTK_CELL_RENDERER_GET_CLASS(renderer)->get_aligned_area(renderer, widget, flags, cellArea, alignedArea);
277 }
278
279 static void browser_cell_renderer_variant_init(BrowserCellRendererVariant *renderer)
280 {
281     g_object_set(renderer, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
282
283     renderer->toggleRenderer = gtk_cell_renderer_toggle_new();
284     g_object_set(G_OBJECT(renderer->toggleRenderer), "xalign", 0.0, NULL);
285     g_object_ref_sink(renderer->toggleRenderer);
286
287     renderer->textRenderer = gtk_cell_renderer_text_new();
288     g_signal_connect_swapped(renderer->textRenderer, "edited",
289                              G_CALLBACK(browserCellRendererVariantCellRendererTextEdited), renderer);
290     g_object_set(G_OBJECT(renderer->textRenderer), "editable", TRUE, NULL);
291     g_object_ref_sink(renderer->textRenderer);
292
293     renderer->spinRenderer = gtk_cell_renderer_spin_new();
294     g_signal_connect_swapped(renderer->spinRenderer, "edited",
295                              G_CALLBACK(browserCellRendererVariantCellRendererSpinEdited), renderer);
296     g_object_set(G_OBJECT(renderer->spinRenderer), "editable", TRUE, NULL);
297 }
298
299 static void browser_cell_renderer_variant_class_init(BrowserCellRendererVariantClass *klass)
300 {
301     GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
302     GtkCellRendererClass *cellRendererClass = GTK_CELL_RENDERER_CLASS(klass);
303
304     gobjectClass->get_property = browserCellRendererVariantGetProperty;
305     gobjectClass->set_property = browserCellRendererVariantSetProperty;
306     gobjectClass->finalize = browserCellRendererVariantFinalize;
307
308     cellRendererClass->activate = browserCellRendererVariantCellRendererActivate;
309     cellRendererClass->render = browserCellRendererVariantCellRendererRender;
310     cellRendererClass->start_editing = browserCellRendererVariantCellRendererStartEditing;
311     cellRendererClass->get_preferred_width = browserCellRendererVariantCellRendererGetPreferredWidth;
312     cellRendererClass->get_preferred_height = browserCellRendererVariantCellRendererGetPreferredHeight;
313     cellRendererClass->get_preferred_width_for_height = browserCellRendererVariantCellRendererGetPreferredWidthForHeight;
314     cellRendererClass->get_preferred_height_for_width = browserCellRendererVariantCellRendererGetPreferredHeightForWidth;
315     cellRendererClass->get_aligned_area = browserCellRendererVariantCellRendererGetAlignedArea;
316
317     g_object_class_install_property(gobjectClass,
318                                     PROP_VALUE,
319                                     g_param_spec_boxed("value",
320                                                        "Value",
321                                                        "The cell renderer value",
322                                                        G_TYPE_VALUE,
323                                                        G_PARAM_READWRITE));
324     g_object_class_install_property(gobjectClass,
325                                     PROP_ADJUSTMENT,
326                                     g_param_spec_object("adjustment",
327                                                         "Adjustment",
328                                                         "The adjustment that holds the value of the spin button",
329                                                         GTK_TYPE_ADJUSTMENT,
330                                                         G_PARAM_READWRITE));
331
332     signals[CHANGED] =
333         g_signal_new("changed",
334                      G_TYPE_FROM_CLASS(gobjectClass),
335                      G_SIGNAL_RUN_LAST,
336                      0, NULL, NULL,
337                      browser_marshal_VOID__STRING_BOXED,
338                      G_TYPE_NONE, 2,
339                      G_TYPE_STRING, G_TYPE_VALUE);
340 }
341
342 GtkCellRenderer *browser_cell_renderer_variant_new(void)
343 {
344     return GTK_CELL_RENDERER(g_object_new(BROWSER_TYPE_CELL_RENDERER_VARIANT, NULL));
345 }
346