1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2005, 2007, 2010, 2011 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/.
59 #include "customentry.h"
62 static void psppire_custom_entry_class_init (PsppireCustomEntryClass *klass);
63 static void psppire_custom_entry_init (PsppireCustomEntry *ce);
65 static GtkEntryClass *parent_class = NULL;
75 static guint custom_entry_signals[n_SIGNALS] = {0};
79 psppire_custom_entry_get_type (void)
81 static GType ce_type = 0;
85 static const GTypeInfo ce_info =
87 sizeof (PsppireCustomEntryClass),
89 NULL, /* base_finalize */
90 (GClassInitFunc) psppire_custom_entry_class_init,
91 NULL, /* class_finalize */
92 NULL, /* class_data */
93 sizeof (PsppireCustomEntry),
95 (GInstanceInitFunc) psppire_custom_entry_init,
98 ce_type = g_type_register_static (GTK_TYPE_ENTRY, "PsppireCustomEntry",
107 psppire_custom_entry_map (GtkWidget *widget)
109 if (gtk_widget_get_realized (widget) && !gtk_widget_get_mapped (widget))
111 GTK_WIDGET_CLASS (parent_class)->map (widget);
112 gdk_window_show (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
117 psppire_custom_entry_unmap (GtkWidget *widget)
119 if (gtk_widget_get_mapped (widget))
121 gdk_window_hide (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
122 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
126 static gint psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry);
129 psppire_custom_entry_realize (GtkWidget *widget)
131 PsppireCustomEntry *custom_entry;
132 GdkWindowAttr attributes;
133 gint attributes_mask;
137 custom_entry = PSPPIRE_CUSTOM_ENTRY (widget);
139 button_size = psppire_custom_entry_get_button_width (custom_entry);
141 real_width = widget->allocation.width;
142 widget->allocation.width -= button_size + 2 * widget->style->xthickness;
143 gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
144 GDK_KEY_RELEASE_MASK);
145 GTK_WIDGET_CLASS (parent_class)->realize (widget);
147 widget->allocation.width = real_width;
149 attributes.window_type = GDK_WINDOW_CHILD;
150 attributes.wclass = GDK_INPUT_OUTPUT;
151 attributes.visual = gtk_widget_get_visual (widget);
152 attributes.colormap = gtk_widget_get_colormap (widget);
153 attributes.event_mask = gtk_widget_get_events (widget);
154 attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
155 | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
156 | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
158 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
160 attributes.x = (widget->allocation.x +
161 widget->allocation.width - button_size -
162 2 * widget->style->xthickness);
163 attributes.y = widget->allocation.y + (widget->allocation.height -
164 widget->requisition.height) / 2;
165 attributes.width = button_size + 2 * widget->style->xthickness;
166 attributes.height = widget->requisition.height;
168 custom_entry->panel = gdk_window_new (gtk_widget_get_parent_window (widget),
169 &attributes, attributes_mask);
170 gdk_window_set_user_data (custom_entry->panel, widget);
172 gtk_style_set_background (widget->style, custom_entry->panel, GTK_STATE_NORMAL);
175 gtk_widget_queue_resize (GTK_WIDGET (custom_entry));
179 #define MIN_BUTTON_WIDTH 6
182 psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry)
184 const gint size = pango_font_description_get_size
185 (GTK_WIDGET (custom_entry)->style->font_desc);
187 gint button_width = MAX (PANGO_PIXELS (size), MIN_BUTTON_WIDTH);
189 return button_width - button_width % 2; /* force even */
193 * custom_entry_get_shadow_type:
194 * @custom_entry: a #PsppireCustomEntry
196 * Convenience function to Get the shadow type from the underlying widget's
199 * Return value: the #GtkShadowType
202 psppire_custom_entry_get_shadow_type (PsppireCustomEntry *custom_entry)
204 GtkShadowType rc_shadow_type;
206 gtk_widget_style_get (GTK_WIDGET (custom_entry), "shadow_type", &rc_shadow_type, NULL);
208 return rc_shadow_type;
213 psppire_custom_entry_unrealize (GtkWidget *widget)
215 PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
217 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
221 gdk_window_set_user_data (ce->panel, NULL);
222 gdk_window_destroy (ce->panel);
229 psppire_custom_entry_redraw (PsppireCustomEntry *custom_entry)
233 widget = GTK_WIDGET (custom_entry);
235 if (gtk_widget_is_drawable (widget))
237 gtk_widget_queue_draw (widget);
239 /* We must invalidate the panel window ourselves, because it
240 * is not a child of widget->window
242 gdk_window_invalidate_rect (custom_entry->panel, NULL, TRUE);
248 psppire_custom_entry_expose (GtkWidget *widget,
249 GdkEventExpose *event)
251 PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
253 g_return_val_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget), FALSE);
254 g_return_val_if_fail (event != NULL, FALSE);
256 if (gtk_widget_is_drawable (widget))
258 gboolean is_editable;
259 GtkShadowType shadow_type;
265 if (event->window != ce->panel)
266 GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
268 gdk_drawable_get_size (ce->panel, &rect.width, &rect.height);
270 gdk_window_begin_paint_rect (ce->panel, &rect);
273 shadow_type = psppire_custom_entry_get_shadow_type (ce);
275 g_object_get (widget, "editable", &is_editable, NULL);
277 gtk_paint_box (widget->style, ce->panel,
278 is_editable ? GTK_STATE_NORMAL: GTK_STATE_INSENSITIVE,
280 NULL, widget, "customentry",
281 rect.x, rect.y, rect.width, rect.height);
284 gdk_window_end_paint (ce->panel);
292 psppire_custom_entry_button_press (GtkWidget *widget,
293 GdkEventButton *event);
296 psppire_custom_entry_size_allocate (GtkWidget *widget,
297 GtkAllocation *allocation);
302 psppire_custom_entry_class_init (PsppireCustomEntryClass *klass)
304 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
306 GtkWidgetClass *widget_class;
307 GtkEntryClass *entry_class;
309 parent_class = g_type_class_peek_parent (klass);
311 widget_class = (GtkWidgetClass*) klass;
312 entry_class = (GtkEntryClass*) klass;
314 widget_class->map = psppire_custom_entry_map;
315 widget_class->unmap = psppire_custom_entry_unmap;
317 widget_class->realize = psppire_custom_entry_realize;
318 widget_class->unrealize = psppire_custom_entry_unrealize;
320 widget_class->expose_event = psppire_custom_entry_expose;
321 widget_class->button_press_event = psppire_custom_entry_button_press;
323 widget_class->size_allocate = psppire_custom_entry_size_allocate;
326 gtk_widget_class_install_style_property_parser
328 g_param_spec_enum ("shadow_type",
330 "Style of bevel around the custom entry button",
331 GTK_TYPE_SHADOW_TYPE,
332 GTK_SHADOW_ETCHED_IN,
334 gtk_rc_property_parse_enum);
336 custom_entry_signals[CLICKED] =
337 g_signal_new ("clicked",
338 G_TYPE_FROM_CLASS (gobject_class),
342 g_cclosure_marshal_VOID__VOID,
350 psppire_custom_entry_init (PsppireCustomEntry *ce)
355 psppire_custom_entry_new ()
357 return GTK_WIDGET (g_object_new (psppire_custom_entry_get_type (), NULL));
363 psppire_custom_entry_button_press (GtkWidget *widget,
364 GdkEventButton *event)
366 PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
368 if (event->window == ce->panel)
370 gboolean is_editable ;
371 if (!gtk_widget_has_focus (widget))
372 gtk_widget_grab_focus (widget);
374 g_object_get (ce, "editable", &is_editable, NULL);
376 if ( event->button == 1 && is_editable )
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_get_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);