1 /*
2 * Copyright (C) 2009 - 2011 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 <glib/gi18n-lib.h>
23 #include <libgda/binreloc/gda-binreloc.h>
24 #include "gdaui-entry-bin.h"
25 #include "common-bin.h"
26
27 /*
28 * Main static functions
29 */
30 static void gdaui_entry_bin_class_init (GdauiEntryBinClass * class);
31 static void gdaui_entry_bin_init (GdauiEntryBin * srv);
32 static void gdaui_entry_bin_dispose (GObject * object);
33 static void gdaui_entry_bin_finalize (GObject * object);
34
35 /* virtual functions */
36 static GtkWidget *create_entry (GdauiEntryWrapper *mgwrap);
37 static void real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value);
38 static GValue *real_get_value (GdauiEntryWrapper *mgwrap);
39 static void connect_signals(GdauiEntryWrapper *mgwrap, GCallback modify_cb, GCallback activate_cb);
40 static void set_editable (GdauiEntryWrapper *mgwrap, gboolean editable);
41 static void grab_focus (GdauiEntryWrapper *mgwrap);
42
43 static void show (GtkWidget *widget);
44
45 /* get a pointer to the parents to be able to call their destructor */
46 static GObjectClass *parent_class = NULL;
47 static GdkPixbuf *attach_pixbuf = NULL;
48
49 /* private structure */
50 struct _GdauiEntryBinPrivate
51 {
52 GtkWidget *button;
53 GtkWidget *button_hbox;
54 GtkWidget *button_label; /* ref held! */
55 GtkWidget *button_image; /* ref held! */
56
57 BinMenu menu;
58 gboolean editable;
59
60 GValue *current_data;
61 };
62
63
64 GType
gdaui_entry_bin_get_type(void)65 gdaui_entry_bin_get_type (void)
66 {
67 static GType type = 0;
68
69 if (G_UNLIKELY (type == 0)) {
70 static const GTypeInfo info = {
71 sizeof (GdauiEntryBinClass),
72 (GBaseInitFunc) NULL,
73 (GBaseFinalizeFunc) NULL,
74 (GClassInitFunc) gdaui_entry_bin_class_init,
75 NULL,
76 NULL,
77 sizeof (GdauiEntryBin),
78 0,
79 (GInstanceInitFunc) gdaui_entry_bin_init,
80 0
81 };
82
83 type = g_type_register_static (GDAUI_TYPE_ENTRY_WRAPPER, "GdauiEntryBin", &info, 0);
84 }
85 return type;
86 }
87
88 static void
gdaui_entry_bin_class_init(GdauiEntryBinClass * class)89 gdaui_entry_bin_class_init (GdauiEntryBinClass *class)
90 {
91 GObjectClass *object_class = G_OBJECT_CLASS (class);
92
93 parent_class = g_type_class_peek_parent (class);
94
95 object_class->dispose = gdaui_entry_bin_dispose;
96 object_class->finalize = gdaui_entry_bin_finalize;
97
98 GDAUI_ENTRY_WRAPPER_CLASS (class)->create_entry = create_entry;
99 GDAUI_ENTRY_WRAPPER_CLASS (class)->real_set_value = real_set_value;
100 GDAUI_ENTRY_WRAPPER_CLASS (class)->real_get_value = real_get_value;
101 GDAUI_ENTRY_WRAPPER_CLASS (class)->connect_signals = connect_signals;
102 GDAUI_ENTRY_WRAPPER_CLASS (class)->set_editable = set_editable;
103 GDAUI_ENTRY_WRAPPER_CLASS (class)->grab_focus = grab_focus;
104
105 GTK_WIDGET_CLASS (class)->show = show;
106
107 if (! attach_pixbuf) {
108 gchar *tmp;
109 tmp = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "pixmaps", "bin-attachment-16x16.png", NULL);
110 attach_pixbuf = gdk_pixbuf_new_from_file (tmp, NULL);
111 if (!attach_pixbuf)
112 g_warning ("Could not find icon file %s", tmp);
113 g_free (tmp);
114 }
115 }
116
117 static void
gdaui_entry_bin_init(GdauiEntryBin * gdaui_entry_bin)118 gdaui_entry_bin_init (GdauiEntryBin * gdaui_entry_bin)
119 {
120 gdaui_entry_bin->priv = g_new0 (GdauiEntryBinPrivate, 1);
121 gdaui_entry_bin->priv->button = NULL;
122 gdaui_entry_bin->priv->current_data = NULL;
123 gdaui_entry_bin->priv->editable = TRUE;
124 }
125
126 static void
show(GtkWidget * widget)127 show (GtkWidget *widget)
128 {
129 GValue *value;
130 GdauiEntryBin *dbin;
131
132 ((GtkWidgetClass *)parent_class)->show (widget);
133
134 dbin = GDAUI_ENTRY_BIN (widget);
135 value = dbin->priv->current_data;
136 if (value && (G_VALUE_TYPE (value) != GDA_TYPE_NULL)) {
137 gtk_widget_show (dbin->priv->button_image);
138 gtk_widget_hide (dbin->priv->button_label);
139 }
140 else {
141 gtk_widget_hide (dbin->priv->button_image);
142 gtk_widget_show (dbin->priv->button_label);
143 }
144 }
145
146 /**
147 * gdaui_entry_bin_new:
148 * @dh: the data handler to be used by the new widget
149 * @type: the requested data type (compatible with @dh)
150 *
151 * Creates a new widget which is mainly a #GtkEntry
152 *
153 * Returns: (transfer full): the new widget
154 */
155 GtkWidget *
gdaui_entry_bin_new(GdaDataHandler * dh,GType type)156 gdaui_entry_bin_new (GdaDataHandler *dh, GType type)
157 {
158 GObject *obj;
159 GdauiEntryBin *dbin;
160
161 g_return_val_if_fail (GDA_IS_DATA_HANDLER (dh), NULL);
162 g_return_val_if_fail (gda_data_handler_accepts_g_type (dh, type), NULL);
163
164 obj = g_object_new (GDAUI_TYPE_ENTRY_BIN, "handler", dh, NULL);
165 dbin = GDAUI_ENTRY_BIN (obj);
166 gdaui_data_entry_set_value_type (GDAUI_DATA_ENTRY (dbin), type);
167
168 return GTK_WIDGET (obj);
169 }
170
171
172 static void
gdaui_entry_bin_dispose(GObject * object)173 gdaui_entry_bin_dispose (GObject * object)
174 {
175 GdauiEntryBin *gdaui_entry_bin;
176
177 g_return_if_fail (object != NULL);
178 g_return_if_fail (GDAUI_IS_ENTRY_BIN (object));
179
180 gdaui_entry_bin = GDAUI_ENTRY_BIN (object);
181 if (gdaui_entry_bin->priv) {
182 if (gdaui_entry_bin->priv->current_data) {
183 gda_value_free (gdaui_entry_bin->priv->current_data);
184 gdaui_entry_bin->priv->current_data = NULL;
185 }
186 common_bin_reset (&(gdaui_entry_bin->priv->menu));
187
188 if (gdaui_entry_bin->priv->button_label) {
189 g_object_unref (gdaui_entry_bin->priv->button_label);
190 gdaui_entry_bin->priv->button_label = NULL;
191 }
192
193 if (gdaui_entry_bin->priv->button_image) {
194 g_object_unref (gdaui_entry_bin->priv->button_image);
195 gdaui_entry_bin->priv->button_image = NULL;
196 }
197 }
198
199 /* parent class */
200 parent_class->dispose (object);
201 }
202
203 static void
gdaui_entry_bin_finalize(GObject * object)204 gdaui_entry_bin_finalize (GObject * object)
205 {
206 GdauiEntryBin *gdaui_entry_bin;
207
208 g_return_if_fail (object != NULL);
209 g_return_if_fail (GDAUI_IS_ENTRY_BIN (object));
210
211 gdaui_entry_bin = GDAUI_ENTRY_BIN (object);
212 if (gdaui_entry_bin->priv) {
213
214 g_free (gdaui_entry_bin->priv);
215 gdaui_entry_bin->priv = NULL;
216 }
217
218 /* parent class */
219 parent_class->finalize (object);
220 }
221
222 static GtkWidget *
create_entry(GdauiEntryWrapper * mgwrap)223 create_entry (GdauiEntryWrapper *mgwrap)
224 {
225 GtkWidget *button, *arrow, *label, *img;
226 GdauiEntryBin *dbin;
227 GtkWidget *hbox;
228
229 g_return_val_if_fail (GDAUI_IS_ENTRY_BIN (mgwrap), NULL);
230 dbin = GDAUI_ENTRY_BIN (mgwrap);
231 g_return_val_if_fail (dbin->priv, NULL);
232
233 button = gtk_button_new ();
234 dbin->priv->button = button;
235
236 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
237 gtk_container_add (GTK_CONTAINER (button), hbox);
238 dbin->priv->button_hbox = hbox;
239
240 label = gtk_label_new ("");
241 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
242 dbin->priv->button_label = g_object_ref (G_OBJECT (label));
243
244 img = gtk_image_new_from_pixbuf (attach_pixbuf);
245 gtk_box_pack_start (GTK_BOX (hbox), img, FALSE, FALSE, 0);
246 dbin->priv->button_image = g_object_ref (G_OBJECT (img));
247
248 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
249 gtk_misc_set_alignment (GTK_MISC (arrow), 1.0, -1);
250 gtk_box_pack_start (GTK_BOX (hbox), arrow, TRUE, TRUE, 0);
251
252 gtk_widget_show_all (hbox);
253 gtk_widget_hide (dbin->priv->button_label);
254
255 return button;
256 }
257
258 /*
259 * WARNING:
260 * Does NOT emit any signal
261 */
262 static void
take_current_value(GdauiEntryBin * dbin,GValue * value)263 take_current_value (GdauiEntryBin *dbin, GValue *value)
264 {
265 /* clear previous situation */
266 if (dbin->priv->current_data) {
267 gda_value_free (dbin->priv->current_data);
268 dbin->priv->current_data = NULL;
269 }
270
271 /* new situation */
272 dbin->priv->current_data = value;
273 if (value && (G_VALUE_TYPE (value) != GDA_TYPE_NULL)) {
274 gtk_widget_show (dbin->priv->button_image);
275 gtk_widget_hide (dbin->priv->button_label);
276 }
277 else {
278 gtk_widget_hide (dbin->priv->button_image);
279 gtk_widget_show (dbin->priv->button_label);
280 }
281
282 common_bin_adjust_menu (&(dbin->priv->menu), dbin->priv->editable, value);
283 }
284
285 static void
real_set_value(GdauiEntryWrapper * mgwrap,const GValue * value)286 real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value)
287 {
288 GdauiEntryBin *dbin;
289
290 g_return_if_fail (GDAUI_IS_ENTRY_BIN (mgwrap));
291 dbin = GDAUI_ENTRY_BIN (mgwrap);
292 g_return_if_fail (dbin->priv);
293
294 take_current_value (dbin, value ? gda_value_copy (value) : NULL);
295 }
296
297 static GValue *
real_get_value(GdauiEntryWrapper * mgwrap)298 real_get_value (GdauiEntryWrapper *mgwrap)
299 {
300 GdauiEntryBin *dbin;
301
302 g_return_val_if_fail (GDAUI_IS_ENTRY_BIN (mgwrap), NULL);
303 dbin = GDAUI_ENTRY_BIN (mgwrap);
304 g_return_val_if_fail (dbin->priv, NULL);
305
306 if (dbin->priv->current_data)
307 return gda_value_copy (dbin->priv->current_data);
308 else
309 return gda_value_new_null ();
310 }
311
312
313 static void
value_loaded_cb(GdauiEntryBin * dbin,GValue * new_value)314 value_loaded_cb (GdauiEntryBin *dbin, GValue *new_value)
315 {
316 take_current_value (dbin, new_value);
317
318 /* signal changes */
319 gdaui_entry_wrapper_contents_changed (GDAUI_ENTRY_WRAPPER (dbin));
320 gdaui_entry_wrapper_contents_activated (GDAUI_ENTRY_WRAPPER (dbin));
321 }
322
323 static void
popup_position(PopupContainer * container,gint * out_x,gint * out_y)324 popup_position (PopupContainer *container, gint *out_x, gint *out_y)
325 {
326 GtkWidget *poswidget;
327 poswidget = g_object_get_data (G_OBJECT (container), "__poswidget");
328
329 gint x, y;
330 gdk_window_get_origin (gtk_widget_get_window (poswidget), &x, &y);
331 GtkAllocation alloc;
332 gtk_widget_get_allocation (poswidget, &alloc);
333 x += alloc.x;
334 y += alloc.y;
335 y += alloc.height;
336
337 if (x < 0)
338 x = 0;
339
340 if (y < 0)
341 y = 0;
342
343 *out_x = x;
344 *out_y = y;
345 }
346
347 static void
button_clicked_cb(GtkWidget * button,GdauiEntryBin * dbin)348 button_clicked_cb (GtkWidget *button, GdauiEntryBin *dbin)
349 {
350 if (!dbin->priv->menu.popup) {
351 common_bin_create_menu (&(dbin->priv->menu), popup_position,
352 gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (dbin)),
353 (BinCallback) value_loaded_cb, dbin);
354 g_object_set_data (G_OBJECT (dbin->priv->menu.popup), "__poswidget", button);
355 }
356
357 common_bin_adjust_menu (&(dbin->priv->menu), dbin->priv->editable, dbin->priv->current_data);
358 gtk_widget_show (dbin->priv->menu.popup);
359 }
360
361 static void
connect_signals(GdauiEntryWrapper * mgwrap,G_GNUC_UNUSED GCallback modify_cb,G_GNUC_UNUSED GCallback activate_cb)362 connect_signals (GdauiEntryWrapper *mgwrap, G_GNUC_UNUSED GCallback modify_cb,
363 G_GNUC_UNUSED GCallback activate_cb)
364 {
365 GdauiEntryBin *dbin;
366
367 g_return_if_fail (GDAUI_IS_ENTRY_BIN (mgwrap));
368 dbin = GDAUI_ENTRY_BIN (mgwrap);
369 g_return_if_fail (dbin->priv);
370
371 g_signal_connect (G_OBJECT (dbin->priv->button), "clicked",
372 G_CALLBACK (button_clicked_cb), dbin);
373 }
374
375 static void
set_editable(GdauiEntryWrapper * mgwrap,gboolean editable)376 set_editable (GdauiEntryWrapper *mgwrap, gboolean editable)
377 {
378 GdauiEntryBin *dbin;
379
380 g_return_if_fail (GDAUI_IS_ENTRY_BIN (mgwrap));
381 dbin = GDAUI_ENTRY_BIN (mgwrap);
382 g_return_if_fail (dbin->priv);
383
384 dbin->priv->editable = editable;
385 common_bin_adjust_menu (&(dbin->priv->menu), editable, dbin->priv->current_data);
386 }
387
388 static void
grab_focus(GdauiEntryWrapper * mgwrap)389 grab_focus (GdauiEntryWrapper *mgwrap)
390 {
391 GdauiEntryBin *dbin;
392
393 g_return_if_fail (GDAUI_IS_ENTRY_BIN (mgwrap));
394 dbin = GDAUI_ENTRY_BIN (mgwrap);
395 g_return_if_fail (dbin->priv);
396
397 gtk_widget_grab_focus (dbin->priv->button);
398 }
399