]> git.sur5r.net Git - glabels/blob - src/field-button.c
Reference glabels.org website
[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         GtkWidget  *label;
41
42         GtkWidget  *menu;
43 };
44
45 enum {
46         KEY_SELECTED,
47         LAST_SIGNAL
48 };
49
50
51 /*========================================================*/
52 /* Private globals.                                       */
53 /*========================================================*/
54
55 static guint signals[LAST_SIGNAL] = {0};
56
57
58 /*========================================================*/
59 /* Private function prototypes.                           */
60 /*========================================================*/
61
62 static void gl_field_button_finalize      (GObject             *object);
63
64 static gboolean
65 button_press_event_cb (GtkWidget          *widget,
66                        GdkEventButton     *event,
67                        glFieldButton      *this);
68
69 static void
70 menu_key_selected_cb  (glFieldButtonMenu  *menu,
71                        gchar              *key,
72                        glFieldButton      *this);
73
74 static void
75 menu_selection_done_cb (GtkMenuShell      *object,
76                         glFieldButton     *this);
77
78
79 /*****************************************************************************/
80 /* Object infrastructure.                                                    */
81 /*****************************************************************************/
82 G_DEFINE_TYPE (glFieldButton, gl_field_button, GTK_TYPE_TOGGLE_BUTTON);
83
84
85 /*****************************************************************************/
86 /* Class Init Function.                                                      */
87 /*****************************************************************************/
88 static void
89 gl_field_button_class_init (glFieldButtonClass *class)
90 {
91         GObjectClass      *gobject_class = (GObjectClass *) class;
92
93         gl_field_button_parent_class = g_type_class_peek_parent (class);
94
95         gobject_class->finalize = gl_field_button_finalize;
96
97         signals[KEY_SELECTED] =
98                 g_signal_new ("key_selected",
99                               G_OBJECT_CLASS_TYPE (gobject_class),
100                               G_SIGNAL_RUN_LAST,
101                               G_STRUCT_OFFSET (glFieldButtonClass, key_selected),
102                               NULL, NULL,
103                               gl_marshal_VOID__STRING,
104                               G_TYPE_NONE, 1, G_TYPE_STRING);
105 }
106
107
108 /*****************************************************************************/
109 /* Object Instance Init Function.                                            */
110 /*****************************************************************************/
111 static void
112 gl_field_button_init (glFieldButton *this)
113 {
114         GtkWidget *hbox;
115         GtkWidget *arrow;
116
117         this->priv = g_new0 (glFieldButtonPrivate, 1);
118
119         hbox = gtk_hbox_new (FALSE, 3);
120         gtk_container_add (GTK_CONTAINER (this), hbox);
121         
122         this->priv->label = gtk_label_new ("");
123         gtk_misc_set_alignment (GTK_MISC (this->priv->label), 0.0, 0.5);
124         gtk_box_pack_start (GTK_BOX (hbox), this->priv->label, TRUE, TRUE, 0);
125
126         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN);
127         gtk_box_pack_end (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
128
129         g_signal_connect (this, "button_press_event",
130                           G_CALLBACK(button_press_event_cb), this);
131 }
132
133
134 /*****************************************************************************/
135 /* Finalize Method.                                                          */
136 /*****************************************************************************/
137 static void
138 gl_field_button_finalize (GObject *object)
139 {
140         glFieldButton    *this;
141
142         g_return_if_fail (object && IS_GL_FIELD_BUTTON (object));
143         this = GL_FIELD_BUTTON (object);
144
145         g_object_ref_sink (this->priv->menu);
146         g_free (this->priv);
147
148         G_OBJECT_CLASS (gl_field_button_parent_class)->finalize (object);
149 }
150
151
152 /*****************************************************************************/
153 /** New Object Generator.                                                    */
154 /*****************************************************************************/
155 GtkWidget *
156 gl_field_button_new (const gchar  *name)
157 {
158         glFieldButton  *this;
159
160         this = g_object_new (TYPE_GL_FIELD_BUTTON, NULL);
161
162         gtk_label_set_text (GTK_LABEL (this->priv->label), name);
163
164         this->priv->menu = gl_field_button_menu_new ();
165
166         gtk_widget_show_all (this->priv->menu);
167
168         g_signal_connect (this->priv->menu, "key_selected",
169                           G_CALLBACK (menu_key_selected_cb), this);
170         g_signal_connect (this->priv->menu, "selection_done",
171                           G_CALLBACK (menu_selection_done_cb), this);
172
173         return GTK_WIDGET (this);
174 }
175
176
177 /*****************************************************************************/
178 /* Set key list.                                                             */
179 /*****************************************************************************/
180 void
181 gl_field_button_set_keys (glFieldButton  *this,
182                           GList          *key_list)
183 {
184         gl_field_button_menu_set_keys (GL_FIELD_BUTTON_MENU (this->priv->menu),
185                                        key_list);
186
187         gtk_widget_show_all (this->priv->menu);
188 }
189
190
191 /*****************************************************************************/
192 /* Menu positioning function.                                                */
193 /*****************************************************************************/
194 static void
195 menu_position_function (GtkMenu       *menu,
196                         gint          *x,
197                         gint          *y,
198                         gboolean      *push_in,
199                         glFieldButton *this)
200 {
201         GdkScreen          *screen;
202         gint                w_screen, h_screen;
203         GdkWindow          *window;
204         gint                x_window, y_window;
205         GtkAllocation      *allocation;
206         gint                x_this, y_this, h_this;
207         GtkRequisition      menu_requisition;
208         gint                h_menu, w_menu;
209
210         /*
211          * Screen size
212          */
213         screen = gtk_widget_get_screen (GTK_WIDGET (this));
214         w_screen = gdk_screen_get_width (screen);
215         h_screen = gdk_screen_get_height (screen);
216
217         /*
218          * Absolute position of "this" window on screen.
219          */
220         window = gtk_widget_get_window (GTK_WIDGET (this));
221         gdk_window_get_origin (window, &x_window, &y_window);
222
223         /*
224          *  Position and size of "this" inside window
225          */
226         allocation = &GTK_WIDGET (this)->allocation;
227         x_this = allocation->x;
228         y_this = allocation->y;
229         h_this = allocation->height;
230
231         /*
232          * Size of menu.
233          */
234         gtk_widget_size_request (this->priv->menu, &menu_requisition);
235         h_menu = menu_requisition.height;
236         w_menu = menu_requisition.width;
237
238         /*
239          * Default position anchored to lower left corner of "this".
240          */
241         *x = x_window + x_this;
242         *y = y_window + y_this + h_this;
243                 
244         /*
245          * Adjust vertical position if menu if extends past bottom of screen.
246          */
247         if ( (*y + h_menu) > h_screen )
248         {
249                 *y = y_window + y_this - h_menu;
250
251                 if ( *y < 0 )
252                 {
253                         *y = h_screen - h_menu;
254                 }
255         }
256
257         /*
258          * Adjust horizontal position if menu if extends past edge of screen.
259          */
260         if ( (*x + w_menu) > w_screen )
261         {
262                 *x = w_screen - w_menu;
263         }
264
265
266         *push_in = TRUE;
267 }
268
269
270 /*****************************************************************************/
271 /* Button "button_press_event" callback.                                     */
272 /*****************************************************************************/
273 static gboolean
274 button_press_event_cb (GtkWidget      *widget,
275                        GdkEventButton *event,
276                        glFieldButton  *this)
277 {
278         switch (event->button)
279         {
280
281         case 1:
282                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (this), TRUE);
283
284                 gtk_menu_popup (GTK_MENU (this->priv->menu),
285                                 NULL, NULL,
286                                 (GtkMenuPositionFunc)menu_position_function, this,
287                                 event->button, event->time);
288                 break;
289
290         default:
291                 break;
292
293         }
294
295         return FALSE;
296 }
297
298
299 /*****************************************************************************/
300 /* Menu "key selected" callback.                                             */
301 /*****************************************************************************/
302 static void
303 menu_key_selected_cb (glFieldButtonMenu     *menu,
304                       gchar                 *field,
305                       glFieldButton         *this)
306 {
307         g_signal_emit (this, signals[KEY_SELECTED], 0, field);
308 }
309
310
311 /*****************************************************************************/
312 /* Menu "selection done" callback.                                           */
313 /*****************************************************************************/
314 static void
315 menu_selection_done_cb (GtkMenuShell         *object,
316                         glFieldButton        *this)
317 {
318         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (this), FALSE);
319 }
320
321
322
323 /*
324  * Local Variables:       -- emacs
325  * mode: C                -- emacs
326  * c-basic-offset: 8      -- emacs
327  * tab-width: 8           -- emacs
328  * indent-tabs-mode: nil  -- emacs
329  * End:                   -- emacs
330  */