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 #define _(msgid) gettext (msgid)
61 #include <gtk/gtksignal.h>
62 #include <gtk/gtkentry.h>
63 #include "customentry.h"
66 static void psppire_custom_entry_class_init (PsppireCustomEntryClass *klass);
67 static void psppire_custom_entry_init (PsppireCustomEntry *ce);
69 static GtkEntryClass *parent_class = NULL;
79 static guint custom_entry_signals[n_SIGNALS] = {0};
83 psppire_custom_entry_get_type (void)
85 static GType ce_type = 0;
89 static const GTypeInfo ce_info =
91 sizeof (PsppireCustomEntryClass),
93 NULL, /* base_finalize */
94 (GClassInitFunc) psppire_custom_entry_class_init,
95 NULL, /* class_finalize */
96 NULL, /* class_data */
97 sizeof (PsppireCustomEntry),
99 (GInstanceInitFunc) psppire_custom_entry_init,
102 ce_type = g_type_register_static (GTK_TYPE_ENTRY, "PsppireCustomEntry",
111 psppire_custom_entry_map (GtkWidget *widget)
113 if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
115 GTK_WIDGET_CLASS (parent_class)->map (widget);
116 gdk_window_show (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
121 psppire_custom_entry_unmap (GtkWidget *widget)
123 if (GTK_WIDGET_MAPPED (widget))
125 gdk_window_hide (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
126 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
130 static gint psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry);
133 psppire_custom_entry_realize (GtkWidget *widget)
135 PsppireCustomEntry *custom_entry;
136 GdkWindowAttr attributes;
137 gint attributes_mask;
141 custom_entry = PSPPIRE_CUSTOM_ENTRY (widget);
143 button_size = psppire_custom_entry_get_button_width (custom_entry);
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);
151 widget->allocation.width = real_width;
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;
162 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
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;
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);
176 gtk_style_set_background (widget->style, custom_entry->panel, GTK_STATE_NORMAL);
179 gtk_widget_queue_resize (GTK_WIDGET (custom_entry));
183 #define MIN_BUTTON_WIDTH 6
186 psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry)
188 const gint size = pango_font_description_get_size
189 (GTK_WIDGET (custom_entry)->style->font_desc);
191 gint button_width = MAX (PANGO_PIXELS (size), MIN_BUTTON_WIDTH);
193 return button_width - button_width % 2; /* force even */
197 * custom_entry_get_shadow_type:
198 * @custom_entry: a #PsppireCustomEntry
200 * Convenience function to Get the shadow type from the underlying widget's
203 * Return value: the #GtkShadowType
206 psppire_custom_entry_get_shadow_type (PsppireCustomEntry *custom_entry)
208 GtkShadowType rc_shadow_type;
210 gtk_widget_style_get (GTK_WIDGET (custom_entry), "shadow_type", &rc_shadow_type, NULL);
212 return rc_shadow_type;
217 psppire_custom_entry_unrealize (GtkWidget *widget)
219 PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
221 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
225 gdk_window_set_user_data (ce->panel, NULL);
226 gdk_window_destroy (ce->panel);
233 psppire_custom_entry_redraw (PsppireCustomEntry *custom_entry)
237 widget = GTK_WIDGET (custom_entry);
239 if (GTK_WIDGET_DRAWABLE (widget))
241 gtk_widget_queue_draw (widget);
243 /* We must invalidate the panel window ourselves, because it
244 * is not a child of widget->window
246 gdk_window_invalidate_rect (custom_entry->panel, NULL, TRUE);
252 psppire_custom_entry_expose (GtkWidget *widget,
253 GdkEventExpose *event)
255 PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
257 g_return_val_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget), FALSE);
258 g_return_val_if_fail (event != NULL, FALSE);
260 if (GTK_WIDGET_DRAWABLE (widget))
262 gboolean is_editable;
263 GtkShadowType shadow_type;
269 if (event->window != ce->panel)
270 GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
272 gdk_drawable_get_size (ce->panel, &rect.width, &rect.height);
274 gdk_window_begin_paint_rect (ce->panel, &rect);
277 shadow_type = psppire_custom_entry_get_shadow_type (ce);
279 g_object_get (widget, "editable", &is_editable, NULL);
281 gtk_paint_box (widget->style, ce->panel,
282 is_editable ? GTK_STATE_NORMAL: GTK_STATE_INSENSITIVE,
284 NULL, widget, "customentry",
285 rect.x, rect.y, rect.width, rect.height);
288 gdk_window_end_paint (ce->panel);
296 psppire_custom_entry_button_press (GtkWidget *widget,
297 GdkEventButton *event);
300 psppire_custom_entry_size_allocate (GtkWidget *widget,
301 GtkAllocation *allocation);
306 psppire_custom_entry_class_init (PsppireCustomEntryClass *klass)
308 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
310 GtkWidgetClass *widget_class;
311 GtkEntryClass *entry_class;
313 parent_class = g_type_class_peek_parent (klass);
315 widget_class = (GtkWidgetClass*) klass;
316 entry_class = (GtkEntryClass*) klass;
318 widget_class->map = psppire_custom_entry_map;
319 widget_class->unmap = psppire_custom_entry_unmap;
321 widget_class->realize = psppire_custom_entry_realize;
322 widget_class->unrealize = psppire_custom_entry_unrealize;
324 widget_class->expose_event = psppire_custom_entry_expose;
325 widget_class->button_press_event = psppire_custom_entry_button_press;
327 widget_class->size_allocate = psppire_custom_entry_size_allocate;
330 gtk_widget_class_install_style_property_parser
332 g_param_spec_enum ("shadow_type",
334 _("Style of bevel around the custom entry button"),
335 GTK_TYPE_SHADOW_TYPE,
336 GTK_SHADOW_ETCHED_IN,
338 gtk_rc_property_parse_enum);
340 custom_entry_signals[CLICKED] =
341 g_signal_new ("clicked",
342 G_TYPE_FROM_CLASS (gobject_class),
346 g_cclosure_marshal_VOID__VOID,
354 psppire_custom_entry_init (PsppireCustomEntry *ce)
359 psppire_custom_entry_new ()
361 return GTK_WIDGET (g_object_new (psppire_custom_entry_get_type (), NULL));
367 psppire_custom_entry_button_press (GtkWidget *widget,
368 GdkEventButton *event)
370 PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
372 if (event->window == ce->panel)
374 gboolean is_editable ;
375 if (!GTK_WIDGET_HAS_FOCUS (widget))
376 gtk_widget_grab_focus (widget);
378 g_object_get (ce, "editable", &is_editable, NULL);
380 if ( event->button == 1 && is_editable )
381 g_signal_emit (widget, custom_entry_signals[CLICKED], 0);
385 return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
393 psppire_custom_entry_size_allocate (GtkWidget *widget,
394 GtkAllocation *allocation)
396 PsppireCustomEntry *ce;
397 GtkAllocation entry_allocation;
398 GtkAllocation panel_allocation;
402 g_return_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget));
403 g_return_if_fail (allocation != NULL);
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;
409 widget->allocation = *allocation;
411 entry_allocation = *allocation;
412 entry_allocation.width -= panel_width;
414 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
416 entry_allocation.x += panel_width;
417 panel_allocation.x = allocation->x;
421 panel_allocation.x = allocation->x + allocation->width - panel_width;
424 panel_allocation.width = panel_width;
425 panel_allocation.height = MIN (widget->requisition.height, allocation->height);
427 panel_allocation.y = allocation->y + (allocation->height -
428 panel_allocation.height) / 2;
430 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &entry_allocation);
432 if (GTK_WIDGET_REALIZED (widget))
434 gdk_window_move_resize (PSPPIRE_CUSTOM_ENTRY (widget)->panel,
437 panel_allocation.width,
438 panel_allocation.height);
441 psppire_custom_entry_redraw (ce);