gui: Include only <gtk/gtk.h> to use GTK+.
[pspp-builds.git] / src / ui / gui / customentry.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2005, 2007, 2010  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 /*
18    This widget is a subclass of GtkEntry.  It's an entry widget with a
19    button on the right hand side.
20
21    This code is heavily based upon the GtkSpinButton widget.  Therefore
22    the copyright notice of that code is pasted below.
23
24    Please note however,  this code is covered by the GPL, not the LGPL.
25 */
26
27 /* GTK - The GIMP Toolkit
28  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
29  *
30  * GtkSpinButton widget for GTK+
31  * Copyright (C) 1998 Lars Hamann and Stefan Jeske
32  *
33  * This library is free software; you can redistribute it and/or
34  * modify it under the terms of the GNU Lesser General Public
35  * License as published by the Free Software Foundation; either
36  * version 2 of the License, or (at your option) any later version.
37  *
38  * This library is distributed in the hope that it will be useful,
39  * but WITHOUT ANY WARRANTY; without even the implied warranty of
40  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
41  * Lesser General Public License for more details.
42  *
43  * You should have received a copy of the GNU Lesser General Public
44  * License along with this library.  If not, see
45  * <http://www.gnu.org/licenses/>.
46  */
47
48
49 /*
50  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
51  * file for a list of people on the GTK+ Team.  See the ChangeLog
52  * files for a list of changes.  These files are distributed with
53  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
54  */
55
56 #include <config.h>
57
58 #include <gtk/gtk.h>
59 #include "customentry.h"
60
61
62 static void psppire_custom_entry_class_init          (PsppireCustomEntryClass *klass);
63 static void psppire_custom_entry_init                (PsppireCustomEntry      *ce);
64
65 static GtkEntryClass *parent_class = NULL;
66
67 /* Signals */
68 enum
69 {
70   CLICKED,
71   n_SIGNALS
72 };
73
74
75 static guint custom_entry_signals[n_SIGNALS] = {0};
76
77
78 GType
79 psppire_custom_entry_get_type (void)
80 {
81   static GType ce_type = 0;
82
83   if (!ce_type)
84     {
85       static const GTypeInfo ce_info =
86         {
87           sizeof (PsppireCustomEntryClass),
88           NULL, /* base_init */
89           NULL, /* base_finalize */
90           (GClassInitFunc) psppire_custom_entry_class_init,
91           NULL, /* class_finalize */
92           NULL, /* class_data */
93           sizeof (PsppireCustomEntry),
94           0,
95           (GInstanceInitFunc) psppire_custom_entry_init,
96         };
97
98       ce_type = g_type_register_static (GTK_TYPE_ENTRY, "PsppireCustomEntry",
99                                         &ce_info, 0);
100     }
101
102   return ce_type;
103 }
104
105
106 static void
107 psppire_custom_entry_map (GtkWidget *widget)
108 {
109   if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
110     {
111       GTK_WIDGET_CLASS (parent_class)->map (widget);
112       gdk_window_show (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
113     }
114 }
115
116 static void
117 psppire_custom_entry_unmap (GtkWidget *widget)
118 {
119   if (GTK_WIDGET_MAPPED (widget))
120     {
121       gdk_window_hide (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
122       GTK_WIDGET_CLASS (parent_class)->unmap (widget);
123     }
124 }
125
126 static gint psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry);
127
128 static void
129 psppire_custom_entry_realize (GtkWidget *widget)
130 {
131   PsppireCustomEntry *custom_entry;
132   GdkWindowAttr attributes;
133   gint attributes_mask;
134   guint real_width;
135   gint button_size ;
136
137   custom_entry = PSPPIRE_CUSTOM_ENTRY (widget);
138
139   button_size = psppire_custom_entry_get_button_width (custom_entry);
140
141   real_width = widget->allocation.width;
142   widget->allocation.width -= button_size + 2 * widget->style->xthickness;
143   gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
144                          GDK_KEY_RELEASE_MASK);
145   GTK_WIDGET_CLASS (parent_class)->realize (widget);
146
147   widget->allocation.width = real_width;
148
149   attributes.window_type = GDK_WINDOW_CHILD;
150   attributes.wclass = GDK_INPUT_OUTPUT;
151   attributes.visual = gtk_widget_get_visual (widget);
152   attributes.colormap = gtk_widget_get_colormap (widget);
153   attributes.event_mask = gtk_widget_get_events (widget);
154   attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
155     | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
156     | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
157
158   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
159
160   attributes.x = (widget->allocation.x +
161                   widget->allocation.width - button_size -
162                   2 * widget->style->xthickness);
163   attributes.y = widget->allocation.y + (widget->allocation.height -
164                                          widget->requisition.height) / 2;
165   attributes.width = button_size + 2 * widget->style->xthickness;
166   attributes.height = widget->requisition.height;
167
168   custom_entry->panel = gdk_window_new (gtk_widget_get_parent_window (widget),
169                                         &attributes, attributes_mask);
170   gdk_window_set_user_data (custom_entry->panel, widget);
171
172   gtk_style_set_background (widget->style, custom_entry->panel, GTK_STATE_NORMAL);
173
174
175   gtk_widget_queue_resize (GTK_WIDGET (custom_entry));
176 }
177
178
179 #define MIN_BUTTON_WIDTH  6
180
181 static gint
182 psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry)
183 {
184   const gint size = pango_font_description_get_size
185     (GTK_WIDGET (custom_entry)->style->font_desc);
186
187   gint button_width = MAX (PANGO_PIXELS (size), MIN_BUTTON_WIDTH);
188
189   return button_width - button_width % 2; /* force even */
190 }
191
192 /**
193  * custom_entry_get_shadow_type:
194  * @custom_entry: a #PsppireCustomEntry
195  *
196  * Convenience function to Get the shadow type from the underlying widget's
197  * style.
198  *
199  * Return value: the #GtkShadowType
200  **/
201 static gint
202 psppire_custom_entry_get_shadow_type (PsppireCustomEntry *custom_entry)
203 {
204   GtkShadowType rc_shadow_type;
205
206   gtk_widget_style_get (GTK_WIDGET (custom_entry), "shadow_type", &rc_shadow_type, NULL);
207
208   return rc_shadow_type;
209 }
210
211
212 static void
213 psppire_custom_entry_unrealize (GtkWidget *widget)
214 {
215   PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
216
217   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
218
219   if (ce->panel)
220     {
221       gdk_window_set_user_data (ce->panel, NULL);
222       gdk_window_destroy (ce->panel);
223       ce->panel = NULL;
224     }
225 }
226
227
228 static void
229 psppire_custom_entry_redraw (PsppireCustomEntry *custom_entry)
230 {
231   GtkWidget *widget;
232
233   widget = GTK_WIDGET (custom_entry);
234
235   if (GTK_WIDGET_DRAWABLE (widget))
236     {
237       gtk_widget_queue_draw (widget);
238
239       /* We must invalidate the panel window ourselves, because it
240        * is not a child of widget->window
241        */
242       gdk_window_invalidate_rect (custom_entry->panel, NULL, TRUE);
243     }
244 }
245
246
247 static gint
248 psppire_custom_entry_expose (GtkWidget      *widget,
249                      GdkEventExpose *event)
250 {
251   PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
252
253   g_return_val_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget), FALSE);
254   g_return_val_if_fail (event != NULL, FALSE);
255
256   if (GTK_WIDGET_DRAWABLE (widget))
257     {
258       gboolean is_editable;
259       GtkShadowType shadow_type;
260       GdkRectangle rect;
261
262       rect.x = 0;
263       rect.y = 0;
264
265       if (event->window != ce->panel)
266         GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
267
268       gdk_drawable_get_size (ce->panel, &rect.width, &rect.height);
269
270       gdk_window_begin_paint_rect (ce->panel, &rect);
271
272
273       shadow_type = psppire_custom_entry_get_shadow_type (ce);
274
275       g_object_get (widget, "editable", &is_editable, NULL);
276
277       gtk_paint_box (widget->style, ce->panel,
278                      is_editable ? GTK_STATE_NORMAL: GTK_STATE_INSENSITIVE,
279                      shadow_type,
280                      NULL, widget, "customentry",
281                      rect.x, rect.y, rect.width, rect.height);
282
283
284       gdk_window_end_paint (ce->panel);
285     }
286
287   return FALSE;
288 }
289
290
291 static gint
292 psppire_custom_entry_button_press (GtkWidget      *widget,
293                            GdkEventButton *event);
294
295 static void
296 psppire_custom_entry_size_allocate (GtkWidget     *widget,
297                             GtkAllocation *allocation);
298
299
300
301 static void
302 psppire_custom_entry_class_init (PsppireCustomEntryClass *klass)
303 {
304   GObjectClass     *gobject_class = G_OBJECT_CLASS (klass);
305
306   GtkWidgetClass   *widget_class;
307   GtkEntryClass   *entry_class;
308
309   parent_class = g_type_class_peek_parent (klass);
310
311   widget_class   = (GtkWidgetClass*)   klass;
312   entry_class   = (GtkEntryClass*)   klass;
313
314   widget_class->map = psppire_custom_entry_map;
315   widget_class->unmap = psppire_custom_entry_unmap;
316
317   widget_class->realize = psppire_custom_entry_realize;
318   widget_class->unrealize = psppire_custom_entry_unrealize;
319
320   widget_class->expose_event = psppire_custom_entry_expose;
321   widget_class->button_press_event = psppire_custom_entry_button_press;
322
323   widget_class->size_allocate = psppire_custom_entry_size_allocate;
324
325
326   gtk_widget_class_install_style_property_parser
327     (widget_class,
328      g_param_spec_enum ("shadow_type",
329                         "Shadow Type",
330                         "Style of bevel around the custom entry button",
331                         GTK_TYPE_SHADOW_TYPE,
332                         GTK_SHADOW_ETCHED_IN,
333                         G_PARAM_READABLE),
334      gtk_rc_property_parse_enum);
335
336   custom_entry_signals[CLICKED] =
337     g_signal_new ("clicked",
338                   G_TYPE_FROM_CLASS (gobject_class),
339                   G_SIGNAL_RUN_LAST,
340                   0,
341                   NULL, NULL,
342                   g_cclosure_marshal_VOID__VOID,
343                   G_TYPE_NONE,
344                   0);
345
346
347 }
348
349 static void
350 psppire_custom_entry_init (PsppireCustomEntry *ce)
351 {
352 }
353
354 GtkWidget*
355 psppire_custom_entry_new ()
356 {
357   return GTK_WIDGET (g_object_new (psppire_custom_entry_get_type (), NULL));
358 }
359
360
361
362 static gint
363 psppire_custom_entry_button_press (GtkWidget *widget,
364                                    GdkEventButton *event)
365 {
366   PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
367
368   if (event->window == ce->panel)
369     {
370       gboolean is_editable ;
371       if (!GTK_WIDGET_HAS_FOCUS (widget))
372         gtk_widget_grab_focus (widget);
373
374       g_object_get (ce, "editable", &is_editable, NULL);
375
376       if ( event->button == 1 && is_editable )
377         g_signal_emit (widget, custom_entry_signals[CLICKED], 0);
378
379     }
380   else
381     return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
382
383   return FALSE;
384 }
385
386
387
388 static void
389 psppire_custom_entry_size_allocate (GtkWidget     *widget,
390                             GtkAllocation *allocation)
391 {
392   PsppireCustomEntry *ce;
393   GtkAllocation entry_allocation;
394   GtkAllocation panel_allocation;
395   gint button_width;
396   gint panel_width;
397
398   g_return_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget));
399   g_return_if_fail (allocation != NULL);
400
401   ce = PSPPIRE_CUSTOM_ENTRY (widget);
402   button_width = psppire_custom_entry_get_button_width (ce);
403   panel_width = button_width + 2 * widget->style->xthickness;
404
405   widget->allocation = *allocation;
406
407   entry_allocation = *allocation;
408   entry_allocation.width -= panel_width;
409
410   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
411     {
412       entry_allocation.x += panel_width;
413       panel_allocation.x = allocation->x;
414     }
415   else
416     {
417       panel_allocation.x = allocation->x + allocation->width - panel_width;
418     }
419
420   panel_allocation.width = panel_width;
421   panel_allocation.height = MIN (widget->requisition.height, allocation->height);
422
423   panel_allocation.y = allocation->y + (allocation->height -
424                                         panel_allocation.height) / 2;
425
426   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &entry_allocation);
427
428   if (GTK_WIDGET_REALIZED (widget))
429     {
430       gdk_window_move_resize (PSPPIRE_CUSTOM_ENTRY (widget)->panel,
431                               panel_allocation.x,
432                               panel_allocation.y,
433                               panel_allocation.width,
434                               panel_allocation.height);
435     }
436
437   psppire_custom_entry_redraw (ce);
438 }
439
440
441
442
443