Fixed a multitude of C89 compatibility warnings.
[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    Written by John Darrington
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19    02110-1301, USA. 
20 */
21
22 /* 
23    This widget is a subclass of GtkEntry.  It's an entry widget with a 
24    button on the right hand side.
25
26    This code is heavily based upon the GtkSpinButton widget.  Therefore
27    the copyright notice of that code is pasted below.
28
29    Please note however,  this code is covered by the GPL, not the LGPL.
30 */
31
32 /* GTK - The GIMP Toolkit
33  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
34  *
35  * GtkSpinButton widget for GTK+
36  * Copyright (C) 1998 Lars Hamann and Stefan Jeske
37  *
38  * This library is free software; you can redistribute it and/or
39  * modify it under the terms of the GNU Lesser General Public
40  * License as published by the Free Software Foundation; either
41  * version 2 of the License, or (at your option) any later version.
42  *
43  * This library is distributed in the hope that it will be useful,
44  * but WITHOUT ANY WARRANTY; without even the implied warranty of
45  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
46  * Lesser General Public License for more details.
47  *
48  * You should have received a copy of the GNU Lesser General Public
49  * License along with this library; if not, write to the
50  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
51  * Boston, MA 02111-1307, USA.
52  */
53
54 /*
55  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
56  * file for a list of people on the GTK+ Team.  See the ChangeLog
57  * files for a list of changes.  These files are distributed with
58  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
59  */
60
61 #include <gtk/gtksignal.h>
62 #include <gtk/gtkentry.h>
63 #include "customentry.h"
64
65 #define P_(A) A
66
67 static void psppire_custom_entry_class_init          (PsppireCustomEntryClass *klass);
68 static void psppire_custom_entry_init                (PsppireCustomEntry      *ce);
69
70 static GtkEntryClass *parent_class = NULL;
71
72 /* Signals */
73 enum
74 {
75   CLICKED,
76   n_SIGNALS
77 };
78
79
80 static guint custom_entry_signals[n_SIGNALS] = {0};
81
82
83 GType
84 psppire_custom_entry_get_type (void)
85 {
86   static GType ce_type = 0;
87
88   if (!ce_type)
89     {
90       static const GTypeInfo ce_info =
91         {
92           sizeof (PsppireCustomEntryClass),
93           NULL, /* base_init */
94           NULL, /* base_finalize */
95           (GClassInitFunc) psppire_custom_entry_class_init,
96           NULL, /* class_finalize */
97           NULL, /* class_data */
98           sizeof (PsppireCustomEntry),
99           0,
100           (GInstanceInitFunc) psppire_custom_entry_init,
101         };
102
103       ce_type = g_type_register_static (GTK_TYPE_ENTRY, "PsppireCustomEntry", 
104                                         &ce_info, 0);
105     }
106
107   return ce_type;
108 }
109
110
111 static void
112 psppire_custom_entry_map (GtkWidget *widget)
113 {
114   if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
115     {
116       GTK_WIDGET_CLASS (parent_class)->map (widget);
117       gdk_window_show (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
118     }
119 }
120
121 static void
122 psppire_custom_entry_unmap (GtkWidget *widget)
123 {
124   if (GTK_WIDGET_MAPPED (widget))
125     {
126       gdk_window_hide (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
127       GTK_WIDGET_CLASS (parent_class)->unmap (widget);
128     }
129 }
130
131 static gint psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry);
132
133 static void
134 psppire_custom_entry_realize (GtkWidget *widget)
135 {
136   PsppireCustomEntry *custom_entry;
137   GdkWindowAttr attributes;
138   gint attributes_mask;
139   guint real_width;
140   gint button_size ;
141
142   custom_entry = PSPPIRE_CUSTOM_ENTRY (widget);
143
144   button_size = psppire_custom_entry_get_button_width (custom_entry);
145
146   real_width = widget->allocation.width;
147   widget->allocation.width -= button_size + 2 * widget->style->xthickness;
148   gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
149                          GDK_KEY_RELEASE_MASK);
150   GTK_WIDGET_CLASS (parent_class)->realize (widget);
151
152   widget->allocation.width = real_width;
153   
154   attributes.window_type = GDK_WINDOW_CHILD;
155   attributes.wclass = GDK_INPUT_OUTPUT;
156   attributes.visual = gtk_widget_get_visual (widget);
157   attributes.colormap = gtk_widget_get_colormap (widget);
158   attributes.event_mask = gtk_widget_get_events (widget);
159   attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK 
160     | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK 
161     | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
162
163   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
164
165   attributes.x = (widget->allocation.x +
166                   widget->allocation.width - button_size -
167                   2 * widget->style->xthickness);
168   attributes.y = widget->allocation.y + (widget->allocation.height -
169                                          widget->requisition.height) / 2;
170   attributes.width = button_size + 2 * widget->style->xthickness;
171   attributes.height = widget->requisition.height;
172   
173   custom_entry->panel = gdk_window_new (gtk_widget_get_parent_window (widget), 
174                                         &attributes, attributes_mask);
175   gdk_window_set_user_data (custom_entry->panel, widget);
176
177   gtk_style_set_background (widget->style, custom_entry->panel, GTK_STATE_NORMAL);
178
179
180   gtk_widget_queue_resize (GTK_WIDGET (custom_entry));
181 }
182
183
184 #define MIN_BUTTON_WIDTH  6
185
186 static gint
187 psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry)
188 {
189   const gint size = pango_font_description_get_size 
190     (GTK_WIDGET (custom_entry)->style->font_desc);
191
192   gint button_width = MAX (PANGO_PIXELS (size), MIN_BUTTON_WIDTH);
193
194   return button_width - button_width % 2; /* force even */
195 }
196
197 /**
198  * custom_entry_get_shadow_type:
199  * @custom_entry: a #PsppireCustomEntry
200  * 
201  * Convenience function to Get the shadow type from the underlying widget's
202  * style.
203  * 
204  * Return value: the #GtkShadowType
205  **/
206 static gint
207 psppire_custom_entry_get_shadow_type (PsppireCustomEntry *custom_entry)
208 {
209   GtkShadowType rc_shadow_type;
210
211   gtk_widget_style_get (GTK_WIDGET (custom_entry), "shadow_type", &rc_shadow_type, NULL);
212
213   return rc_shadow_type;
214 }
215
216
217 static void
218 psppire_custom_entry_unrealize (GtkWidget *widget)
219 {
220   PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
221
222   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
223
224   if (ce->panel)
225     {
226       gdk_window_set_user_data (ce->panel, NULL);
227       gdk_window_destroy (ce->panel);
228       ce->panel = NULL;
229     }
230 }
231
232
233 static void
234 psppire_custom_entry_redraw (PsppireCustomEntry *custom_entry)
235 {
236   GtkWidget *widget;
237
238   widget = GTK_WIDGET (custom_entry);
239
240   if (GTK_WIDGET_DRAWABLE (widget))
241     {
242       gtk_widget_queue_draw (widget);
243
244       /* We must invalidate the panel window ourselves, because it
245        * is not a child of widget->window
246        */
247       gdk_window_invalidate_rect (custom_entry->panel, NULL, TRUE);
248     }
249 }        
250
251
252 static gint
253 psppire_custom_entry_expose (GtkWidget      *widget,
254                      GdkEventExpose *event)
255 {
256   PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY(widget);
257
258   g_return_val_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget), FALSE);
259   g_return_val_if_fail (event != NULL, FALSE);
260
261   if (GTK_WIDGET_DRAWABLE (widget))
262     {
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       if (shadow_type != GTK_SHADOW_NONE)
280         {
281           gtk_paint_box (widget->style, ce->panel,
282                          GTK_STATE_NORMAL, shadow_type,
283                          NULL, widget, "customentry",
284                          rect.x, rect.y, rect.width, rect.height);
285
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                         P_("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       if (!GTK_WIDGET_HAS_FOCUS (widget))
375         gtk_widget_grab_focus (widget);
376
377       if ( event->button == 1)
378         g_signal_emit (widget, custom_entry_signals[CLICKED], 0);
379
380     }
381   else
382     return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
383
384   return FALSE;
385 }
386
387
388
389 static void
390 psppire_custom_entry_size_allocate (GtkWidget     *widget,
391                             GtkAllocation *allocation)
392 {
393   PsppireCustomEntry *ce;
394   GtkAllocation entry_allocation;
395   GtkAllocation panel_allocation;
396   gint button_width;
397   gint panel_width;
398
399   g_return_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget));
400   g_return_if_fail (allocation != NULL);
401
402   ce = PSPPIRE_CUSTOM_ENTRY (widget);
403   button_width = psppire_custom_entry_get_button_width(ce);
404   panel_width = button_width + 2 * widget->style->xthickness;
405   
406   widget->allocation = *allocation;
407   
408   entry_allocation = *allocation;
409   entry_allocation.width -= panel_width;
410
411   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
412     {
413       entry_allocation.x += panel_width;
414       panel_allocation.x = allocation->x;
415     }
416   else
417     {
418       panel_allocation.x = allocation->x + allocation->width - panel_width;
419     }
420
421   panel_allocation.width = panel_width;
422   panel_allocation.height = MIN (widget->requisition.height, allocation->height);
423
424   panel_allocation.y = allocation->y + (allocation->height -
425                                         panel_allocation.height) / 2;
426
427   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &entry_allocation);
428
429   if (GTK_WIDGET_REALIZED (widget))
430     {
431       gdk_window_move_resize (PSPPIRE_CUSTOM_ENTRY (widget)->panel, 
432                               panel_allocation.x,
433                               panel_allocation.y,
434                               panel_allocation.width,
435                               panel_allocation.height); 
436     }
437
438   psppire_custom_entry_redraw (ce);
439 }
440
441
442
443
444