Added new files resulting from directory restructuring.
[pspp-builds.git] / src / ui / gui / customentry.c
diff --git a/src/ui/gui/customentry.c b/src/ui/gui/customentry.c
new file mode 100644 (file)
index 0000000..9afe978
--- /dev/null
@@ -0,0 +1,444 @@
+/* 
+   PSPPIRE --- A Graphical User Interface for PSPP
+   Copyright (C) 2005  Free Software Foundation
+   Written by John Darrington
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. 
+*/
+
+/* 
+   This widget is a subclass of GtkEntry.  It's an entry widget with a 
+   button on the right hand side.
+
+   This code is heavily based upon the GtkSpinButton widget.  Therefore
+   the copyright notice of that code is pasted below.
+
+   Please note however,  this code is covered by the GPL, not the LGPL.
+*/
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * GtkSpinButton widget for GTK+
+ * Copyright (C) 1998 Lars Hamann and Stefan Jeske
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+#include <gtk/gtksignal.h>
+#include <gtk/gtkentry.h>
+#include "customentry.h"
+
+#define P_(A) A
+
+static void psppire_custom_entry_class_init          (PsppireCustomEntryClass *klass);
+static void psppire_custom_entry_init                (PsppireCustomEntry      *ce);
+
+static GtkEntryClass *parent_class = NULL;
+
+/* Signals */
+enum
+{
+  CLICKED,
+  n_SIGNALS
+};
+
+
+static guint custom_entry_signals[n_SIGNALS] = {0};
+
+
+GType
+psppire_custom_entry_get_type (void)
+{
+  static GType ce_type = 0;
+
+  if (!ce_type)
+    {
+      static const GTypeInfo ce_info =
+       {
+         sizeof (PsppireCustomEntryClass),
+         NULL, /* base_init */
+         NULL, /* base_finalize */
+         (GClassInitFunc) psppire_custom_entry_class_init,
+         NULL, /* class_finalize */
+         NULL, /* class_data */
+         sizeof (PsppireCustomEntry),
+         0,
+         (GInstanceInitFunc) psppire_custom_entry_init,
+       };
+
+      ce_type = g_type_register_static (GTK_TYPE_ENTRY, "PsppireCustomEntry", 
+                                       &ce_info, 0);
+    }
+
+  return ce_type;
+}
+
+
+static void
+psppire_custom_entry_map (GtkWidget *widget)
+{
+  if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
+    {
+      GTK_WIDGET_CLASS (parent_class)->map (widget);
+      gdk_window_show (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
+    }
+}
+
+static void
+psppire_custom_entry_unmap (GtkWidget *widget)
+{
+  if (GTK_WIDGET_MAPPED (widget))
+    {
+      gdk_window_hide (PSPPIRE_CUSTOM_ENTRY (widget)->panel);
+      GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+    }
+}
+
+static gint psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry);
+
+static void
+psppire_custom_entry_realize (GtkWidget *widget)
+{
+  PsppireCustomEntry *custom_entry;
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+  guint real_width;
+  gint button_size ;
+
+  custom_entry = PSPPIRE_CUSTOM_ENTRY (widget);
+
+  button_size = psppire_custom_entry_get_button_width (custom_entry);
+
+  real_width = widget->allocation.width;
+  widget->allocation.width -= button_size + 2 * widget->style->xthickness;
+  gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
+                        GDK_KEY_RELEASE_MASK);
+  GTK_WIDGET_CLASS (parent_class)->realize (widget);
+
+  widget->allocation.width = real_width;
+  
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes.event_mask = gtk_widget_get_events (widget);
+  attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK 
+    | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK 
+    | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+  attributes.x = (widget->allocation.x +
+                 widget->allocation.width - button_size -
+                 2 * widget->style->xthickness);
+  attributes.y = widget->allocation.y + (widget->allocation.height -
+                                        widget->requisition.height) / 2;
+  attributes.width = button_size + 2 * widget->style->xthickness;
+  attributes.height = widget->requisition.height;
+  
+  custom_entry->panel = gdk_window_new (gtk_widget_get_parent_window (widget), 
+                                       &attributes, attributes_mask);
+  gdk_window_set_user_data (custom_entry->panel, widget);
+
+  gtk_style_set_background (widget->style, custom_entry->panel, GTK_STATE_NORMAL);
+
+
+  gtk_widget_queue_resize (GTK_WIDGET (custom_entry));
+}
+
+
+#define MIN_BUTTON_WIDTH  6
+
+static gint
+psppire_custom_entry_get_button_width (PsppireCustomEntry *custom_entry)
+{
+  const gint size = pango_font_description_get_size 
+    (GTK_WIDGET (custom_entry)->style->font_desc);
+
+  gint button_width = MAX (PANGO_PIXELS (size), MIN_BUTTON_WIDTH);
+
+  return button_width - button_width % 2; /* force even */
+}
+
+/**
+ * custom_entry_get_shadow_type:
+ * @custom_entry: a #PsppireCustomEntry
+ * 
+ * Convenience function to Get the shadow type from the underlying widget's
+ * style.
+ * 
+ * Return value: the #GtkShadowType
+ **/
+static gint
+psppire_custom_entry_get_shadow_type (PsppireCustomEntry *custom_entry)
+{
+  GtkShadowType rc_shadow_type;
+
+  gtk_widget_style_get (GTK_WIDGET (custom_entry), "shadow_type", &rc_shadow_type, NULL);
+
+  return rc_shadow_type;
+}
+
+
+static void
+psppire_custom_entry_unrealize (GtkWidget *widget)
+{
+  PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
+
+  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
+
+  if (ce->panel)
+    {
+      gdk_window_set_user_data (ce->panel, NULL);
+      gdk_window_destroy (ce->panel);
+      ce->panel = NULL;
+    }
+}
+
+
+static void
+psppire_custom_entry_redraw (PsppireCustomEntry *custom_entry)
+{
+  GtkWidget *widget;
+
+  widget = GTK_WIDGET (custom_entry);
+
+  if (GTK_WIDGET_DRAWABLE (widget))
+    {
+      gtk_widget_queue_draw (widget);
+
+      /* We must invalidate the panel window ourselves, because it
+       * is not a child of widget->window
+       */
+      gdk_window_invalidate_rect (custom_entry->panel, NULL, TRUE);
+    }
+}        
+
+
+static gint
+psppire_custom_entry_expose (GtkWidget      *widget,
+                    GdkEventExpose *event)
+{
+  g_return_val_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY(widget);
+
+  if (GTK_WIDGET_DRAWABLE (widget))
+    {
+      GtkShadowType shadow_type;
+      GdkRectangle rect;
+
+      rect.x = 0;
+      rect.y = 0;
+
+      if (event->window != ce->panel)
+       GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
+
+      gdk_drawable_get_size (ce->panel, &rect.width, &rect.height);
+
+      gdk_window_begin_paint_rect (ce->panel, &rect);      
+
+
+      shadow_type = psppire_custom_entry_get_shadow_type (ce);
+      
+      if (shadow_type != GTK_SHADOW_NONE)
+       {
+         gtk_paint_box (widget->style, ce->panel,
+                        GTK_STATE_NORMAL, shadow_type,
+                        NULL, widget, "customentry",
+                        rect.x, rect.y, rect.width, rect.height);
+
+       }
+
+      gdk_window_end_paint (ce->panel);
+    }
+
+  return FALSE;
+}
+
+
+static gint
+psppire_custom_entry_button_press (GtkWidget      *widget,
+                          GdkEventButton *event);
+
+static void
+psppire_custom_entry_size_allocate (GtkWidget     *widget,
+                           GtkAllocation *allocation);
+
+
+
+static void
+psppire_custom_entry_class_init (PsppireCustomEntryClass *klass)
+{
+  GObjectClass     *gobject_class = G_OBJECT_CLASS (klass);
+
+  GtkWidgetClass   *widget_class;
+  GtkEntryClass   *entry_class;
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  widget_class   = (GtkWidgetClass*)   klass;
+  entry_class   = (GtkEntryClass*)   klass;
+
+  widget_class->map = psppire_custom_entry_map;
+  widget_class->unmap = psppire_custom_entry_unmap;
+
+  widget_class->realize = psppire_custom_entry_realize;
+  widget_class->unrealize = psppire_custom_entry_unrealize;
+
+  widget_class->expose_event = psppire_custom_entry_expose;
+  widget_class->button_press_event = psppire_custom_entry_button_press;
+
+  widget_class->size_allocate = psppire_custom_entry_size_allocate;
+
+
+  gtk_widget_class_install_style_property_parser 
+    (widget_class,
+     g_param_spec_enum ("shadow_type", 
+                       "Shadow Type", 
+                       P_("Style of bevel around the custom entry button"),
+                       GTK_TYPE_SHADOW_TYPE,
+                       GTK_SHADOW_ETCHED_IN,
+                       G_PARAM_READABLE),
+     gtk_rc_property_parse_enum);
+
+  custom_entry_signals[CLICKED] = 
+    g_signal_new ("clicked",
+                 G_TYPE_FROM_CLASS(gobject_class),
+                 G_SIGNAL_RUN_LAST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 
+                 0);
+
+
+}
+
+static void
+psppire_custom_entry_init (PsppireCustomEntry *ce)
+{
+}
+
+GtkWidget*
+psppire_custom_entry_new ()
+{
+  return GTK_WIDGET (g_object_new (psppire_custom_entry_get_type (), NULL));
+}
+
+
+
+static gint
+psppire_custom_entry_button_press (GtkWidget *widget,
+                                  GdkEventButton *event)
+{
+  PsppireCustomEntry *ce = PSPPIRE_CUSTOM_ENTRY (widget);
+
+  if (event->window == ce->panel)
+    {
+      if (!GTK_WIDGET_HAS_FOCUS (widget))
+       gtk_widget_grab_focus (widget);
+
+      if ( event->button == 1)
+       g_signal_emit (widget, custom_entry_signals[CLICKED], 0);
+
+    }
+  else
+    return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
+
+  return FALSE;
+}
+
+
+
+static void
+psppire_custom_entry_size_allocate (GtkWidget     *widget,
+                           GtkAllocation *allocation)
+{
+  PsppireCustomEntry *ce;
+  GtkAllocation entry_allocation;
+  GtkAllocation panel_allocation;
+  gint button_width;
+  gint panel_width;
+
+  g_return_if_fail (PSPPIRE_IS_CUSTOM_ENTRY (widget));
+  g_return_if_fail (allocation != NULL);
+
+  ce = PSPPIRE_CUSTOM_ENTRY (widget);
+  button_width = psppire_custom_entry_get_button_width(ce);
+  panel_width = button_width + 2 * widget->style->xthickness;
+  
+  widget->allocation = *allocation;
+  
+  entry_allocation = *allocation;
+  entry_allocation.width -= panel_width;
+
+  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+    {
+      entry_allocation.x += panel_width;
+      panel_allocation.x = allocation->x;
+    }
+  else
+    {
+      panel_allocation.x = allocation->x + allocation->width - panel_width;
+    }
+
+  panel_allocation.width = panel_width;
+  panel_allocation.height = MIN (widget->requisition.height, allocation->height);
+
+  panel_allocation.y = allocation->y + (allocation->height -
+                                       panel_allocation.height) / 2;
+
+  GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &entry_allocation);
+
+  if (GTK_WIDGET_REALIZED (widget))
+    {
+      gdk_window_move_resize (PSPPIRE_CUSTOM_ENTRY (widget)->panel, 
+                             panel_allocation.x,
+                             panel_allocation.y,
+                             panel_allocation.width,
+                             panel_allocation.height); 
+    }
+
+  psppire_custom_entry_redraw (ce);
+}
+
+
+
+
+