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