1 /*
2 * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org>
3 * Copyright (C) 2010 David King <davidk@openismus.com>
4 * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include <gdk/gdkkeysyms.h>
23 #include "gdaui-entry-shell.h"
24 #include "gdaui-entry-none.h"
25 #include <libgda/gda-data-handler.h>
26 #include <libgda-ui/internal/utility.h>
27 #include <glib/gi18n-lib.h>
28 #include "widget-embedder.h"
29 static void gdaui_entry_shell_class_init (GdauiEntryShellClass *class);
30 static void gdaui_entry_shell_init (GdauiEntryShell *wid);
31 static void gdaui_entry_shell_dispose (GObject *object);
32
33 static void gdaui_entry_shell_set_property (GObject *object,
34 guint param_id,
35 const GValue *value,
36 GParamSpec *pspec);
37 static void gdaui_entry_shell_get_property (GObject *object,
38 guint param_id,
39 GValue *value,
40 GParamSpec *pspec);
41
42
43 static gint event_cb (GtkWidget *widget, GdkEvent *event, GdauiEntryShell *shell);
44 static void show_all (GtkWidget *widget);
45 static void contents_modified_cb (GdauiEntryShell *shell, gpointer unused);
46 static void gdaui_entry_shell_refresh_status_display (GdauiEntryShell *shell);
47
48 /* properties */
49 enum {
50 PROP_0,
51 PROP_HANDLER,
52 PROP_ACTIONS,
53 PROP_IS_CELL_RENDERER
54 };
55
56 struct _GdauiEntryShellPriv {
57 GtkWidget *embedder;
58 GtkWidget *hbox;
59 GtkWidget *button;
60 GdaDataHandler *data_handler;
61 gboolean show_actions;
62
63 gboolean value_is_null;
64 gboolean value_is_modified;
65 gboolean value_is_default;
66 gboolean value_is_non_valid;
67
68 gboolean is_cell_renderer;
69 };
70
71 /* get a pointer to the parents to be able to call their destructor */
72 static GObjectClass *parent_class = NULL;
73
74 /**
75 * gdaui_entry_shell_get_type:
76 *
77 * Register the GdauiEntryShell class on the GLib type system.
78 *
79 * Returns: the GType identifying the class.
80 */
81 GType
gdaui_entry_shell_get_type(void)82 gdaui_entry_shell_get_type (void)
83 {
84 static GType type = 0;
85
86 if (G_UNLIKELY (type == 0)) {
87 static const GTypeInfo info = {
88 sizeof (GdauiEntryShellClass),
89 (GBaseInitFunc) NULL,
90 (GBaseFinalizeFunc) NULL,
91 (GClassInitFunc) gdaui_entry_shell_class_init,
92 NULL,
93 NULL,
94 sizeof (GdauiEntryShell),
95 0,
96 (GInstanceInitFunc) gdaui_entry_shell_init,
97 0
98 };
99
100 type = g_type_register_static (GTK_TYPE_VIEWPORT, "GdauiEntryShell", &info, 0);
101 }
102 return type;
103 }
104
105
106 static void
gdaui_entry_shell_class_init(GdauiEntryShellClass * class)107 gdaui_entry_shell_class_init (GdauiEntryShellClass * class)
108 {
109 GObjectClass *object_class = G_OBJECT_CLASS (class);
110 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
111
112 parent_class = g_type_class_peek_parent (class);
113
114 object_class->dispose = gdaui_entry_shell_dispose;
115 widget_class->show_all = show_all;
116
117 /* Properties */
118 object_class->set_property = gdaui_entry_shell_set_property;
119 object_class->get_property = gdaui_entry_shell_get_property;
120 g_object_class_install_property (object_class, PROP_HANDLER,
121 g_param_spec_object ("handler", NULL, NULL, GDA_TYPE_DATA_HANDLER,
122 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
123 g_object_class_install_property (object_class, PROP_ACTIONS,
124 g_param_spec_boolean ("actions", NULL, NULL, TRUE,
125 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
126
127 g_object_class_install_property (object_class, PROP_IS_CELL_RENDERER,
128 g_param_spec_boolean ("is-cell-renderer", NULL, NULL, TRUE,
129 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
130 }
131
132 static void
show_all(GtkWidget * widget)133 show_all (GtkWidget *widget)
134 {
135 if (((GdauiEntryShell*) widget)->priv->show_actions)
136 gtk_widget_show (((GdauiEntryShell*) widget)->priv->button);
137 }
138
139 static void
gdaui_entry_shell_init(GdauiEntryShell * shell)140 gdaui_entry_shell_init (GdauiEntryShell *shell)
141 {
142 GtkWidget *button, *hbox, *arrow;
143 GValue *gval;
144
145 /* Private structure */
146 shell->priv = g_new0 (GdauiEntryShellPriv, 1);
147 shell->priv->embedder = NULL;
148 shell->priv->button = NULL;
149 shell->priv->show_actions = TRUE;
150 shell->priv->data_handler = NULL;
151
152 shell->priv->value_is_null = FALSE;
153 shell->priv->value_is_modified = FALSE;
154 shell->priv->value_is_default = FALSE;
155 shell->priv->value_is_non_valid = FALSE;
156
157 shell->priv->is_cell_renderer = FALSE;
158
159 /* Setting the initial layout */
160 gtk_viewport_set_shadow_type (GTK_VIEWPORT (shell), GTK_SHADOW_NONE);
161 gtk_container_set_border_width (GTK_CONTAINER (shell), 0);
162
163 /* hbox */
164 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
165 gtk_container_add (GTK_CONTAINER (shell), hbox);
166 gtk_widget_show (hbox);
167 shell->priv->hbox = hbox;
168
169 /* vbox to insert the real widget to edit data */
170 shell->priv->embedder = widget_embedder_new ();
171 gtk_box_pack_start (GTK_BOX (hbox), shell->priv->embedder, TRUE, TRUE, 0);
172 gtk_widget_show (shell->priv->embedder);
173
174 /* button to change the entry's state and to display that state */
175 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
176 button = gtk_button_new ();
177 gtk_container_add (GTK_CONTAINER (button), arrow);
178 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
179 shell->priv->button = button;
180 gtk_widget_show_all (button);
181
182 g_signal_connect (G_OBJECT (button), "event",
183 G_CALLBACK (event_cb), shell);
184
185 /* focus */
186 gval = g_new0 (GValue, 1);
187 g_value_init (gval, G_TYPE_BOOLEAN);
188 g_value_set_boolean (gval, TRUE);
189 g_object_set_property (G_OBJECT (button), "can-focus", gval);
190 g_free (gval);
191 }
192
193 static void
gdaui_entry_shell_dispose(GObject * object)194 gdaui_entry_shell_dispose (GObject * object)
195 {
196 GdauiEntryShell *shell;
197
198 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (object));
199
200 shell = GDAUI_ENTRY_SHELL (object);
201
202 if (shell->priv) {
203 if (shell->priv->data_handler)
204 g_object_unref (shell->priv->data_handler);
205
206 g_free (shell->priv);
207 shell->priv = NULL;
208 }
209
210 /* for the parent class */
211 parent_class->dispose (object);
212 }
213
214 static void
gdaui_entry_shell_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)215 gdaui_entry_shell_set_property (GObject *object,
216 guint param_id,
217 const GValue *value,
218 GParamSpec *pspec)
219 {
220 gpointer ptr;
221 GdauiEntryShell *shell;
222
223 shell = GDAUI_ENTRY_SHELL (object);
224 if (shell->priv) {
225 switch (param_id) {
226 case PROP_HANDLER:
227 ptr = g_value_get_object (value);
228 if (shell->priv->data_handler) {
229 g_object_unref (shell->priv->data_handler);
230 shell->priv->data_handler = NULL;
231 }
232
233 if (ptr) {
234 shell->priv->data_handler = GDA_DATA_HANDLER (ptr);
235 g_object_ref (G_OBJECT (shell->priv->data_handler));
236 }
237 else
238 g_message (_("Widget of class '%s' does not have any associated GdaDataHandler, "
239 "(to be set using the 'handler' property) expect some mis-behaviours"),
240 G_OBJECT_TYPE_NAME (object));
241 break;
242 case PROP_ACTIONS:
243 shell->priv->show_actions = g_value_get_boolean (value);
244 if (shell->priv->show_actions)
245 gtk_widget_show (shell->priv->button);
246 else
247 gtk_widget_hide (shell->priv->button);
248 break;
249 case PROP_IS_CELL_RENDERER:
250 if (GTK_IS_CELL_EDITABLE (shell) &&
251 (g_value_get_boolean (value) != shell->priv->is_cell_renderer)) {
252 shell->priv->is_cell_renderer = g_value_get_boolean (value);
253 }
254 break;
255 default:
256 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
257 break;
258 }
259 }
260 }
261
262 static void
gdaui_entry_shell_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)263 gdaui_entry_shell_get_property (GObject *object,
264 guint param_id,
265 GValue *value,
266 GParamSpec *pspec)
267 {
268 GdauiEntryShell *shell;
269
270 shell = GDAUI_ENTRY_SHELL (object);
271 if (shell->priv) {
272 switch (param_id) {
273 case PROP_HANDLER:
274 g_value_set_object (value, shell->priv->data_handler);
275 break;
276 case PROP_ACTIONS:
277 g_value_set_boolean (value, shell->priv->show_actions);
278 break;
279 case PROP_IS_CELL_RENDERER:
280 g_value_set_boolean (value, shell->priv->is_cell_renderer);
281 break;
282 default:
283 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
284 break;
285 }
286 }
287 }
288
289
290 /**
291 * gdaui_entry_shell_pack_entry:
292 * @shell: a #GdauiEntryShell object
293 * @main_widget: a #GtkWidget to pack into @shell
294 *
295 * Packs a #GTkWidget widget into the GdauiEntryShell.
296 */
297 void
gdaui_entry_shell_pack_entry(GdauiEntryShell * shell,GtkWidget * main_widget)298 gdaui_entry_shell_pack_entry (GdauiEntryShell *shell, GtkWidget *main_widget)
299 {
300 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (shell));
301 g_return_if_fail (main_widget && GTK_IS_WIDGET (main_widget));
302 gtk_container_add (GTK_CONTAINER (shell->priv->embedder), main_widget);
303
304 /* signals */
305 g_signal_connect (G_OBJECT (shell), "contents-modified",
306 G_CALLBACK (contents_modified_cb), NULL);
307
308 g_signal_connect (G_OBJECT (shell), "status-changed",
309 G_CALLBACK (contents_modified_cb), NULL);
310 }
311
312 static void
contents_modified_cb(GdauiEntryShell * shell,G_GNUC_UNUSED gpointer unused)313 contents_modified_cb (GdauiEntryShell *shell, G_GNUC_UNUSED gpointer unused)
314 {
315 gdaui_entry_shell_refresh (shell);
316 }
317
318 static void mitem_activated_cb (GtkWidget *mitem, GdauiEntryShell *shell);
319 static GdaValueAttribute gdaui_entry_shell_refresh_attributes (GdauiEntryShell *shell);
320 static gint
event_cb(G_GNUC_UNUSED GtkWidget * widget,GdkEvent * event,GdauiEntryShell * shell)321 event_cb (G_GNUC_UNUSED GtkWidget *widget, GdkEvent *event, GdauiEntryShell *shell)
322 {
323 gboolean done = FALSE;
324
325 if (!shell->priv->show_actions)
326 return done;
327
328 if (event->type == GDK_BUTTON_PRESS) {
329 GdkEventButton *bevent = (GdkEventButton *) event;
330 if ((bevent->button == 1) || (bevent->button == 3)) {
331 GtkWidget *menu;
332 guint attributes;
333
334 attributes = gdaui_entry_shell_refresh_attributes (shell);
335 menu = _gdaui_utility_entry_build_actions_menu (G_OBJECT (shell), attributes,
336 G_CALLBACK (mitem_activated_cb));
337 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
338 bevent->button, bevent->time);
339 done = TRUE;
340 }
341 }
342
343 if (event->type == GDK_KEY_PRESS) {
344 GtkWidget *menu;
345 GdkEventKey *kevent = (GdkEventKey *) event;
346
347 if (kevent->keyval == GDK_KEY_space) {
348 guint attributes;
349
350 attributes = gdaui_entry_shell_refresh_attributes (shell);
351 menu = _gdaui_utility_entry_build_actions_menu (G_OBJECT (shell), attributes,
352 G_CALLBACK (mitem_activated_cb));
353
354 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
355 0, kevent->time);
356 done = TRUE;
357 }
358 else {
359 if (kevent->keyval == GDK_KEY_Tab)
360 done = FALSE;
361 else
362 done = TRUE;
363 }
364 }
365
366 return done;
367 }
368
369 static void
mitem_activated_cb(GtkWidget * mitem,GdauiEntryShell * shell)370 mitem_activated_cb (GtkWidget *mitem, GdauiEntryShell *shell)
371 {
372 guint action;
373
374 action = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (mitem), "action"));
375 gdaui_data_entry_set_attributes (GDAUI_DATA_ENTRY (shell), action, action);
376 }
377
378 static void
gdaui_entry_shell_refresh_status_display(GdauiEntryShell * shell)379 gdaui_entry_shell_refresh_status_display (GdauiEntryShell *shell)
380 {
381 static GdkRGBA **colors = NULL;
382 GdkRGBA *normal = NULL, *prelight = NULL;
383
384 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (shell));
385
386 if (!colors)
387 colors = _gdaui_utility_entry_build_info_colors_array_a ();
388
389 gtk_widget_set_tooltip_text (shell->priv->button, NULL);
390
391 if (shell->priv->value_is_null) {
392 normal = colors[0];
393 prelight = colors[1];
394 gtk_widget_set_tooltip_text (shell->priv->button, _("Value is NULL"));
395 }
396
397 if (shell->priv->value_is_default) {
398 normal = colors[2];
399 prelight = colors[3];
400 gtk_widget_set_tooltip_text (shell->priv->button, _("Value will be determined by default"));
401 }
402
403 if (shell->priv->value_is_non_valid) {
404 normal = colors[4];
405 prelight = colors[5];
406 gtk_widget_set_tooltip_text (shell->priv->button, _("Value is invalid"));
407 }
408
409 gtk_widget_override_background_color (shell->priv->button, GTK_STATE_FLAG_NORMAL, normal);
410 gtk_widget_override_background_color (shell->priv->button, GTK_STATE_FLAG_ACTIVE, normal);
411 gtk_widget_override_background_color (shell->priv->button, GTK_STATE_FLAG_PRELIGHT, prelight);
412 }
413
414 static GdaValueAttribute
gdaui_entry_shell_refresh_attributes(GdauiEntryShell * shell)415 gdaui_entry_shell_refresh_attributes (GdauiEntryShell *shell)
416 {
417 GdaValueAttribute attrs;
418
419 attrs = gdaui_data_entry_get_attributes (GDAUI_DATA_ENTRY (shell));
420 shell->priv->value_is_null = attrs & GDA_VALUE_ATTR_IS_NULL;
421 shell->priv->value_is_modified = ! (attrs & GDA_VALUE_ATTR_IS_UNCHANGED);
422 shell->priv->value_is_default = attrs & GDA_VALUE_ATTR_IS_DEFAULT;
423 shell->priv->value_is_non_valid = attrs & GDA_VALUE_ATTR_DATA_NON_VALID;
424
425 return attrs;
426 }
427
428 /**
429 * gdaui_entry_shell_refresh:
430 * @shell: the GdauiEntryShell widget to refresh
431 *
432 * Forces the shell to refresh its display (mainly the color of the
433 * button).
434 */
435 void
gdaui_entry_shell_refresh(GdauiEntryShell * shell)436 gdaui_entry_shell_refresh (GdauiEntryShell *shell)
437 {
438 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (shell));
439 gdaui_entry_shell_refresh_attributes (shell);
440 gdaui_entry_shell_refresh_status_display (shell);
441 }
442
443 /**
444 * gdaui_entry_shell_set_unknown:
445 * @shell: the #GdauiEntryShell widget to refresh
446 * @unknown: set to %TRUE if @shell's contents is unavailable and should not be modified
447 *
448 * Defines if @shell's contents is in an undefined state (shows or hides @shell's contents)
449 */
450 void
gdaui_entry_shell_set_unknown(GdauiEntryShell * shell,gboolean unknown)451 gdaui_entry_shell_set_unknown (GdauiEntryShell *shell, gboolean unknown)
452 {
453 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (shell));
454
455 widget_embedder_set_valid ((WidgetEmbedder*) shell->priv->embedder, !unknown);
456 }
457
458 /**
459 * gdaui_entry_shell_set_ucolor:
460 * @shell: a #GdauiEntryShell
461 * @red: the red component of a color
462 * @green: the green component of a color
463 * @blue: the blue component of a color
464 * @alpha: the alpha component of a color
465 *
466 * Defines the color to be used when @de displays an invalid value. Any value not
467 * between 0. and 1. will result in the default hard coded values to be used (grayish).
468 *
469 * Since: 5.0.3
470 */
471 void
gdaui_entry_shell_set_ucolor(GdauiEntryShell * shell,gdouble red,gdouble green,gdouble blue,gdouble alpha)472 gdaui_entry_shell_set_ucolor (GdauiEntryShell *shell, gdouble red, gdouble green,
473 gdouble blue, gdouble alpha)
474 {
475 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (shell));
476 widget_embedder_set_ucolor ((WidgetEmbedder*) shell->priv->embedder, red, green, blue, alpha);
477 }
478