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