1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2005, 2007, 2010, 2011, 2012 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;
308 parent_class = g_type_class_peek_parent (klass);
310 widget_class = (GtkWidgetClass*) klass;
312 widget_class->map = psppire_custom_entry_map;
313 widget_class->unmap = psppire_custom_entry_unmap;
315 widget_class->realize = psppire_custom_entry_realize;
316 widget_class->unrealize = psppire_custom_entry_unrealize;
318 widget_class->expose_event = psppire_custom_entry_expose;
319 widget_class->button_press_event = psppire_custom_entry_button_press;
321 widget_class->size_allocate = psppire_custom_entry_size_allocate;
324 gtk_widget_class_install_style_property_parser
326 g_param_spec_enum ("shadow_type",
328 "Style of bevel around the custom entry button",
329 GTK_TYPE_SHADOW_TYPE,
330 GTK_SHADOW_ETCHED_IN,
332 gtk_rc_property_parse_enum);
334 custom_entry_signals[CLICKED] =
335 g_signal_new ("clicked",
336 G_TYPE_FROM_CLASS (gobject_class),
340 g_cclosure_marshal_VOID__VOID,
348 psppire_custom_entry_init (PsppireCustomEntry *ce)
353 psppire_custom_entry_new ()
355 return GTK_WIDGET (g_object_new (psppire_custom_entry_get_type (), NULL));
361 psppire_custom_entry_button_press (GtkWidget *widget,
362 GdkEventButton *event)
364 PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
366 if (event->window == ce->panel)
368 gboolean is_editable ;
369 if (!gtk_widget_has_focus (widget))
370 gtk_widget_grab_focus (widget);
372 g_object_get (ce, "editable", &is_editable, NULL);
374 if ( event->button == 1 && is_editable )
375 g_signal_emit (widget, custom_entry_signals[CLICKED], 0);
379 return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
387 psppire_custom_entry_size_allocate (GtkWidget *widget,
388 GtkAllocation *allocation)
390 PsppireCustomEntry *ce;
391 GtkAllocation entry_allocation;
392 GtkAllocation panel_allocation;
396 g_return_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget));
397 g_return_if_fail (allocation != NULL);
399 ce = PSPPIRE_CUSTOM_ENTRY (widget);
400 button_width = psppire_custom_entry_get_button_width (ce);
401 panel_width = button_width + 2 * widget->style->xthickness;
403 widget->allocation = *allocation;
405 entry_allocation = *allocation;
406 entry_allocation.width -= panel_width;
408 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
410 entry_allocation.x += panel_width;
411 panel_allocation.x = allocation->x;
415 panel_allocation.x = allocation->x + allocation->width - panel_width;
418 panel_allocation.width = panel_width;
419 panel_allocation.height = MIN (widget->requisition.height, allocation->height);
421 panel_allocation.y = allocation->y + (allocation->height -
422 panel_allocation.height) / 2;
424 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &entry_allocation);
426 if (gtk_widget_get_realized (widget))
428 gdk_window_move_resize (PSPPIRE_CUSTOM_ENTRY (widget)->panel,
431 panel_allocation.width,
432 panel_allocation.height);
435 psppire_custom_entry_redraw (ce);