]> git.sur5r.net Git - glabels/blob - src/field-button.c
Imported Upstream version 3.0.0
[glabels] / src / field-button.c
1 /*
2  *  field-button.c
3  *  Copyright (C) 2009  Jim Evins <evins@snaught.com>.
4  *
5  *  This file is part of gLabels.
6  *
7  *  gLabels is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  gLabels is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with gLabels.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include "field-button.h"
24
25 #include <glib/gi18n.h>
26 #include <gtk/gtk.h>
27
28 #include "field-button-menu.h"
29 #include "marshal.h"
30
31
32
33 /*========================================================*/
34 /* Private types.                                         */
35 /*========================================================*/
36
37 /** GL_FIELD_BUTTON Private fields */
38 struct _glFieldButtonPrivate {
39
40         gboolean    label_is_key;
41         gchar      *key;
42
43         GtkWidget  *label;
44
45         GtkWidget  *menu;
46 };
47
48 enum {
49         KEY_SELECTED,
50         CHANGED,
51         LAST_SIGNAL
52 };
53
54
55 /*========================================================*/
56 /* Private globals.                                       */
57 /*========================================================*/
58
59 static guint signals[LAST_SIGNAL] = {0};
60
61
62 /*========================================================*/
63 /* Private function prototypes.                           */
64 /*========================================================*/
65
66 static void gl_field_button_finalize      (GObject             *object);
67
68 static gboolean
69 button_press_event_cb (GtkWidget          *widget,
70                        GdkEventButton     *event,
71                        glFieldButton      *this);
72
73 static void
74 menu_key_selected_cb  (glFieldButtonMenu  *menu,
75                        gchar              *key,
76                        glFieldButton      *this);
77
78 static void
79 menu_selection_done_cb (GtkMenuShell      *object,
80                         glFieldButton     *this);
81
82
83 /*****************************************************************************/
84 /* Object infrastructure.                                                    */
85 /*****************************************************************************/
86 G_DEFINE_TYPE (glFieldButton, gl_field_button, GTK_TYPE_TOGGLE_BUTTON)
87
88
89 /*****************************************************************************/
90 /* Class Init Function.                                                      */
91 /*****************************************************************************/
92 static void
93 gl_field_button_class_init (glFieldButtonClass *class)
94 {
95         GObjectClass      *gobject_class = (GObjectClass *) class;
96
97         gl_field_button_parent_class = g_type_class_peek_parent (class);
98
99         gobject_class->finalize = gl_field_button_finalize;
100
101         signals[KEY_SELECTED] =
102                 g_signal_new ("key_selected",
103                               G_OBJECT_CLASS_TYPE (gobject_class),
104                               G_SIGNAL_RUN_LAST,
105                               G_STRUCT_OFFSET (glFieldButtonClass, key_selected),
106                               NULL, NULL,
107                               gl_marshal_VOID__STRING,
108                               G_TYPE_NONE, 1, G_TYPE_STRING);
109
110         signals[CHANGED] =
111                 g_signal_new ("changed",
112                               G_OBJECT_CLASS_TYPE (gobject_class),
113                               G_SIGNAL_RUN_LAST,
114                               G_STRUCT_OFFSET (glFieldButtonClass, changed),
115                               NULL, NULL,
116                               gl_marshal_VOID__VOID,
117                               G_TYPE_NONE, 0);
118 }
119
120
121 /*****************************************************************************/
122 /* Object Instance Init Function.                                            */
123 /*****************************************************************************/
124 static void
125 gl_field_button_init (glFieldButton *this)
126 {
127         GtkWidget *hbox;
128         GtkWidget *arrow;
129
130         this->priv = g_new0 (glFieldButtonPrivate, 1);
131
132         hbox = gtk_hbox_new (FALSE, 3);
133         gtk_container_add (GTK_CONTAINER (this), hbox);
134         
135         this->priv->label = gtk_label_new ("");
136         gtk_misc_set_alignment (GTK_MISC (this->priv->label), 0.0, 0.5);
137         gtk_box_pack_start (GTK_BOX (hbox), this->priv->label, TRUE, TRUE, 0);
138
139         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN);
140         gtk_box_pack_end (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
141
142         g_signal_connect (this, "button_press_event",
143                           G_CALLBACK(button_press_event_cb), this);
144 }
145
146
147 /*****************************************************************************/
148 /* Finalize Method.                                                          */
149 /*****************************************************************************/
150 static void
151 gl_field_button_finalize (GObject *object)
152 {
153         glFieldButton    *this;
154
155         g_return_if_fail (object && IS_GL_FIELD_BUTTON (object));
156         this = GL_FIELD_BUTTON (object);
157
158         g_object_ref_sink (this->priv->menu);
159         g_free (this->priv);
160
161         G_OBJECT_CLASS (gl_field_button_parent_class)->finalize (object);
162 }
163
164
165 /*****************************************************************************/
166 /** New Object Generator.                                                    */
167 /*****************************************************************************/
168 GtkWidget *
169 gl_field_button_new (const gchar  *name)
170 {
171         glFieldButton  *this;
172
173         this = g_object_new (TYPE_GL_FIELD_BUTTON, NULL);
174
175         if ( name )
176         {
177                 gtk_label_set_text (GTK_LABEL (this->priv->label), name);
178         }
179         else
180         {
181                 this->priv->label_is_key = TRUE;
182         }
183
184         this->priv->menu = gl_field_button_menu_new ();
185
186         gtk_widget_show_all (this->priv->menu);
187
188         g_signal_connect (this->priv->menu, "key_selected",
189                           G_CALLBACK (menu_key_selected_cb), this);
190         g_signal_connect (this->priv->menu, "selection_done",
191                           G_CALLBACK (menu_selection_done_cb), this);
192
193         return GTK_WIDGET (this);
194 }
195
196
197 /*****************************************************************************/
198 /* Set key list.                                                             */
199 /*****************************************************************************/
200 void
201 gl_field_button_set_keys (glFieldButton  *this,
202                           GList          *key_list)
203 {
204         gl_field_button_menu_set_keys (GL_FIELD_BUTTON_MENU (this->priv->menu),
205                                        key_list);
206         this->priv->key = g_strdup (key_list->data);
207         if ( this->priv->label_is_key )
208         {
209                 gtk_label_set_text (GTK_LABEL (this->priv->label), key_list->data);
210         }
211
212         gtk_widget_show_all (this->priv->menu);
213 }
214
215
216 /*****************************************************************************/
217 /* Set current key.                                                          */
218 /*****************************************************************************/
219 void
220 gl_field_button_set_key (glFieldButton   *this,
221                          const gchar     *key)
222 {
223         g_free (this->priv->key);
224         this->priv->key = g_strdup (key);
225
226         if ( this->priv->label_is_key )
227         {
228                 gtk_label_set_text (GTK_LABEL (this->priv->label), key);
229         }
230 }
231
232
233 /*****************************************************************************/
234 /* Get current key.                                                        */
235 /*****************************************************************************/
236 gchar *
237 gl_field_button_get_key (glFieldButton   *this)
238 {
239         return g_strdup (this->priv->key);
240 }
241
242
243 /*****************************************************************************/
244 /* Menu positioning function.                                                */
245 /*****************************************************************************/
246 static void
247 menu_position_function (GtkMenu       *menu,
248                         gint          *x,
249                         gint          *y,
250                         gboolean      *push_in,
251                         glFieldButton *this)
252 {
253         GdkScreen          *screen;
254         gint                w_screen, h_screen;
255         GdkWindow          *window;
256         gint                x_window, y_window;
257         GtkAllocation       allocation;
258         gint                x_this, y_this, h_this;
259         GtkRequisition      menu_requisition;
260         gint                h_menu, w_menu;
261
262         /*
263          * Screen size
264          */
265         screen = gtk_widget_get_screen (GTK_WIDGET (this));
266         w_screen = gdk_screen_get_width (screen);
267         h_screen = gdk_screen_get_height (screen);
268
269         /*
270          * Absolute position of "this" window on screen.
271          */
272         window = gtk_widget_get_window (GTK_WIDGET (this));
273         gdk_window_get_origin (window, &x_window, &y_window);
274
275         /*
276          *  Position and size of "this" inside window
277          */
278         gtk_widget_get_allocation (GTK_WIDGET (this), &allocation);
279         x_this = allocation.x;
280         y_this = allocation.y;
281         h_this = allocation.height;
282
283         /*
284          * Size of menu.
285          */
286         gtk_widget_size_request (this->priv->menu, &menu_requisition);
287         h_menu = menu_requisition.height;
288         w_menu = menu_requisition.width;
289
290         /*
291          * Default position anchored to lower left corner of "this".
292          */
293         *x = x_window + x_this;
294         *y = y_window + y_this + h_this;
295                 
296         /*
297          * Adjust vertical position if menu if extends past bottom of screen.
298          */
299         if ( (*y + h_menu) > h_screen )
300         {
301                 *y = y_window + y_this - h_menu;
302
303                 if ( *y < 0 )
304                 {
305                         *y = h_screen - h_menu;
306                 }
307         }
308
309         /*
310          * Adjust horizontal position if menu if extends past edge of screen.
311          */
312         if ( (*x + w_menu) > w_screen )
313         {
314                 *x = w_screen - w_menu;
315         }
316
317
318         *push_in = TRUE;
319 }
320
321
322 /*****************************************************************************/
323 /* Button "button_press_event" callback.                                     */
324 /*****************************************************************************/
325 static gboolean
326 button_press_event_cb (GtkWidget      *widget,
327                        GdkEventButton *event,
328                        glFieldButton  *this)
329 {
330         switch (event->button)
331         {
332
333         case 1:
334                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (this), TRUE);
335
336                 gtk_menu_popup (GTK_MENU (this->priv->menu),
337                                 NULL, NULL,
338                                 (GtkMenuPositionFunc)menu_position_function, this,
339                                 event->button, event->time);
340                 break;
341
342         default:
343                 break;
344
345         }
346
347         return FALSE;
348 }
349
350
351 /*****************************************************************************/
352 /* Menu "key selected" callback.                                             */
353 /*****************************************************************************/
354 static void
355 menu_key_selected_cb (glFieldButtonMenu     *menu,
356                       gchar                 *key,
357                       glFieldButton         *this)
358 {
359         if (this->priv->label_is_key)
360         {
361                 gtk_label_set_text (GTK_LABEL (this->priv->label), key);
362         }
363
364         g_free (this->priv->key);
365         this->priv->key = g_strdup (key);
366
367         g_signal_emit (this, signals[KEY_SELECTED], 0, key);
368         g_signal_emit (this, signals[CHANGED], 0);
369 }
370
371
372 /*****************************************************************************/
373 /* Menu "selection done" callback.                                           */
374 /*****************************************************************************/
375 static void
376 menu_selection_done_cb (GtkMenuShell         *object,
377                         glFieldButton        *this)
378 {
379         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (this), FALSE);
380 }
381
382
383
384 /*
385  * Local Variables:       -- emacs
386  * mode: C                -- emacs
387  * c-basic-offset: 8      -- emacs
388  * tab-width: 8           -- emacs
389  * indent-tabs-mode: nil  -- emacs
390  * End:                   -- emacs
391  */