Change license from GPLv2+ to GPLv3+.
[pspp-builds.git] / src / ui / gui / customentry.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2005  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 #include <gettext.h>
58 #define _(msgid) gettext (msgid)
59
60
61 #include <gtk/gtksignal.h>
62 #include <gtk/gtkentry.h>
63 #include "customentry.h"
64
65
66 static void psppire_custom_entry_class_init          (PsppireCustomEntryClass *klass);
67 static void psppire_custom_entry_init                (PsppireCustomEntry      *ce);
68
69 static GtkEntryClass *parent_class = NULL;
70
71 /* Signals */
72 enum
73 {
74   CLICKED,
75   n_SIGNALS
76 };
77
78
79 static guint custom_entry_signals[n_SIGNALS] = {0};
80
81
82 GType
83 psppire_custom_entry_get_type (void)
84 {
85   static GType ce_type = 0;
86
87   if (!ce_type)
88     {
89       static const GTypeInfo ce_info =
90         {
91           sizeof (PsppireCustomEntryClass),
92           NULL, /* base_init */
93           NULL, /* base_finalize */
94           (GClassInitFunc) psppire_custom_entry_class_init,
95           NULL, /* class_finalize */
96           NULL, /* class_data */
97           sizeof (PsppireCustomEntry),
98           0,
99           (GInstanceInitFunc) psppire_custom_entry_init,
100         };
101
102       ce_type = g_type_register_static (GTK_TYPE_ENTRY, "PsppireCustomEntry",
103                                         &ce_info, 0);
104     }
105
106   return ce_type;
107 }
108
109
110 static void
111 psppire_custom_entry_map (GtkWidget *widget)
112 {
113   if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
114     {
115       GTK_WIDGET_CLASS (parent_class)->map (widget);
116       gdk_window_show (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
117     }
118 }
119
120 static void
121 psppire_custom_entry_unmap (GtkWidget *widget)
122 {
123   if (GTK_WIDGET_MAPPED (widget))
124     {
125       gdk_window_hide (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
126       GTK_WIDGET_CLASS (parent_class)->unmap (widget);
127     }
128 }
129
130 static gint psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry);
131
132 static void
133 psppire_custom_entry_realize (GtkWidget *widget)
134 {
135   PsppireCustomEntry *custom_entry;
136   GdkWindowAttr attributes;
137   gint attributes_mask;
138   guint real_width;
139   gint button_size ;
140
141   custom_entry = PSPPIRE_CUSTOM_ENTRY (widget);
142
143   button_size = psppire_custom_entry_get_button_width (custom_entry);
144
145   real_width = widget->allocation.width;
146   widget->allocation.width -= button_size + 2 * widget->style->xthickness;
147   gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
148                          GDK_KEY_RELEASE_MASK);
149   GTK_WIDGET_CLASS (parent_class)->realize (widget);
150
151   widget->allocation.width = real_width;
152
153   attributes.window_type = GDK_WINDOW_CHILD;
154   attributes.wclass = GDK_INPUT_OUTPUT;
155   attributes.visual = gtk_widget_get_visual (widget);
156   attributes.colormap = gtk_widget_get_colormap (widget);
157   attributes.event_mask = gtk_widget_get_events (widget);
158   attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
159     | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
160     | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
161
162   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
163
164   attributes.x = (widget->allocation.x +
165                   widget->allocation.width - button_size -
166                   2 * widget->style->xthickness);
167   attributes.y = widget->allocation.y + (widget->allocation.height -
168                                          widget->requisition.height) / 2;
169   attributes.width = button_size + 2 * widget->style->xthickness;
170   attributes.height = widget->requisition.height;
171
172   custom_entry->panel = gdk_window_new (gtk_widget_get_parent_window (widget),
173                                         &attributes, attributes_mask);
174   gdk_window_set_user_data (custom_entry->panel, widget);
175
176   gtk_style_set_background (widget->style, custom_entry->panel, GTK_STATE_NORMAL);
177
178
179   gtk_widget_queue_resize (GTK_WIDGET (custom_entry));
180 }
181
182
183 #define MIN_BUTTON_WIDTH  6
184
185 static gint
186 psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry)
187 {
188   const gint size = pango_font_description_get_size
189     (GTK_WIDGET (custom_entry)->style->font_desc);
190
191   gint button_width = MAX (PANGO_PIXELS (size), MIN_BUTTON_WIDTH);
192
193   return button_width - button_width % 2; /* force even */
194 }
195
196 /**
197  * custom_entry_get_shadow_type:
198  * @custom_entry: a #PsppireCustomEntry
199  *
200  * Convenience function to Get the shadow type from the underlying widget's
201  * style.
202  *
203  * Return value: the #GtkShadowType
204  **/
205 static gint
206 psppire_custom_entry_get_shadow_type (PsppireCustomEntry *custom_entry)
207 {
208   GtkShadowType rc_shadow_type;
209
210   gtk_widget_style_get (GTK_WIDGET (custom_entry), "shadow_type", &rc_shadow_type, NULL);
211
212   return rc_shadow_type;
213 }
214
215
216 static void
217 psppire_custom_entry_unrealize (GtkWidget *widget)
218 {
219   PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
220
221   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
222
223   if (ce->panel)
224     {
225       gdk_window_set_user_data (ce->panel, NULL);
226       gdk_window_destroy (ce->panel);
227       ce->panel = NULL;
228     }
229 }
230
231
232 static void
233 psppire_custom_entry_redraw (PsppireCustomEntry *custom_entry)
234 {
235   GtkWidget *widget;
236
237   widget = GTK_WIDGET (custom_entry);
238
239   if (GTK_WIDGET_DRAWABLE (widget))
240     {
241       gtk_widget_queue_draw (widget);
242
243       /* We must invalidate the panel window ourselves, because it
244        * is not a child of widget->window
245        */
246       gdk_window_invalidate_rect (custom_entry->panel, NULL, TRUE);
247     }
248 }
249
250
251 static gint
252 psppire_custom_entry_expose (GtkWidget      *widget,
253                      GdkEventExpose *event)
254 {
255   PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
256
257   g_return_val_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget), FALSE);
258   g_return_val_if_fail (event != NULL, FALSE);
259
260   if (GTK_WIDGET_DRAWABLE (widget))
261     {
262       GtkShadowType shadow_type;
263       GdkRectangle rect;
264
265       rect.x = 0;
266       rect.y = 0;
267
268       if (event->window != ce->panel)
269         GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
270
271       gdk_drawable_get_size (ce->panel, &rect.width, &rect.height);
272
273       gdk_window_begin_paint_rect (ce->panel, &rect);
274
275
276       shadow_type = psppire_custom_entry_get_shadow_type (ce);
277
278       if (shadow_type != GTK_SHADOW_NONE)
279         {
280           gtk_paint_box (widget->style, ce->panel,
281                          GTK_STATE_NORMAL, shadow_type,
282                          NULL, widget, "customentry",
283                          rect.x, rect.y, rect.width, rect.height);
284
285         }
286
287       gdk_window_end_paint (ce->panel);
288     }
289
290   return FALSE;
291 }
292
293
294 static gint
295 psppire_custom_entry_button_press (GtkWidget      *widget,
296                            GdkEventButton *event);
297
298 static void
299 psppire_custom_entry_size_allocate (GtkWidget     *widget,
300                             GtkAllocation *allocation);
301
302
303
304 static void
305 psppire_custom_entry_class_init (PsppireCustomEntryClass *klass)
306 {
307   GObjectClass     *gobject_class = G_OBJECT_CLASS (klass);
308
309   GtkWidgetClass   *widget_class;
310   GtkEntryClass   *entry_class;
311
312   parent_class = g_type_class_peek_parent (klass);
313
314   widget_class   = (GtkWidgetClass*)   klass;
315   entry_class   = (GtkEntryClass*)   klass;
316
317   widget_class->map = psppire_custom_entry_map;
318   widget_class->unmap = psppire_custom_entry_unmap;
319
320   widget_class->realize = psppire_custom_entry_realize;
321   widget_class->unrealize = psppire_custom_entry_unrealize;
322
323   widget_class->expose_event = psppire_custom_entry_expose;
324   widget_class->button_press_event = psppire_custom_entry_button_press;
325
326   widget_class->size_allocate = psppire_custom_entry_size_allocate;
327
328
329   gtk_widget_class_install_style_property_parser
330     (widget_class,
331      g_param_spec_enum ("shadow_type",
332                         "Shadow Type",
333                         _("Style of bevel around the custom entry button"),
334                         GTK_TYPE_SHADOW_TYPE,
335                         GTK_SHADOW_ETCHED_IN,
336                         G_PARAM_READABLE),
337      gtk_rc_property_parse_enum);
338
339   custom_entry_signals[CLICKED] =
340     g_signal_new ("clicked",
341                   G_TYPE_FROM_CLASS (gobject_class),
342                   G_SIGNAL_RUN_LAST,
343                   0,
344                   NULL, NULL,
345                   g_cclosure_marshal_VOID__VOID,
346                   G_TYPE_NONE,
347                   0);
348
349
350 }
351
352 static void
353 psppire_custom_entry_init (PsppireCustomEntry *ce)
354 {
355 }
356
357 GtkWidget*
358 psppire_custom_entry_new ()
359 {
360   return GTK_WIDGET (g_object_new (psppire_custom_entry_get_type (), NULL));
361 }
362
363
364
365 static gint
366 psppire_custom_entry_button_press (GtkWidget *widget,
367                                    GdkEventButton *event)
368 {
369   PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
370
371   if (event->window == ce->panel)
372     {
373       if (!GTK_WIDGET_HAS_FOCUS (widget))
374         gtk_widget_grab_focus (widget);
375
376       if ( event->button == 1)
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