--- /dev/null
+/* PSPPIRE - a graphical user interface for PSPP.
+ Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "ui/gui/psppire-button-editable.h"
+
+#include <gettext.h>
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+/* GtkCellEditable interface. */
+static void gtk_cell_editable_interface_init (GtkCellEditableIface *iface);
+static void button_editable_editing_done (GtkCellEditable *cell_editable);
+static void button_editable_remove_widget (GtkCellEditable *cell_editable);
+static void button_editable_start_editing (GtkCellEditable *cell_editable,
+ GdkEvent *event);
+
+G_DEFINE_TYPE_EXTENDED (PsppireButtonEditable,
+ psppire_button_editable,
+ GTK_TYPE_BUTTON,
+ 0,
+ G_IMPLEMENT_INTERFACE (
+ GTK_TYPE_CELL_EDITABLE,
+ gtk_cell_editable_interface_init));
+
+enum
+ {
+ PROP_0,
+ PROP_PATH
+ };
+
+static void
+psppire_button_editable_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PsppireButtonEditable *obj = PSPPIRE_BUTTON_EDITABLE (object);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_free (obj->path);
+ obj->path = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+psppire_button_editable_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PsppireButtonEditable *obj = PSPPIRE_BUTTON_EDITABLE (object);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_value_set_string (value, obj->path);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+psppire_button_editable_dispose (GObject *gobject)
+{
+ PsppireButtonEditable *obj = PSPPIRE_BUTTON_EDITABLE (gobject);
+
+ g_free (obj->path);
+ obj->path = NULL;
+
+ G_OBJECT_CLASS (psppire_button_editable_parent_class)->dispose (gobject);
+}
+
+static gboolean
+psppire_button_editable_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkButton *button;
+
+ if (event->button == 1)
+ {
+ button = GTK_BUTTON (widget);
+ gtk_button_released (button);
+ }
+
+ return TRUE;
+}
+
+static void
+psppire_button_editable_class_init (PsppireButtonEditableClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+
+ gobject_class = G_OBJECT_CLASS (class);
+ widget_class = GTK_WIDGET_CLASS (class);
+
+ gobject_class->set_property = psppire_button_editable_set_property;
+ gobject_class->get_property = psppire_button_editable_get_property;
+ gobject_class->dispose = psppire_button_editable_dispose;
+
+ widget_class->button_release_event = psppire_button_editable_button_release;
+
+ g_object_class_install_property (G_OBJECT_CLASS (class),
+ PROP_PATH,
+ g_param_spec_string ("path",
+ _("TreeView path"),
+ _("The path to the row in the GtkTreeView, as a string"),
+ "",
+ G_PARAM_READWRITE));
+}
+
+static void
+psppire_button_editable_init (PsppireButtonEditable *obj)
+{
+ obj->path = g_strdup ("");
+}
+
+PsppireButtonEditable *
+psppire_button_editable_new (void)
+{
+ return PSPPIRE_BUTTON_EDITABLE (g_object_new (PSPPIRE_TYPE_BUTTON_EDITABLE, NULL));
+}
+\f
+/* GtkCellEditable interface. */
+
+static void
+gtk_cell_editable_interface_init (GtkCellEditableIface *iface)
+{
+ g_return_if_fail (iface != NULL);
+
+ iface->editing_done = button_editable_editing_done;
+ iface->remove_widget = button_editable_remove_widget;
+ iface->start_editing = button_editable_start_editing;
+}
+
+static void
+button_editable_editing_done (GtkCellEditable *cell_editable)
+{
+
+
+}
+
+static void
+button_editable_remove_widget (GtkCellEditable *cell_editable)
+{
+
+
+}
+
+static void
+button_editable_start_editing (GtkCellEditable *cell_editable,
+ GdkEvent *event)
+{
+}
--- /dev/null
+/* PSPPIRE - a graphical user interface for PSPP.
+ Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "ui/gui/psppire-cell-renderer-button.h"
+
+#include <math.h>
+#include <string.h>
+
+#include "ui/gui/psppire-button-editable.h"
+#include "ui/gui/pspp-widget-facade.h"
+
+#include "gl/configmake.h"
+#include "gl/relocatable.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+static void psppire_cell_renderer_button_destroy (GtkObject *);
+static void psppire_cell_renderer_button_finalize (GObject *);
+
+static void update_style_cache (PsppireCellRendererButton *button,
+ GtkWidget *widget);
+
+static void psppire_cell_renderer_button_load_gtkrc (void);
+
+
+G_DEFINE_TYPE_EXTENDED (PsppireCellRendererButton,
+ psppire_cell_renderer_button,
+ GTK_TYPE_CELL_RENDERER,
+ 0,
+ psppire_cell_renderer_button_load_gtkrc ());
+
+static void
+psppire_cell_renderer_button_load_gtkrc (void)
+{
+ const char *gtkrc_file;
+
+ gtkrc_file = relocate (PKGDATADIR "/psppire.gtkrc");
+ gtk_rc_add_default_file (gtkrc_file);
+ gtk_rc_parse (gtkrc_file);
+}
+
+enum
+ {
+ PROP_0,
+ PROP_EDITABLE,
+ PROP_LABEL
+ };
+
+static void
+psppire_cell_renderer_button_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PsppireCellRendererButton *obj = PSPPIRE_CELL_RENDERER_BUTTON (object);
+
+ switch (prop_id)
+ {
+ case PROP_EDITABLE:
+ obj->editable = g_value_get_boolean (value);
+ if (obj->editable)
+ GTK_CELL_RENDERER (obj)->mode = GTK_CELL_RENDERER_MODE_EDITABLE;
+ else
+ GTK_CELL_RENDERER (obj)->mode = GTK_CELL_RENDERER_MODE_INERT;
+ break;
+
+ case PROP_LABEL:
+ g_free (obj->label);
+ obj->label = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+psppire_cell_renderer_button_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PsppireCellRendererButton *obj = PSPPIRE_CELL_RENDERER_BUTTON (object);
+
+ switch (prop_id)
+ {
+ case PROP_EDITABLE:
+ g_value_set_boolean (value, obj->editable);
+ break;
+
+ case PROP_LABEL:
+ g_value_set_string (value, obj->label);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+on_style_set (GtkWidget *base,
+ GtkStyle *previous_style,
+ PsppireCellRendererButton *button)
+{
+ update_style_cache (button, NULL);
+}
+
+static void
+on_destroy (GtkObject *base,
+ PsppireCellRendererButton *button)
+{
+ update_style_cache (button, NULL);
+}
+
+static void
+update_style_cache (PsppireCellRendererButton *button,
+ GtkWidget *widget)
+{
+ if (button->base == widget)
+ return;
+
+ /* Clear old cache. */
+ if (button->button_style)
+ {
+ g_object_unref (button->button_style);
+ button->button_style = NULL;
+ }
+ if (button->label_style)
+ {
+ g_object_unref (button->label_style);
+ button->label_style = NULL;
+ }
+ if (button->base != NULL)
+ {
+ if (button->style_set_handler)
+ {
+ g_signal_handler_disconnect (button->base,
+ button->style_set_handler);
+ button->style_set_handler = 0;
+ }
+ if (button->destroy_handler)
+ {
+ g_signal_handler_disconnect (button->base, button->destroy_handler);
+ button->destroy_handler = 0;
+ }
+ g_object_unref (button->base);
+ button->base = NULL;
+ }
+
+ /* Populate cache. */
+ if (widget)
+ {
+ button->button_style = facade_get_style (widget, GTK_TYPE_BUTTON, 0);
+ button->label_style = facade_get_style (widget, GTK_TYPE_BUTTON,
+ GTK_TYPE_LABEL, 0);
+ button->base = widget;
+ button->style_set_handler = g_signal_connect (widget, "style-set",
+ G_CALLBACK (on_style_set),
+ button);
+ button->destroy_handler = g_signal_connect (widget, "destroy",
+ G_CALLBACK (on_destroy),
+ button);
+
+ g_object_ref (widget);
+ g_object_ref (button->button_style);
+ g_object_ref (button->label_style);
+ }
+}
+
+static void
+psppire_cell_renderer_button_render (GtkCellRenderer *cell,
+ GdkDrawable *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ GtkCellRendererState flags)
+{
+ PsppireCellRendererButton *button = PSPPIRE_CELL_RENDERER_BUTTON (cell);
+ GtkStateType state_type;
+
+ if (!button->editable || !cell->sensitive)
+ state_type = GTK_STATE_INSENSITIVE;
+ else if (flags & GTK_CELL_RENDERER_SELECTED)
+ {
+ if (gtk_widget_has_focus (widget))
+ state_type = GTK_STATE_SELECTED;
+ else
+ state_type = GTK_STATE_ACTIVE;
+ }
+ else if (flags & GTK_CELL_RENDERER_PRELIT)
+ state_type = GTK_STATE_PRELIGHT;
+ else
+ {
+ if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE)
+ state_type = GTK_STATE_INSENSITIVE;
+ else
+ state_type = GTK_STATE_NORMAL;
+ }
+
+ update_style_cache (button, widget);
+ facade_button_render (widget, window, expose_area,
+ cell_area, button->border_width, button->button_style,
+ state_type,
+ button->label_style, button->label, button->xpad,
+ button->ypad, cell->xalign, cell->yalign);
+}
+
+static void
+psppire_cell_renderer_button_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GdkRectangle *cell_area,
+ gint *x_offset,
+ gint *y_offset,
+ gint *width,
+ gint *height)
+{
+ PsppireCellRendererButton *button = PSPPIRE_CELL_RENDERER_BUTTON (cell);
+
+ update_style_cache (button, widget);
+ if (cell_area != NULL)
+ {
+ /* The caller is really asking for the placement of the focus rectangle.
+ The focus rectangle should surround the whole label area, so calculate
+ that area. */
+ GtkBorder inset;
+
+ facade_button_get_focus_inset (button->border_width, widget,
+ button->button_style, &inset);
+
+ if (x_offset)
+ *x_offset = inset.left;
+ if (y_offset)
+ *y_offset = inset.top;
+ if (width)
+ *width = MAX (1, cell_area->width - inset.left - inset.right);
+ if (height)
+ *height = MAX (1, cell_area->height - inset.top - inset.bottom);
+ }
+ else
+ {
+ /* The caller is asking for the preferred size of the cell. */
+ GtkRequisition label_req;
+ GtkRequisition request;
+
+ facade_label_get_size_request (button->xpad, button->ypad,
+ widget, button->label, &label_req);
+ facade_button_get_size_request (button->border_width, widget,
+ button->button_style, &label_req,
+ &request);
+
+ if (x_offset)
+ *x_offset = 0;
+ if (y_offset)
+ *y_offset = 0;
+ if (width)
+ *width = request.width;
+ if (height)
+ *height = request.height;
+ }
+}
+
+static void
+psppire_cell_renderer_button_clicked (GtkButton *button,
+ gpointer data)
+{
+ PsppireCellRendererButton *cell_button = data;
+ gchar *path;
+
+ g_object_get (button, "path", &path, NULL);
+ g_signal_emit_by_name (cell_button, "clicked", path);
+ g_free (path);
+}
+
+static gboolean
+psppire_cell_renderer_button_focus_out_event (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer data)
+{
+ PsppireCellRendererButton *cell_button = data;
+
+ g_signal_handlers_disconnect_by_func (widget,
+ psppire_cell_renderer_button_focus_out_event,
+ data);
+ g_signal_handlers_disconnect_by_func (widget,
+ psppire_cell_renderer_button_clicked,
+ data);
+
+ gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (cell_button), FALSE);
+
+ return FALSE;
+}
+
+#define IDLE_ID_STRING "psppire-cell-renderer-button-idle-id"
+
+static gboolean
+psppire_cell_renderer_button_initial_click (gpointer data)
+{
+ GtkButton *button = data;
+
+ gtk_button_clicked (button);
+ g_object_steal_data (G_OBJECT (button), IDLE_ID_STRING);
+ return FALSE;
+}
+
+static void
+psppire_cell_renderer_button_on_destroy (GObject *object, gpointer user_data)
+{
+ guint idle_id;
+
+ idle_id = GPOINTER_TO_INT (g_object_steal_data (object, IDLE_ID_STRING));
+ if (idle_id != 0)
+ g_source_remove (idle_id);
+}
+
+static void
+psppire_cell_renderer_button_double_click (GtkButton *button,
+ PsppireCellRendererButton *cell_button)
+{
+ gchar *path;
+
+ if (g_object_get_data (G_OBJECT (button), IDLE_ID_STRING))
+ psppire_cell_renderer_button_initial_click (button);
+
+ g_object_get (button, "path", &path, NULL);
+ g_signal_emit_by_name (cell_button, "double-clicked", path);
+ g_free (path);
+}
+
+static gboolean
+psppire_cell_renderer_button_press_event (GtkButton *button,
+ GdkEventButton *event,
+ gpointer data)
+{
+ PsppireCellRendererButton *cell_button = data;
+
+ if (cell_button->click_time != 0)
+ {
+ GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (button));
+ GtkSettings *settings = gtk_settings_get_for_screen (screen);
+ gint double_click_distance;
+ gint double_click_time;
+
+ g_object_get (settings,
+ "gtk-double-click-time", &double_click_time,
+ "gtk-double-click-distance", &double_click_distance,
+ NULL);
+
+ if (event->type == GDK_BUTTON_PRESS
+ && event->button == 1
+ && event->time <= cell_button->click_time + double_click_time
+ && ABS (event->x_root - cell_button->click_x) <= double_click_distance
+ && ABS (event->y_root - cell_button->click_y) <= double_click_distance)
+ {
+ psppire_cell_renderer_button_double_click (button, cell_button);
+ return TRUE;
+ }
+
+ cell_button->click_time = 0;
+ }
+
+ if (event->type == GDK_2BUTTON_PRESS)
+ {
+ psppire_cell_renderer_button_double_click (button, cell_button);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GtkCellEditable *
+psppire_cell_renderer_button_start_editing (GtkCellRenderer *cell,
+ GdkEvent *event,
+ GtkWidget *widget,
+ const gchar *path,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GtkCellRendererState flags)
+{
+ PsppireCellRendererButton *cell_button = PSPPIRE_CELL_RENDERER_BUTTON (cell);
+ gfloat xalign, yalign;
+
+ gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
+ cell_button->button = g_object_new (PSPPIRE_TYPE_BUTTON_EDITABLE,
+ "label", cell_button->label,
+ "xalign", xalign,
+ "yalign", yalign,
+ "path", path,
+ NULL);
+
+ g_signal_connect (G_OBJECT (cell_button->button), "focus-out-event",
+ G_CALLBACK (psppire_cell_renderer_button_focus_out_event),
+ cell);
+ g_signal_connect (G_OBJECT (cell_button->button), "clicked",
+ G_CALLBACK (psppire_cell_renderer_button_clicked),
+ cell);
+ g_signal_connect (G_OBJECT (cell_button->button), "button-press-event",
+ G_CALLBACK (psppire_cell_renderer_button_press_event),
+ cell);
+
+ gtk_widget_show (cell_button->button);
+
+ if (event != NULL && event->any.type == GDK_BUTTON_RELEASE)
+ {
+ guint idle_id;
+
+ cell_button->click_time = event->button.time;
+ cell_button->click_x = event->button.x_root;
+ cell_button->click_y = event->button.y_root;
+ idle_id = g_idle_add (psppire_cell_renderer_button_initial_click,
+ cell_button->button);
+ g_object_set_data (G_OBJECT (cell_button->button), IDLE_ID_STRING,
+ GINT_TO_POINTER (idle_id));
+ g_signal_connect (G_OBJECT (cell_button->button), "destroy",
+ G_CALLBACK (psppire_cell_renderer_button_on_destroy),
+ NULL);
+ }
+ else
+ {
+ cell_button->click_time = 0;
+ cell_button->click_x = 0;
+ cell_button->click_y = 0;
+ }
+
+ return GTK_CELL_EDITABLE (cell_button->button);
+}
+
+static void
+psppire_cell_renderer_button_class_init (PsppireCellRendererButtonClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
+
+ gobject_class->set_property = psppire_cell_renderer_button_set_property;
+ gobject_class->get_property = psppire_cell_renderer_button_get_property;
+ gobject_class->finalize = psppire_cell_renderer_button_finalize;
+
+ gtk_object_class->destroy = psppire_cell_renderer_button_destroy;
+
+ cell_class->get_size = psppire_cell_renderer_button_get_size;
+ cell_class->render = psppire_cell_renderer_button_render;
+ cell_class->start_editing = psppire_cell_renderer_button_start_editing;
+
+ g_signal_new ("clicked",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+
+ g_signal_new ("double-clicked",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+
+ g_object_class_install_property (gobject_class,
+ PROP_EDITABLE,
+ g_param_spec_boolean ("editable",
+ "Editable",
+ "Whether the button may be clicked.",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_LABEL,
+ g_param_spec_string ("label",
+ "Label",
+ "Text to appear in button.",
+ "",
+ G_PARAM_READWRITE));
+}
+
+static void
+psppire_cell_renderer_button_init (PsppireCellRendererButton *obj)
+{
+ obj->editable = FALSE;
+ obj->label = g_strdup ("");
+ obj->border_width = 0;
+ obj->xpad = 0;
+ obj->ypad = 0;
+
+ obj->button = NULL;
+
+ obj->button_style = NULL;
+ obj->label_style = NULL;
+ obj->base = NULL;
+ obj->style_set_handler = 0;
+ obj->destroy_handler = 0;
+}
+
+static void
+psppire_cell_renderer_button_finalize (GObject *obj)
+{
+ PsppireCellRendererButton *button = PSPPIRE_CELL_RENDERER_BUTTON (obj);
+
+ g_free (button->label);
+}
+
+static void
+psppire_cell_renderer_button_destroy (GtkObject *obj)
+{
+ PsppireCellRendererButton *button = PSPPIRE_CELL_RENDERER_BUTTON (obj);
+
+ update_style_cache (button, NULL);
+
+ GTK_OBJECT_CLASS (psppire_cell_renderer_button_parent_class)->destroy (obj);
+}
+
+GtkCellRenderer *
+psppire_cell_renderer_button_new (void)
+{
+ return GTK_CELL_RENDERER (g_object_new (PSPPIRE_TYPE_CELL_RENDERER_BUTTON, NULL));
+}