b40c11cd0301588c14309485e348a86723e19764
[pspp-builds.git] / src / ui / gui / customentry.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2005, 2007  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       gboolean is_editable;
263       GtkShadowType shadow_type;
264       GdkRectangle rect;
265
266       rect.x = 0;
267       rect.y = 0;
268
269       if (event->window != ce->panel)
270         GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
271
272       gdk_drawable_get_size (ce->panel, &rect.width, &rect.height);
273
274       gdk_window_begin_paint_rect (ce->panel, &rect);
275
276
277       shadow_type = psppire_custom_entry_get_shadow_type (ce);
278
279       g_object_get (widget, "editable", &is_editable, NULL);
280
281       gtk_paint_box (widget->style, ce->panel,
282                      is_editable ? GTK_STATE_NORMAL: GTK_STATE_INSENSITIVE,
283                      shadow_type,
284                      NULL, widget, "customentry",
285                      rect.x, rect.y, rect.width, rect.height);
286
287
288       gdk_window_end_paint (ce->panel);
289     }
290
291   return FALSE;
292 }
293
294
295 static gint
296 psppire_custom_entry_button_press (GtkWidget      *widget,
297                            GdkEventButton *event);
298
299 static void
300 psppire_custom_entry_size_allocate (GtkWidget     *widget,
301                             GtkAllocation *allocation);
302
303
304
305 static void
306 psppire_custom_entry_class_init (PsppireCustomEntryClass *klass)
307 {
308   GObjectClass     *gobject_class = G_OBJECT_CLASS (klass);
309
310   GtkWidgetClass   *widget_class;
311   GtkEntryClass   *entry_class;
312
313   parent_class = g_type_class_peek_parent (klass);
314
315   widget_class   = (GtkWidgetClass*)   klass;
316   entry_class   = (GtkEntryClass*)   klass;
317
318   widget_class->map = psppire_custom_entry_map;
319   widget_class->unmap = psppire_custom_entry_unmap;
320
321   widget_class->realize = psppire_custom_entry_realize;
322   widget_class->unrealize = psppire_custom_entry_unrealize;
323
324   widget_class->expose_event = psppire_custom_entry_expose;
325   widget_class->button_press_event = psppire_custom_entry_button_press;
326
327   widget_class->size_allocate = psppire_custom_entry_size_allocate;
328
329
330   gtk_widget_class_install_style_property_parser
331     (widget_class,
332      g_param_spec_enum ("shadow_type",
333                         "Shadow Type",
334                         _("Style of bevel around the custom entry button"),
335                         GTK_TYPE_SHADOW_TYPE,
336                         GTK_SHADOW_ETCHED_IN,
337                         G_PARAM_READABLE),
338      gtk_rc_property_parse_enum);
339
340   custom_entry_signals[CLICKED] =
341     g_signal_new ("clicked",
342                   G_TYPE_FROM_CLASS (gobject_class),
343                   G_SIGNAL_RUN_LAST,
344                   0,
345                   NULL, NULL,
346                   g_cclosure_marshal_VOID__VOID,
347                   G_TYPE_NONE,
348                   0);
349
350
351 }
352
353 static void
354 psppire_custom_entry_init (PsppireCustomEntry *ce)
355 {
356 }
357
358 GtkWidget*
359 psppire_custom_entry_new ()
360 {
361   return GTK_WIDGET (g_object_new (psppire_custom_entry_get_type (), NULL));
362 }
363
364
365
366 static gint
367 psppire_custom_entry_button_press (GtkWidget *widget,
368                                    GdkEventButton *event)
369 {
370   PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
371
372   if (event->window == ce->panel)
373     {
374       gboolean is_editable ;
375       if (!GTK_WIDGET_HAS_FOCUS (widget))
376         gtk_widget_grab_focus (widget);
377
378       g_object_get (ce, "editable", &is_editable, NULL);
379
380       if ( event->button == 1 && is_editable )
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