1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2005 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 GtkShadowType shadow_type;
268 if (event->window != ce->panel)
269 GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
271 gdk_drawable_get_size (ce->panel, &rect.width, &rect.height);
273 gdk_window_begin_paint_rect (ce->panel, &rect);
276 shadow_type = psppire_custom_entry_get_shadow_type (ce);
278 if (shadow_type != GTK_SHADOW_NONE)
280 gtk_paint_box (widget->style, ce->panel,
281 GTK_STATE_NORMAL, shadow_type,
282 NULL, widget, "customentry",
283 rect.x, rect.y, rect.width, rect.height);
287 gdk_window_end_paint (ce->panel);
295 psppire_custom_entry_button_press (GtkWidget *widget,
296 GdkEventButton *event);
299 psppire_custom_entry_size_allocate (GtkWidget *widget,
300 GtkAllocation *allocation);
305 psppire_custom_entry_class_init (PsppireCustomEntryClass *klass)
307 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
309 GtkWidgetClass *widget_class;
310 GtkEntryClass *entry_class;
312 parent_class = g_type_class_peek_parent (klass);
314 widget_class = (GtkWidgetClass*) klass;
315 entry_class = (GtkEntryClass*) klass;
317 widget_class->map = psppire_custom_entry_map;
318 widget_class->unmap = psppire_custom_entry_unmap;
320 widget_class->realize = psppire_custom_entry_realize;
321 widget_class->unrealize = psppire_custom_entry_unrealize;
323 widget_class->expose_event = psppire_custom_entry_expose;
324 widget_class->button_press_event = psppire_custom_entry_button_press;
326 widget_class->size_allocate = psppire_custom_entry_size_allocate;
329 gtk_widget_class_install_style_property_parser
331 g_param_spec_enum ("shadow_type",
333 _("Style of bevel around the custom entry button"),
334 GTK_TYPE_SHADOW_TYPE,
335 GTK_SHADOW_ETCHED_IN,
337 gtk_rc_property_parse_enum);
339 custom_entry_signals[CLICKED] =
340 g_signal_new ("clicked",
341 G_TYPE_FROM_CLASS (gobject_class),
345 g_cclosure_marshal_VOID__VOID,
353 psppire_custom_entry_init (PsppireCustomEntry *ce)
358 psppire_custom_entry_new ()
360 return GTK_WIDGET (g_object_new (psppire_custom_entry_get_type (), NULL));
366 psppire_custom_entry_button_press (GtkWidget *widget,
367 GdkEventButton *event)
369 PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
371 if (event->window == ce->panel)
373 if (!GTK_WIDGET_HAS_FOCUS (widget))
374 gtk_widget_grab_focus (widget);
376 if ( event->button == 1)
377 g_signal_emit (widget, custom_entry_signals[CLICKED], 0);
381 return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
389 psppire_custom_entry_size_allocate (GtkWidget *widget,
390 GtkAllocation *allocation)
392 PsppireCustomEntry *ce;
393 GtkAllocation entry_allocation;
394 GtkAllocation panel_allocation;
398 g_return_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget));
399 g_return_if_fail (allocation != NULL);
401 ce = PSPPIRE_CUSTOM_ENTRY (widget);
402 button_width = psppire_custom_entry_get_button_width (ce);
403 panel_width = button_width + 2 * widget->style->xthickness;
405 widget->allocation = *allocation;
407 entry_allocation = *allocation;
408 entry_allocation.width -= panel_width;
410 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
412 entry_allocation.x += panel_width;
413 panel_allocation.x = allocation->x;
417 panel_allocation.x = allocation->x + allocation->width - panel_width;
420 panel_allocation.width = panel_width;
421 panel_allocation.height = MIN (widget->requisition.height, allocation->height);
423 panel_allocation.y = allocation->y + (allocation->height -
424 panel_allocation.height) / 2;
426 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &entry_allocation);
428 if (GTK_WIDGET_REALIZED (widget))
430 gdk_window_move_resize (PSPPIRE_CUSTOM_ENTRY (widget)->panel,
433 panel_allocation.width,
434 panel_allocation.height);
437 psppire_custom_entry_redraw (ce);