1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2005, 2007 Free Software Foundation
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.
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.
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/>. */
18 This widget is a subclass of GtkEntry. It's an entry widget with a
19 button on the right hand side.
21 This code is heavily based upon the GtkSpinButton widget. Therefore
22 the copyright notice of that code is pasted below.
24 Please note however, this code is covered by the GPL, not the LGPL.
27 /* GTK - The GIMP Toolkit
28 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
30 * GtkSpinButton widget for GTK+
31 * Copyright (C) 1998 Lars Hamann and Stefan Jeske
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.
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.
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/>.
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/.
58 #include <gtk/gtksignal.h>
59 #include <gtk/gtkentry.h>
60 #include "customentry.h"
63 static void psppire_custom_entry_class_init (PsppireCustomEntryClass *klass);
64 static void psppire_custom_entry_init (PsppireCustomEntry *ce);
66 static GtkEntryClass *parent_class = NULL;
76 static guint custom_entry_signals[n_SIGNALS] = {0};
80 psppire_custom_entry_get_type (void)
82 static GType ce_type = 0;
86 static const GTypeInfo ce_info =
88 sizeof (PsppireCustomEntryClass),
90 NULL, /* base_finalize */
91 (GClassInitFunc) psppire_custom_entry_class_init,
92 NULL, /* class_finalize */
93 NULL, /* class_data */
94 sizeof (PsppireCustomEntry),
96 (GInstanceInitFunc) psppire_custom_entry_init,
99 ce_type = g_type_register_static (GTK_TYPE_ENTRY, "PsppireCustomEntry",
108 psppire_custom_entry_map (GtkWidget *widget)
110 if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
112 GTK_WIDGET_CLASS (parent_class)->map (widget);
113 gdk_window_show (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
118 psppire_custom_entry_unmap (GtkWidget *widget)
120 if (GTK_WIDGET_MAPPED (widget))
122 gdk_window_hide (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
123 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
127 static gint psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry);
130 psppire_custom_entry_realize (GtkWidget *widget)
132 PsppireCustomEntry *custom_entry;
133 GdkWindowAttr attributes;
134 gint attributes_mask;
138 custom_entry = PSPPIRE_CUSTOM_ENTRY (widget);
140 button_size = psppire_custom_entry_get_button_width (custom_entry);
142 real_width = widget->allocation.width;
143 widget->allocation.width -= button_size + 2 * widget->style->xthickness;
144 gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
145 GDK_KEY_RELEASE_MASK);
146 GTK_WIDGET_CLASS (parent_class)->realize (widget);
148 widget->allocation.width = real_width;
150 attributes.window_type = GDK_WINDOW_CHILD;
151 attributes.wclass = GDK_INPUT_OUTPUT;
152 attributes.visual = gtk_widget_get_visual (widget);
153 attributes.colormap = gtk_widget_get_colormap (widget);
154 attributes.event_mask = gtk_widget_get_events (widget);
155 attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
156 | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
157 | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
159 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
161 attributes.x = (widget->allocation.x +
162 widget->allocation.width - button_size -
163 2 * widget->style->xthickness);
164 attributes.y = widget->allocation.y + (widget->allocation.height -
165 widget->requisition.height) / 2;
166 attributes.width = button_size + 2 * widget->style->xthickness;
167 attributes.height = widget->requisition.height;
169 custom_entry->panel = gdk_window_new (gtk_widget_get_parent_window (widget),
170 &attributes, attributes_mask);
171 gdk_window_set_user_data (custom_entry->panel, widget);
173 gtk_style_set_background (widget->style, custom_entry->panel, GTK_STATE_NORMAL);
176 gtk_widget_queue_resize (GTK_WIDGET (custom_entry));
180 #define MIN_BUTTON_WIDTH 6
183 psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry)
185 const gint size = pango_font_description_get_size
186 (GTK_WIDGET (custom_entry)->style->font_desc);
188 gint button_width = MAX (PANGO_PIXELS (size), MIN_BUTTON_WIDTH);
190 return button_width - button_width % 2; /* force even */
194 * custom_entry_get_shadow_type:
195 * @custom_entry: a #PsppireCustomEntry
197 * Convenience function to Get the shadow type from the underlying widget's
200 * Return value: the #GtkShadowType
203 psppire_custom_entry_get_shadow_type (PsppireCustomEntry *custom_entry)
205 GtkShadowType rc_shadow_type;
207 gtk_widget_style_get (GTK_WIDGET (custom_entry), "shadow_type", &rc_shadow_type, NULL);
209 return rc_shadow_type;
214 psppire_custom_entry_unrealize (GtkWidget *widget)
216 PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
218 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
222 gdk_window_set_user_data (ce->panel, NULL);
223 gdk_window_destroy (ce->panel);
230 psppire_custom_entry_redraw (PsppireCustomEntry *custom_entry)
234 widget = GTK_WIDGET (custom_entry);
236 if (GTK_WIDGET_DRAWABLE (widget))
238 gtk_widget_queue_draw (widget);
240 /* We must invalidate the panel window ourselves, because it
241 * is not a child of widget->window
243 gdk_window_invalidate_rect (custom_entry->panel, NULL, TRUE);
249 psppire_custom_entry_expose (GtkWidget *widget,
250 GdkEventExpose *event)
252 PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
254 g_return_val_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget), FALSE);
255 g_return_val_if_fail (event != NULL, FALSE);
257 if (GTK_WIDGET_DRAWABLE (widget))
259 gboolean is_editable;
260 GtkShadowType shadow_type;
266 if (event->window != ce->panel)
267 GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
269 gdk_drawable_get_size (ce->panel, &rect.width, &rect.height);
271 gdk_window_begin_paint_rect (ce->panel, &rect);
274 shadow_type = psppire_custom_entry_get_shadow_type (ce);
276 g_object_get (widget, "editable", &is_editable, NULL);
278 gtk_paint_box (widget->style, ce->panel,
279 is_editable ? GTK_STATE_NORMAL: GTK_STATE_INSENSITIVE,
281 NULL, widget, "customentry",
282 rect.x, rect.y, rect.width, rect.height);
285 gdk_window_end_paint (ce->panel);
293 psppire_custom_entry_button_press (GtkWidget *widget,
294 GdkEventButton *event);
297 psppire_custom_entry_size_allocate (GtkWidget *widget,
298 GtkAllocation *allocation);
303 psppire_custom_entry_class_init (PsppireCustomEntryClass *klass)
305 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
307 GtkWidgetClass *widget_class;
308 GtkEntryClass *entry_class;
310 parent_class = g_type_class_peek_parent (klass);
312 widget_class = (GtkWidgetClass*) klass;
313 entry_class = (GtkEntryClass*) klass;
315 widget_class->map = psppire_custom_entry_map;
316 widget_class->unmap = psppire_custom_entry_unmap;
318 widget_class->realize = psppire_custom_entry_realize;
319 widget_class->unrealize = psppire_custom_entry_unrealize;
321 widget_class->expose_event = psppire_custom_entry_expose;
322 widget_class->button_press_event = psppire_custom_entry_button_press;
324 widget_class->size_allocate = psppire_custom_entry_size_allocate;
327 gtk_widget_class_install_style_property_parser
329 g_param_spec_enum ("shadow_type",
331 "Style of bevel around the custom entry button",
332 GTK_TYPE_SHADOW_TYPE,
333 GTK_SHADOW_ETCHED_IN,
335 gtk_rc_property_parse_enum);
337 custom_entry_signals[CLICKED] =
338 g_signal_new ("clicked",
339 G_TYPE_FROM_CLASS (gobject_class),
343 g_cclosure_marshal_VOID__VOID,
351 psppire_custom_entry_init (PsppireCustomEntry *ce)
356 psppire_custom_entry_new ()
358 return GTK_WIDGET (g_object_new (psppire_custom_entry_get_type (), NULL));
364 psppire_custom_entry_button_press (GtkWidget *widget,
365 GdkEventButton *event)
367 PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
369 if (event->window == ce->panel)
371 gboolean is_editable ;
372 if (!GTK_WIDGET_HAS_FOCUS (widget))
373 gtk_widget_grab_focus (widget);
375 g_object_get (ce, "editable", &is_editable, NULL);
377 if ( event->button == 1 && is_editable )
378 g_signal_emit (widget, custom_entry_signals[CLICKED], 0);
382 return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
390 psppire_custom_entry_size_allocate (GtkWidget *widget,
391 GtkAllocation *allocation)
393 PsppireCustomEntry *ce;
394 GtkAllocation entry_allocation;
395 GtkAllocation panel_allocation;
399 g_return_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget));
400 g_return_if_fail (allocation != NULL);
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;
406 widget->allocation = *allocation;
408 entry_allocation = *allocation;
409 entry_allocation.width -= panel_width;
411 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
413 entry_allocation.x += panel_width;
414 panel_allocation.x = allocation->x;
418 panel_allocation.x = allocation->x + allocation->width - panel_width;
421 panel_allocation.width = panel_width;
422 panel_allocation.height = MIN (widget->requisition.height, allocation->height);
424 panel_allocation.y = allocation->y + (allocation->height -
425 panel_allocation.height) / 2;
427 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &entry_allocation);
429 if (GTK_WIDGET_REALIZED (widget))
431 gdk_window_move_resize (PSPPIRE_CUSTOM_ENTRY (widget)->panel,
434 panel_allocation.width,
435 panel_allocation.height);
438 psppire_custom_entry_redraw (ce);