summaryrefslogtreecommitdiffstats
path: root/src/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets')
-rw-r--r--src/widgets/.cvsignore5
-rw-r--r--src/widgets/Makefile_insert75
-rw-r--r--src/widgets/button.cpp324
-rw-r--r--src/widgets/button.h69
-rw-r--r--src/widgets/dash-selector.cpp379
-rw-r--r--src/widgets/dash-selector.h48
-rw-r--r--src/widgets/desktop-widget.cpp1270
-rw-r--r--src/widgets/desktop-widget.h201
-rw-r--r--src/widgets/font-selector.cpp722
-rw-r--r--src/widgets/font-selector.h67
-rw-r--r--src/widgets/gradient-image.cpp277
-rw-r--r--src/widgets/gradient-image.h46
-rw-r--r--src/widgets/gradient-selector.cpp342
-rw-r--r--src/widgets/gradient-selector.h79
-rw-r--r--src/widgets/gradient-toolbar.cpp665
-rw-r--r--src/widgets/gradient-toolbar.h21
-rw-r--r--src/widgets/gradient-vector.cpp1115
-rw-r--r--src/widgets/gradient-vector.h61
-rw-r--r--src/widgets/icon.cpp924
-rw-r--r--src/widgets/icon.h51
-rw-r--r--src/widgets/layer-selector.cpp588
-rw-r--r--src/widgets/layer-selector.h110
-rw-r--r--src/widgets/makefile.in17
-rw-r--r--src/widgets/paint-selector.cpp963
-rw-r--r--src/widgets/paint-selector.h110
-rw-r--r--src/widgets/ruler.cpp663
-rw-r--r--src/widgets/ruler.h84
-rw-r--r--src/widgets/select-toolbar.cpp540
-rw-r--r--src/widgets/select-toolbar.h34
-rw-r--r--src/widgets/shrink-wrap-button.cpp55
-rw-r--r--src/widgets/shrink-wrap-button.h35
-rw-r--r--src/widgets/sp-color-gtkselector.cpp170
-rw-r--r--src/widgets/sp-color-gtkselector.h55
-rw-r--r--src/widgets/sp-color-notebook.cpp625
-rw-r--r--src/widgets/sp-color-notebook.h105
-rw-r--r--src/widgets/sp-color-preview.cpp196
-rw-r--r--src/widgets/sp-color-preview.h47
-rw-r--r--src/widgets/sp-color-scales.cpp741
-rw-r--r--src/widgets/sp-color-scales.h108
-rw-r--r--src/widgets/sp-color-selector.cpp351
-rw-r--r--src/widgets/sp-color-selector.h95
-rw-r--r--src/widgets/sp-color-slider.cpp687
-rw-r--r--src/widgets/sp-color-slider.h67
-rw-r--r--src/widgets/sp-color-wheel-selector.cpp295
-rw-r--r--src/widgets/sp-color-wheel-selector.h90
-rw-r--r--src/widgets/sp-color-wheel.cpp1163
-rw-r--r--src/widgets/sp-color-wheel.h80
-rw-r--r--src/widgets/sp-widget.cpp260
-rw-r--r--src/widgets/sp-widget.h52
-rw-r--r--src/widgets/sp-xmlview-attr-list.cpp181
-rw-r--r--src/widgets/sp-xmlview-attr-list.h54
-rw-r--r--src/widgets/sp-xmlview-content.cpp165
-rw-r--r--src/widgets/sp-xmlview-content.h53
-rw-r--r--src/widgets/sp-xmlview-tree.cpp424
-rw-r--r--src/widgets/sp-xmlview-tree.h55
-rw-r--r--src/widgets/spinbutton-events.cpp137
-rw-r--r--src/widgets/spinbutton-events.h30
-rw-r--r--src/widgets/spw-utilities.cpp255
-rw-r--r--src/widgets/spw-utilities.h56
-rw-r--r--src/widgets/toolbox.cpp3028
-rw-r--r--src/widgets/toolbox.h57
-rw-r--r--src/widgets/widget-sizes.h52
62 files changed, 19674 insertions, 0 deletions
diff --git a/src/widgets/.cvsignore b/src/widgets/.cvsignore
new file mode 100644
index 000000000..e8014d011
--- /dev/null
+++ b/src/widgets/.cvsignore
@@ -0,0 +1,5 @@
+Makefile
+Makefile.in
+.deps
+makefile
+.dirstamp
diff --git a/src/widgets/Makefile_insert b/src/widgets/Makefile_insert
new file mode 100644
index 000000000..854ca9122
--- /dev/null
+++ b/src/widgets/Makefile_insert
@@ -0,0 +1,75 @@
+## Makefile.am fragment sourced by src/Makefile.am.
+
+#
+# Widgets for inkscape
+# Author: Lauris Kaplinski <lauris@ximian.com>
+#
+
+widgets/all: widgets/libspwidgets.a
+
+widgets/clean:
+ rm -f widgets/libspwidgets.a $(widgets_libspwidgets_a_OBJECTS)
+
+widgets_libspwidgets_a_SOURCES = \
+ widgets/button.cpp \
+ widgets/button.h \
+ widgets/dash-selector.cpp \
+ widgets/dash-selector.h \
+ widgets/desktop-widget.cpp \
+ widgets/desktop-widget.h \
+ widgets/font-selector.cpp \
+ widgets/font-selector.h \
+ widgets/gradient-toolbar.cpp \
+ widgets/gradient-toolbar.h \
+ widgets/gradient-image.cpp \
+ widgets/gradient-image.h \
+ widgets/gradient-selector.cpp \
+ widgets/gradient-selector.h \
+ widgets/gradient-vector.cpp \
+ widgets/gradient-vector.h \
+ widgets/icon.cpp \
+ widgets/icon.h \
+ widgets/layer-selector.h \
+ widgets/layer-selector.cpp \
+ widgets/paint-selector.cpp \
+ widgets/paint-selector.h \
+ widgets/ruler.cpp \
+ widgets/ruler.h \
+ widgets/select-toolbar.cpp \
+ widgets/select-toolbar.h \
+ widgets/shrink-wrap-button.cpp \
+ widgets/shrink-wrap-button.h \
+ widgets/sp-color-gtkselector.cpp \
+ widgets/sp-color-gtkselector.h \
+ widgets/sp-color-notebook.cpp \
+ widgets/sp-color-notebook.h \
+ widgets/sp-color-preview.cpp \
+ widgets/sp-color-preview.h \
+ widgets/sp-color-scales.cpp \
+ widgets/sp-color-scales.h \
+ widgets/sp-color-selector.cpp \
+ widgets/sp-color-selector.h \
+ widgets/sp-color-slider.cpp \
+ widgets/sp-color-slider.h \
+ widgets/sp-color-wheel-selector.cpp \
+ widgets/sp-color-wheel-selector.h \
+ widgets/sp-color-wheel.cpp \
+ widgets/sp-color-wheel.h \
+ widgets/sp-widget.cpp \
+ widgets/sp-widget.h\
+ widgets/sp-xmlview-attr-list.cpp \
+ widgets/sp-xmlview-attr-list.h \
+ widgets/sp-xmlview-content.cpp \
+ widgets/sp-xmlview-content.h \
+ widgets/sp-xmlview-tree.cpp \
+ widgets/sp-xmlview-tree.h \
+ widgets/spinbutton-events.cpp \
+ widgets/spinbutton-events.h \
+ widgets/spw-utilities.cpp \
+ widgets/spw-utilities.h \
+ widgets/toolbox.cpp \
+ widgets/toolbox.h \
+ widgets/widget-sizes.h
+
+widgets/button.$(OBJEXT): helper/sp-marshal.h
+widgets/menu.$(OBJEXT): helper/sp-marshal.h
diff --git a/src/widgets/button.cpp b/src/widgets/button.cpp
new file mode 100644
index 000000000..4860c9667
--- /dev/null
+++ b/src/widgets/button.cpp
@@ -0,0 +1,324 @@
+#define __SP_BUTTON_C__
+
+/*
+ * Generic button widget
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 2002 Lauris Kaplinski
+ *
+ * This code is in public domain
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+
+#if HAVE_STRING_H
+#endif
+
+
+
+
+#include "shortcuts.h"
+#include "interface.h"
+
+#include "icon.h"
+#include "button.h"
+
+static void sp_button_class_init (SPButtonClass *klass);
+static void sp_button_init (SPButton *button);
+static void sp_button_destroy (GtkObject *object);
+
+static void sp_button_size_request (GtkWidget *widget, GtkRequisition *requisition);
+static void sp_button_clicked (GtkButton *button);
+static void sp_button_perform_action (SPButton *button, gpointer data);
+static gint sp_button_process_event (SPButton *button, GdkEvent *event);
+
+static void sp_button_set_action (SPButton *button, SPAction *action);
+static void sp_button_set_doubleclick_action (SPButton *button, SPAction *action);
+static void sp_button_action_set_active (SPAction *action, unsigned int active, void *data);
+static void sp_button_action_set_sensitive (SPAction *action, unsigned int sensitive, void *data);
+static void sp_button_action_set_shortcut (SPAction *action, unsigned int shortcut, void *data);
+static void sp_button_set_composed_tooltip (GtkTooltips *tooltips, GtkWidget *widget, SPAction *action);
+
+static GtkToggleButtonClass *parent_class;
+SPActionEventVector button_event_vector = {
+ {NULL},
+ NULL,
+ sp_button_action_set_active,
+ sp_button_action_set_sensitive,
+ sp_button_action_set_shortcut
+};
+
+GtkType
+sp_button_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GtkTypeInfo info = {
+ "SPButton",
+ sizeof (SPButton),
+ sizeof (SPButtonClass),
+ (GtkClassInitFunc) sp_button_class_init,
+ (GtkObjectInitFunc) sp_button_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_TOGGLE_BUTTON, &info);
+ }
+ return type;
+}
+
+static void
+sp_button_class_init (SPButtonClass *klass)
+{
+ GtkObjectClass *object_class=(GtkObjectClass *)klass;
+ GtkWidgetClass *widget_class=(GtkWidgetClass *)klass;
+ GtkButtonClass *button_class=(GtkButtonClass *)klass;
+
+ parent_class = (GtkToggleButtonClass *)g_type_class_peek_parent (klass);
+
+ object_class->destroy = sp_button_destroy;
+ widget_class->size_request = sp_button_size_request;
+ button_class->clicked = sp_button_clicked;
+}
+
+static void
+sp_button_init (SPButton *button)
+{
+ button->action = NULL;
+ button->doubleclick_action = NULL;
+ button->tooltips = NULL;
+
+ gtk_container_set_border_width (GTK_CONTAINER (button), 0);
+
+ GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (button), GTK_CAN_FOCUS);
+ GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (button), GTK_CAN_DEFAULT);
+
+ g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (sp_button_perform_action), NULL);
+ g_signal_connect_after (G_OBJECT (button), "event", G_CALLBACK (sp_button_process_event), NULL);
+}
+
+static void
+sp_button_destroy (GtkObject *object)
+{
+ SPButton *button;
+
+ button = SP_BUTTON (object);
+
+ if (button->tooltips) {
+ g_object_unref (G_OBJECT (button->tooltips));
+ button->tooltips = NULL;
+ }
+
+ if (button->action) {
+ sp_button_set_action (button, NULL);
+ }
+
+ if (button->doubleclick_action) {
+ sp_button_set_doubleclick_action (button, NULL);
+ }
+
+ ((GtkObjectClass *) (parent_class))->destroy (object);
+}
+
+static void
+sp_button_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+ GtkWidget *child;
+
+ child = gtk_bin_get_child (GTK_BIN (widget));
+ if (child) {
+ gtk_widget_size_request (GTK_WIDGET (child), requisition);
+ } else {
+ requisition->width = 0;
+ requisition->height = 0;
+ }
+
+ requisition->width += 2 + 2 * MAX (2, widget->style->xthickness);
+ requisition->height += 2 + 2 * MAX (2, widget->style->ythickness);
+}
+
+static void
+sp_button_clicked (GtkButton *button)
+{
+ SPButton *sp_button=SP_BUTTON (button);
+
+ if (sp_button->type == SP_BUTTON_TYPE_TOGGLE) {
+ ((GtkButtonClass *) (parent_class))->clicked (button);
+ }
+}
+
+static gint
+sp_button_process_event (SPButton *button, GdkEvent *event)
+{
+ switch (event->type) {
+ case GDK_2BUTTON_PRESS:
+ if (button->doubleclick_action) {
+ sp_action_perform (button->doubleclick_action, NULL);
+ }
+ return TRUE;
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+sp_button_perform_action (SPButton *button, gpointer data)
+{
+ if (button->action) {
+ sp_action_perform (button->action, NULL);
+ }
+}
+
+
+GtkWidget *
+sp_button_new( GtkIconSize size, SPButtonType type, SPAction *action, SPAction *doubleclick_action, GtkTooltips *tooltips )
+{
+ SPButton *button;
+
+ button = (SPButton *)g_object_new (SP_TYPE_BUTTON, NULL);
+
+ button->type = type;
+ button->lsize = CLAMP( size, GTK_ICON_SIZE_MENU, GTK_ICON_SIZE_DIALOG );
+ button->tooltips = tooltips;
+
+ if (tooltips) g_object_ref ((GObject *) tooltips);
+
+ sp_button_set_action (button, action);
+ if (doubleclick_action)
+ sp_button_set_doubleclick_action (button, doubleclick_action);
+
+ // The Inkscape style is no-relief buttons
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+
+ return (GtkWidget *) button;
+}
+
+void
+sp_button_toggle_set_down (SPButton *button, gboolean down)
+{
+ g_return_if_fail (button->type == SP_BUTTON_TYPE_TOGGLE);
+ g_signal_handlers_block_by_func (G_OBJECT (button), (gpointer)G_CALLBACK (sp_button_perform_action), NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), (unsigned int)down);
+ g_signal_handlers_unblock_by_func (G_OBJECT (button), (gpointer)G_CALLBACK (sp_button_perform_action), NULL);
+}
+
+static void
+sp_button_set_doubleclick_action (SPButton *button, SPAction *action)
+{
+ if (button->doubleclick_action) {
+ nr_object_unref ((NRObject *) button->doubleclick_action);
+ }
+ button->doubleclick_action = action;
+ if (action) {
+ button->doubleclick_action = (SPAction *) nr_object_ref ((NRObject *) action);
+ }
+}
+
+static void
+sp_button_set_action (SPButton *button, SPAction *action)
+{
+ GtkWidget *child;
+
+ if (button->action) {
+ nr_active_object_remove_listener_by_data ((NRActiveObject *) button->action, button);
+ nr_object_unref ((NRObject *) button->action);
+ child = gtk_bin_get_child (GTK_BIN (button));
+ if (child) {
+ gtk_container_remove (GTK_CONTAINER (button), child);
+ }
+ }
+ button->action = action;
+ if (action) {
+ button->action = (SPAction *) nr_object_ref ((NRObject *) action);
+ nr_active_object_add_listener ((NRActiveObject *) action, (NRObjectEventVector *) &button_event_vector, sizeof (SPActionEventVector), button);
+ if (action->image) {
+ child = sp_icon_new (button->lsize, action->image);
+ gtk_widget_show (child);
+ gtk_container_add (GTK_CONTAINER (button), child);
+ }
+ }
+
+ if (button->tooltips) {
+ sp_button_set_composed_tooltip (button->tooltips, (GtkWidget *) button, action);
+ }
+}
+
+static void
+sp_button_action_set_active (SPAction *action, unsigned int active, void *data)
+{
+ SPButton *button;
+ button = (SPButton *) data;
+ if (button->type != SP_BUTTON_TYPE_TOGGLE) {
+ return;
+ }
+
+ /* temporarily lobotomized until SPActions are per-view */
+ if (0 && !active != !SP_BUTTON_IS_DOWN (button)) {
+ sp_button_toggle_set_down (button, active);
+ }
+}
+
+static void
+sp_button_action_set_sensitive (SPAction *action, unsigned int sensitive, void *data)
+{
+ gtk_widget_set_sensitive (GTK_WIDGET (data), sensitive);
+}
+
+static void
+sp_button_action_set_shortcut (SPAction *action, unsigned int shortcut, void *data)
+{
+ SPButton *button=SP_BUTTON (data);
+ if (button->tooltips) {
+ sp_button_set_composed_tooltip (button->tooltips, GTK_WIDGET (button), action);
+ }
+}
+
+static void
+sp_button_set_composed_tooltip (GtkTooltips *tooltips, GtkWidget *widget, SPAction *action)
+{
+ if (action) {
+ unsigned int shortcut = sp_shortcut_get_primary (action->verb);
+ if (shortcut) {
+ // there's both action and shortcut
+
+ gchar key[256];
+ sp_ui_shortcut_string (shortcut, key);
+
+ gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
+ gtk_tooltips_set_tip (tooltips, widget, tip, NULL);
+ g_free (tip);
+
+ } else {
+ // action has no shortcut
+ gtk_tooltips_set_tip (tooltips, widget, action->tip, NULL);
+ }
+ } else {
+ // no action
+ gtk_tooltips_set_tip (tooltips, widget, NULL, NULL);
+ }
+}
+
+GtkWidget *
+sp_button_new_from_data( GtkIconSize size,
+ SPButtonType type,
+ Inkscape::UI::View::View *view,
+ const gchar *name,
+ const gchar *tip,
+ GtkTooltips *tooltips )
+{
+ GtkWidget *button;
+ SPAction *action=sp_action_new(view, name, name, tip, name, 0);
+ button = sp_button_new (size, type, action, NULL, tooltips);
+ nr_object_unref ((NRObject *) action);
+ return button;
+}
+
diff --git a/src/widgets/button.h b/src/widgets/button.h
new file mode 100644
index 000000000..a29ffe76a
--- /dev/null
+++ b/src/widgets/button.h
@@ -0,0 +1,69 @@
+#ifndef __SP_BUTTON_H__
+#define __SP_BUTTON_H__
+
+/*
+ * Generic button widget
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2002 Lauris Kaplinski
+ *
+ * This code is in public domain
+ */
+
+#define SP_TYPE_BUTTON (sp_button_get_type ())
+#define SP_BUTTON(o) (GTK_CHECK_CAST ((o), SP_TYPE_BUTTON, SPButton))
+#define SP_IS_BUTTON(o) (GTK_CHECK_TYPE ((o), SP_TYPE_BUTTON))
+
+#include <gtk/gtkwidget.h>
+#include <gtk/gtktogglebutton.h>
+#include <gtk/gtktooltips.h>
+
+#include <helper/action.h>
+
+#include <glib.h>
+
+
+
+typedef enum {
+ SP_BUTTON_TYPE_NORMAL,
+ SP_BUTTON_TYPE_TOGGLE
+} SPButtonType;
+
+struct SPBChoiceData {
+ guchar *px;
+};
+
+struct SPButton {
+ GtkToggleButton widget;
+ SPButtonType type;
+ GtkIconSize lsize;
+ unsigned int psize;
+ SPAction *action;
+ SPAction *doubleclick_action;
+ GtkTooltips *tooltips;
+};
+
+struct SPButtonClass {
+ GtkToggleButtonClass parent_class;
+};
+
+#define SP_BUTTON_IS_DOWN(b) gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))
+
+GType sp_button_get_type (void);
+
+GtkWidget *sp_button_new (GtkIconSize size, SPButtonType type, SPAction *action, SPAction *doubleclick_action, GtkTooltips *tooltips);
+
+void sp_button_toggle_set_down (SPButton *button, gboolean down);
+
+GtkWidget *sp_button_new_from_data (GtkIconSize size,
+ SPButtonType type,
+ Inkscape::UI::View::View *view,
+ const gchar *name,
+ const gchar *tip,
+ GtkTooltips *tooltips);
+
+
+
+#endif
diff --git a/src/widgets/dash-selector.cpp b/src/widgets/dash-selector.cpp
new file mode 100644
index 000000000..6274215cf
--- /dev/null
+++ b/src/widgets/dash-selector.cpp
@@ -0,0 +1,379 @@
+#define __SP_DASH_SELECTOR_C__
+
+/*
+ * Optionmenu for selecting dash patterns
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#define DASH_PREVIEW_WIDTH 2
+#define DASH_PREVIEW_LENGTH 80
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <libnr/nr-macros.h>
+#include <gtk/gtk.h>
+
+#include <glibmm/i18n.h>
+#include "../style.h"
+#include "../dialogs/dialog-events.h"
+
+#include "dash-selector.h"
+
+enum {CHANGED, LAST_SIGNAL};
+
+struct SPDashSelector {
+ GtkHBox hbox;
+
+ GtkWidget *dash;
+ GtkObject *offset;
+};
+
+struct SPDashSelectorClass {
+ GtkHBoxClass parent_class;
+
+ void (* changed) (SPDashSelector *dsel);
+};
+
+double dash_0[] = {-1.0};
+double dash_1_1[] = {1.0, 1.0, -1.0};
+double dash_2_1[] = {2.0, 1.0, -1.0};
+double dash_4_1[] = {4.0, 1.0, -1.0};
+double dash_1_2[] = {1.0, 2.0, -1.0};
+double dash_1_4[] = {1.0, 4.0, -1.0};
+
+double *builtin_dashes[] = {dash_0, dash_1_1, dash_2_1, dash_4_1, dash_1_2, dash_1_4, NULL};
+
+static double **dashes = NULL;
+
+static void sp_dash_selector_class_init (SPDashSelectorClass *klass);
+static void sp_dash_selector_init (SPDashSelector *dsel);
+static GtkWidget *sp_dash_selector_menu_item_new (SPDashSelector *dsel, double *pattern);
+static void sp_dash_selector_dash_activate (GtkObject *object, SPDashSelector *dsel);
+static void sp_dash_selector_offset_value_changed (GtkAdjustment *adj, SPDashSelector *dsel);
+
+static GtkHBoxClass *parent_class;
+static guint signals[LAST_SIGNAL] = {0};
+
+GtkType
+sp_dash_selector_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GtkTypeInfo info = {
+ "SPDashSelector",
+ sizeof (SPDashSelector),
+ sizeof (SPDashSelectorClass),
+ (GtkClassInitFunc) sp_dash_selector_class_init,
+ (GtkObjectInitFunc) sp_dash_selector_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_HBOX, &info);
+ }
+ return type;
+}
+
+static void
+sp_dash_selector_class_init (SPDashSelectorClass *klass)
+{
+ parent_class = (GtkHBoxClass*)gtk_type_class (GTK_TYPE_HBOX);
+
+ signals[CHANGED] = gtk_signal_new ("changed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ G_TYPE_FROM_CLASS (klass),
+ GTK_SIGNAL_OFFSET (SPDashSelectorClass, changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+}
+
+static void
+sp_dash_selector_init (SPDashSelector *dsel)
+{
+ GtkTooltips *tt = gtk_tooltips_new();
+
+ dsel->dash = gtk_option_menu_new ();
+ gtk_tooltips_set_tip (tt, dsel->dash, _("Dash pattern"), NULL);
+ gtk_widget_show (dsel->dash);
+ gtk_box_pack_start (GTK_BOX (dsel), dsel->dash, FALSE, FALSE, 0);
+
+ GtkWidget *m = gtk_menu_new ();
+ gtk_widget_show (m);
+ for (int i = 0; dashes[i]; i++) {
+ GtkWidget *mi = sp_dash_selector_menu_item_new (dsel, dashes[i]);
+ gtk_widget_show (mi);
+ gtk_menu_append (GTK_MENU (m), mi);
+ }
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (dsel->dash), m);
+
+ dsel->offset = gtk_adjustment_new (0.0, 0.0, 10.0, 0.1, 1.0, 1.0);
+ GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (dsel->offset), 0.1, 2);
+ gtk_tooltips_set_tip (tt, sb, _("Pattern offset"), NULL);
+
+ sp_dialog_defocus_on_enter (sb);
+ gtk_widget_show (sb);
+ gtk_box_pack_start (GTK_BOX (dsel), sb, FALSE, FALSE, 0);
+ gtk_signal_connect (dsel->offset, "value_changed", GTK_SIGNAL_FUNC (sp_dash_selector_offset_value_changed), dsel);
+
+ gtk_object_set_data (GTK_OBJECT (dsel), "pattern", dashes[0]);
+}
+
+GtkWidget *
+sp_dash_selector_new (Inkscape::XML::Node *drepr)
+{
+ if (!dashes) {
+ int ndashes = 0;
+ if (drepr) {
+ for (Inkscape::XML::Node *dr = drepr->firstChild(); dr; dr = dr->next()) {
+ if (!strcmp (dr->name(), "dash"))
+ ndashes += 1;
+ }
+ }
+
+ if (ndashes > 0) {
+ int pos = 0;
+ SPStyle *style = sp_style_new ();
+ dashes = g_new (double *, ndashes + 1);
+ for (Inkscape::XML::Node *dr = drepr->firstChild(); dr; dr = dr->next()) {
+ if (!strcmp (dr->name(), "dash")) {
+ sp_style_read_from_repr (style, dr);
+ if (style->stroke_dash.n_dash > 0) {
+ dashes[pos] = g_new (double, style->stroke_dash.n_dash + 1);
+ double *d = dashes[pos];
+ int i = 0;
+ for (; i < style->stroke_dash.n_dash; i++) {
+ d[i] = style->stroke_dash.dash[i];
+ }
+ d[i] = -1;
+ } else {
+ dashes[pos] = dash_0;
+ }
+ pos += 1;
+ }
+ }
+ sp_style_unref (style);
+ dashes[pos] = NULL;
+ } else {
+ dashes = builtin_dashes;
+ }
+ }
+
+ GtkWidget *dsel = (GtkWidget*)gtk_type_new (SP_TYPE_DASH_SELECTOR);
+
+ return dsel;
+}
+
+void
+sp_dash_selector_set_dash (SPDashSelector *dsel, int ndash, double *dash, double offset)
+{
+ int pos = 0;
+ if (ndash > 0) {
+ double delta = 0.0;
+ for (int i = 0; i < ndash; i++)
+ delta += dash[i];
+ delta /= 1000.0;
+
+ for (int i = 0; dashes[i]; i++) {
+ double *pattern = dashes[i];
+ int np = 0;
+ while (pattern[np] >= 0.0)
+ np += 1;
+ if (np == ndash) {
+ int j;
+ for (j = 0; j < ndash; j++) {
+ if (!NR_DF_TEST_CLOSE (dash[j], pattern[j], delta))
+ break;
+ }
+ if (j == ndash) {
+ pos = i;
+ break;
+ }
+ }
+ }
+ }
+
+ gtk_object_set_data (GTK_OBJECT (dsel), "pattern", dashes[pos]);
+ gtk_option_menu_set_history (GTK_OPTION_MENU (dsel->dash), pos);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (dsel->offset), offset);
+}
+
+void
+sp_dash_selector_get_dash (SPDashSelector *dsel, int *ndash, double **dash, double *offset)
+{
+ double *pattern = (double*)gtk_object_get_data (GTK_OBJECT (dsel), "pattern");
+
+ int nd = 0;
+ while (pattern[nd] >= 0.0)
+ nd += 1;
+
+ if (nd > 0) {
+ if (ndash)
+ *ndash = nd;
+ if (dash) {
+ *dash = g_new (double, nd);
+ memcpy (*dash, pattern, nd * sizeof (double));
+ }
+ if (offset)
+ *offset = GTK_ADJUSTMENT (dsel->offset)->value;
+ } else {
+ if (ndash)
+ *ndash = 0;
+ if (dash)
+ *dash = NULL;
+ if (offset)
+ *offset = 0.0;
+ }
+}
+
+bool
+all_even_are_zero (double *pattern, int n)
+{
+ for (int i = 0; i < n; i += 2) {
+ if (pattern[i] != 0)
+ return false;
+ }
+ return true;
+}
+
+bool
+all_odd_are_zero (double *pattern, int n)
+{
+ for (int i = 1; i < n; i += 2) {
+ if (pattern[i] != 0)
+ return false;
+ }
+ return true;
+}
+
+static GtkWidget *
+sp_dash_selector_menu_item_new (SPDashSelector *dsel, double *pattern)
+{
+ GtkWidget *mi = gtk_menu_item_new ();
+
+ GdkPixmap *pixmap = gdk_pixmap_new (GTK_WIDGET (dsel)->window, DASH_PREVIEW_LENGTH + 4, 16, gdk_visual_get_best_depth ());
+ GdkGC *gc = gdk_gc_new (pixmap);
+
+ gdk_rgb_gc_set_foreground (gc, 0xffffffff);
+ gdk_draw_rectangle (pixmap, gc, TRUE, 0, 0, DASH_PREVIEW_LENGTH + 4, 16);
+
+ // FIXME: all of the below twibblering is due to the limitations of gdk_gc_set_dashes (only integers, no zeroes).
+ // Perhaps would make sense to rework this with manually drawn dashes.
+
+ // Fill in the integer array of pixel-lengths, for display
+ gint8 pixels_i[64];
+ gdouble pixels_d[64];
+ int n_source_dashes = 0;
+ int n_pixel_dashes = 0;
+
+ signed int i_s, i_p;
+ for (i_s = 0, i_p = 0; pattern[i_s] >= 0.0; i_s ++, i_p ++) {
+ pixels_d[i_p] = 0.0;
+ }
+
+ n_source_dashes = i_s;
+
+ for (i_s = 0, i_p = 0; i_s < n_source_dashes; i_s ++, i_p ++) {
+
+ // calculate the pixel length corresponding to the current dash
+ gdouble pixels = DASH_PREVIEW_WIDTH * pattern[i_s];
+
+ if (pixels > 0.0)
+ pixels_d [i_p] += pixels;
+ else {
+ if (i_p >= 1) {
+ // dash is zero, skip this element in the array, and set pointer backwards so the next dash is added to the previous
+ i_p -= 2;
+ } else {
+ // the first dash is zero; bad luck, gdk cannot start pattern with non-stroke, so we put a 1-pixel stub here
+ // (it may turn out not shown, though, see special cases below)
+ pixels_d [i_p] = 1.0;
+ }
+ }
+ }
+
+ n_pixel_dashes = i_p;
+
+ gdouble longest_dash = 0.0;
+
+ // after summation, convert double dash lengths to ints
+ for (i_p = 0; i_p < n_pixel_dashes; i_p ++) {
+ pixels_i [i_p] = (gint8) (pixels_d [i_p] + 0.5);
+ // zero-length dashes are already eliminated, so the <1 dash is short but not zero;
+ // we approximate it with a one-pixel mark
+ if (pixels_i [i_p] < 1)
+ pixels_i [i_p] = 1;
+ if (i_p % 2 == 0) { // it's a dash
+ if (pixels_d [i_p] > longest_dash)
+ longest_dash = pixels_d [i_p];
+ }
+ }
+
+ if (longest_dash > 1e-18 && longest_dash < 0.5) {
+ // fake "shortening" of one-pixel marks by painting them lighter-than-black
+ gint rgb = 255 - (gint) (255 * longest_dash / 0.5);
+ gdk_rgb_gc_set_foreground (gc, SP_RGBA32_U_COMPOSE (rgb, rgb, rgb, rgb));
+ } else {
+ gdk_rgb_gc_set_foreground (gc, 0x00000000);
+ }
+
+ if (n_source_dashes > 0) {
+ // special cases:
+ if (all_even_are_zero (pattern, n_source_dashes)) {
+ ; // do not draw anything, only gaps are non-zero
+ } else if (all_odd_are_zero (pattern, n_source_dashes)) {
+ // draw solid line, only dashes are non-zero
+ gdk_gc_set_line_attributes (gc, DASH_PREVIEW_WIDTH,
+ GDK_LINE_SOLID, GDK_CAP_BUTT,
+ GDK_JOIN_MITER);
+ gdk_draw_line (pixmap, gc, 4, 8, DASH_PREVIEW_LENGTH, 8);
+ } else {
+ // regular pattern with both gaps and dashes non-zero
+ gdk_gc_set_line_attributes (gc, DASH_PREVIEW_WIDTH,
+ GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT,
+ GDK_JOIN_MITER);
+ gdk_gc_set_dashes (gc, 0, pixels_i, n_pixel_dashes);
+ gdk_draw_line (pixmap, gc, 4, 8, DASH_PREVIEW_LENGTH, 8);
+ }
+ } else {
+ // no pattern, draw solid line
+ gdk_gc_set_line_attributes (gc, DASH_PREVIEW_WIDTH,
+ GDK_LINE_SOLID, GDK_CAP_BUTT,
+ GDK_JOIN_MITER);
+ gdk_draw_line (pixmap, gc, 4, 8, DASH_PREVIEW_LENGTH, 8);
+ }
+
+ gdk_gc_unref (gc);
+
+ GtkWidget *px = gtk_pixmap_new (pixmap, NULL);
+
+ gdk_pixmap_unref (pixmap);
+
+ gtk_widget_show (px);
+ gtk_container_add (GTK_CONTAINER (mi), px);
+
+ gtk_object_set_data (GTK_OBJECT (mi), "pattern", pattern);
+ gtk_signal_connect (GTK_OBJECT (mi), "activate", G_CALLBACK (sp_dash_selector_dash_activate), dsel);
+
+ return mi;
+}
+
+static void
+sp_dash_selector_dash_activate (GtkObject *object, SPDashSelector *dsel)
+{
+ double *pattern = (double*)gtk_object_get_data (object, "pattern");
+ gtk_object_set_data (GTK_OBJECT (dsel), "pattern", pattern);
+
+ gtk_signal_emit (GTK_OBJECT (dsel), signals[CHANGED]);
+}
+
+static void
+sp_dash_selector_offset_value_changed (GtkAdjustment *adj, SPDashSelector *dsel)
+{
+ gtk_signal_emit (GTK_OBJECT (dsel), signals[CHANGED]);
+}
diff --git a/src/widgets/dash-selector.h b/src/widgets/dash-selector.h
new file mode 100644
index 000000000..a00834625
--- /dev/null
+++ b/src/widgets/dash-selector.h
@@ -0,0 +1,48 @@
+#ifndef __SP_DASH_SELECTOR_H__
+#define __SP_DASH_SELECTOR_H__
+
+/*
+ * Optionmenu for selecting dash patterns
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+struct SPDashSelector;
+
+#include <gtk/gtkwidget.h>
+#include "../xml/repr.h"
+
+#include <glib.h>
+
+
+
+#define SP_TYPE_DASH_SELECTOR (sp_dash_selector_get_type ())
+#define SP_DASH_SELECTOR(o) (GTK_CHECK_CAST ((o), SP_TYPE_DASH_SELECTOR, SPDashSelector))
+#define SP_IS_DASH_SELECTOR(o) (GTK_CHECK_TYPE ((o), SP_TYPE_DASH_SELECTOR))
+
+GtkType sp_dash_selector_get_type (void);
+
+GtkWidget *sp_dash_selector_new (Inkscape::XML::Node *repr);
+
+void sp_dash_selector_set_dash (SPDashSelector *dsel, int ndash, double *dash, double offset);
+void sp_dash_selector_get_dash (SPDashSelector *dsel, int *ndash, double **dash, double *offset);
+
+
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp
new file mode 100644
index 000000000..f9a8b8f62
--- /dev/null
+++ b/src/widgets/desktop-widget.cpp
@@ -0,0 +1,1270 @@
+#define __SP_DESKTOP_WIDGET_C__
+
+/** \file
+ * Desktop widget implementation
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * MenTaLguY <mental@rydia.net>
+ * bulia byak <buliabyak@users.sf.net>
+ * Ralf Stephan <ralf@ark.in-berlin.de>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtkmm/paned.h>
+#include <gtk/gtk.h>
+
+#include "macros.h"
+#include "inkscape-private.h"
+#include "desktop-handles.h"
+#include "desktop-events.h"
+#include "document.h"
+#include "desktop-widget.h"
+#include "sp-namedview.h"
+#include "interface.h"
+#include "toolbox.h"
+#include "prefs-utils.h"
+#include "file.h"
+#include "display/canvas-arena.h"
+#include <extension/db.h>
+#include "helper/units.h"
+#include "widgets/button.h"
+#include "widgets/ruler.h"
+#include "widgets/widget-sizes.h"
+#include "widgets/spw-utilities.h"
+#include "widgets/spinbutton-events.h"
+#include "widgets/layer-selector.h"
+#include "ui/widget/selected-style.h"
+#include "sp-item.h"
+#include "dialogs/swatches.h"
+
+#ifdef WITH_INKBOARD
+#endif
+
+
+
+enum {
+ ACTIVATE,
+ DEACTIVATE,
+ MODIFIED,
+ EVENT_CONTEXT_CHANGED,
+ LAST_SIGNAL
+};
+
+
+//---------------------------------------------------------------------
+/* SPDesktopWidget */
+
+static void sp_desktop_widget_class_init (SPDesktopWidgetClass *klass);
+static void sp_desktop_widget_init (SPDesktopWidget *widget);
+static void sp_desktop_widget_destroy (GtkObject *object);
+
+static void sp_desktop_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
+static void sp_desktop_widget_realize (GtkWidget *widget);
+
+static gint sp_desktop_widget_event (GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw);
+
+
+
+static void sp_desktop_widget_adjustment_value_changed (GtkAdjustment *adj, SPDesktopWidget *dtw);
+static void sp_desktop_widget_namedview_modified (SPNamedView *nv, guint flags, SPDesktopWidget *dtw);
+
+static gdouble sp_dtw_zoom_value_to_display (gdouble value);
+static gdouble sp_dtw_zoom_display_to_value (gdouble value);
+static gint sp_dtw_zoom_input (GtkSpinButton *spin, gdouble *new_val, gpointer data);
+static bool sp_dtw_zoom_output (GtkSpinButton *spin, gpointer data);
+static void sp_dtw_zoom_value_changed (GtkSpinButton *spin, gpointer data);
+static void sp_dtw_zoom_populate_popup (GtkEntry *entry, GtkMenu *menu, gpointer data);
+static void sp_dtw_zoom_menu_handler (SPDesktop *dt, gdouble factor);
+static void sp_dtw_zoom_50 (GtkMenuItem *item, gpointer data);
+static void sp_dtw_zoom_100 (GtkMenuItem *item, gpointer data);
+static void sp_dtw_zoom_200 (GtkMenuItem *item, gpointer data);
+static void sp_dtw_zoom_page (GtkMenuItem *item, gpointer data);
+static void sp_dtw_zoom_drawing (GtkMenuItem *item, gpointer data);
+static void sp_dtw_zoom_selection (GtkMenuItem *item, gpointer data);
+
+SPViewWidgetClass *dtw_parent_class;
+
+void
+SPDesktopWidget::setMessage (Inkscape::MessageType type, const gchar *message)
+{
+ GtkLabel *sb=GTK_LABEL(this->select_status);
+ gtk_label_set_markup (sb, message ? message : "");
+ // FIXME: TODO: remove <b></b> before displaying as tooltip
+ gtk_tooltips_set_tip (this->tt, this->select_status_eventbox, message ? message : "", NULL);
+}
+
+NR::Point
+SPDesktopWidget::window_get_pointer()
+{
+ gint x,y;
+ gdk_window_get_pointer (GTK_WIDGET (canvas)->window, &x, &y, NULL);
+ return NR::Point(x,y);
+}
+
+/**
+ * Registers SPDesktopWidget class and returns its type number.
+ */
+GtkType
+sp_desktop_widget_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ static const GtkTypeInfo info = {
+ "SPDesktopWidget",
+ sizeof (SPDesktopWidget),
+ sizeof (SPDesktopWidgetClass),
+ (GtkClassInitFunc) sp_desktop_widget_class_init,
+ (GtkObjectInitFunc) sp_desktop_widget_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (SP_TYPE_VIEW_WIDGET, &info);
+ }
+ return type;
+}
+
+/**
+ * SPDesktopWidget vtable initialization
+ */
+static void
+sp_desktop_widget_class_init (SPDesktopWidgetClass *klass)
+{
+ dtw_parent_class = (SPViewWidgetClass*)gtk_type_class (SP_TYPE_VIEW_WIDGET);
+
+ GtkObjectClass *object_class = (GtkObjectClass *) klass;
+ GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+
+ object_class->destroy = sp_desktop_widget_destroy;
+
+ widget_class->size_allocate = sp_desktop_widget_size_allocate;
+ widget_class->realize = sp_desktop_widget_realize;
+}
+
+/**
+ * Callback for SPDesktopWidget object initialization.
+ */
+static void
+sp_desktop_widget_init (SPDesktopWidget *dtw)
+{
+ GtkWidget *widget;
+ GtkWidget *tbl;
+ GtkWidget *w;
+
+ GtkWidget *hbox;
+ GtkWidget *eventbox;
+ GtkStyle *style;
+
+ widget = GTK_WIDGET (dtw);
+
+ Gtk::VPaned* pane = manage( new Gtk::VPaned() );
+ pane->show_all();
+
+ dtw->window = 0;
+
+ dtw->desktop = NULL;
+
+ dtw->tt = gtk_tooltips_new ();
+
+ /* Main table */
+ dtw->vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add( GTK_CONTAINER(dtw), GTK_WIDGET(pane->gobj()) );
+
+ Gtk::VBox* tmp = Glib::wrap( GTK_VBOX(dtw->vbox) );
+ Gtk::VBox* boxWrap = manage( tmp );
+ pane->pack1( *boxWrap, true, true );
+
+ dtw->statusbar = gtk_hbox_new (FALSE, 0);
+ //gtk_widget_set_usize (dtw->statusbar, -1, BOTTOM_BAR_HEIGHT);
+ gtk_box_pack_end (GTK_BOX (dtw->vbox), dtw->statusbar, FALSE, TRUE, 0);
+
+ {
+ using Inkscape::UI::Dialogs::SwatchesPanel;
+
+ SwatchesPanel* swatches = manage( new SwatchesPanel() );
+ dtw->panels = GTK_WIDGET(swatches->gobj());
+ pane->pack2( *swatches, false, true );
+ }
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (dtw->vbox), hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ dtw->aux_toolbox = sp_aux_toolbox_new ();
+ gtk_box_pack_end (GTK_BOX (dtw->vbox), dtw->aux_toolbox, FALSE, TRUE, 0);
+
+ dtw->commands_toolbox = sp_commands_toolbox_new ();
+ gtk_box_pack_end (GTK_BOX (dtw->vbox), dtw->commands_toolbox, FALSE, TRUE, 0);
+
+ dtw->tool_toolbox = sp_tool_toolbox_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), dtw->tool_toolbox, FALSE, TRUE, 0);
+
+ tbl = gtk_table_new (4, 3, FALSE);
+ gtk_box_pack_start (GTK_BOX (hbox), tbl, TRUE, TRUE, 1);
+
+ /* Horizontal ruler */
+ eventbox = gtk_event_box_new ();
+ dtw->hruler = sp_hruler_new ();
+ dtw->hruler_box = eventbox;
+ sp_ruler_set_metric (GTK_RULER (dtw->hruler), SP_PT);
+ gtk_tooltips_set_tip (dtw->tt, dtw->hruler_box, gettext(sp_unit_get_plural (&sp_unit_get_by_id(SP_UNIT_PT))), NULL);
+ gtk_container_add (GTK_CONTAINER (eventbox), dtw->hruler);
+ gtk_table_attach (GTK_TABLE (tbl), eventbox, 1, 2, 0, 1, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), widget->style->xthickness, 0);
+ g_signal_connect (G_OBJECT (eventbox), "button_press_event", G_CALLBACK (sp_dt_hruler_event), dtw);
+ g_signal_connect (G_OBJECT (eventbox), "button_release_event", G_CALLBACK (sp_dt_hruler_event), dtw);
+ g_signal_connect (G_OBJECT (eventbox), "motion_notify_event", G_CALLBACK (sp_dt_hruler_event), dtw);
+
+ /* Vertical ruler */
+ eventbox = gtk_event_box_new ();
+ dtw->vruler = sp_vruler_new ();
+ dtw->vruler_box = eventbox;
+ sp_ruler_set_metric (GTK_RULER (dtw->vruler), SP_PT);
+ gtk_tooltips_set_tip (dtw->tt, dtw->vruler_box, gettext(sp_unit_get_plural (&sp_unit_get_by_id(SP_UNIT_PT))), NULL);
+ gtk_container_add (GTK_CONTAINER (eventbox), GTK_WIDGET (dtw->vruler));
+ gtk_table_attach (GTK_TABLE (tbl), eventbox, 0, 1, 1, 2, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), 0, widget->style->ythickness);
+ g_signal_connect (G_OBJECT (eventbox), "button_press_event", G_CALLBACK (sp_dt_vruler_event), dtw);
+ g_signal_connect (G_OBJECT (eventbox), "button_release_event", G_CALLBACK (sp_dt_vruler_event), dtw);
+ g_signal_connect (G_OBJECT (eventbox), "motion_notify_event", G_CALLBACK (sp_dt_vruler_event), dtw);
+
+ /* Horizontal scrollbar */
+ dtw->hadj = (GtkAdjustment *) gtk_adjustment_new (0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0);
+ dtw->hscrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (dtw->hadj));
+ gtk_table_attach (GTK_TABLE (tbl), dtw->hscrollbar, 1, 2, 2, 3, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_FILL), 0, 0);
+ /* Vertical scrollbar and the sticky zoom button */
+ dtw->vscrollbar_box = gtk_vbox_new (FALSE, 0);
+ dtw->sticky_zoom = sp_button_new_from_data ( GTK_ICON_SIZE_MENU,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "sticky_zoom",
+ _("Zoom drawing if window size changes"),
+ dtw->tt);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dtw->sticky_zoom), prefs_get_int_attribute ("options.stickyzoom", "value", 0));
+ gtk_box_pack_start (GTK_BOX (dtw->vscrollbar_box), dtw->sticky_zoom, FALSE, FALSE, 0);
+ dtw->vadj = (GtkAdjustment *) gtk_adjustment_new (0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0);
+ dtw->vscrollbar = gtk_vscrollbar_new (GTK_ADJUSTMENT (dtw->vadj));
+ gtk_box_pack_start (GTK_BOX (dtw->vscrollbar_box), dtw->vscrollbar, TRUE, TRUE, 0);
+ gtk_table_attach (GTK_TABLE (tbl), dtw->vscrollbar_box, 2, 3, 0, 2, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
+
+ /* Canvas */
+ w = gtk_frame_new (NULL);
+ gtk_table_attach (GTK_TABLE (tbl), w, 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
+ dtw->canvas = SP_CANVAS (sp_canvas_new_aa ());
+ GTK_WIDGET_SET_FLAGS (GTK_WIDGET (dtw->canvas), GTK_CAN_FOCUS);
+ style = gtk_style_copy (GTK_WIDGET (dtw->canvas)->style);
+ style->bg[GTK_STATE_NORMAL] = style->white;
+ gtk_widget_set_style (GTK_WIDGET (dtw->canvas), style);
+ gtk_widget_set_extension_events(GTK_WIDGET (dtw->canvas) , GDK_EXTENSION_EVENTS_ALL);
+ g_signal_connect (G_OBJECT (dtw->canvas), "event", G_CALLBACK (sp_desktop_widget_event), dtw);
+ gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (dtw->canvas));
+
+ dtw->selected_style = new Inkscape::UI::Widget::SelectedStyle(true);
+ GtkHBox *ss_ = dtw->selected_style->gobj();
+ gtk_box_pack_start (GTK_BOX (dtw->statusbar), GTK_WIDGET(ss_), FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (dtw->statusbar), gtk_vseparator_new(), FALSE, FALSE, 0);
+
+ // connect scrollbar signals
+ g_signal_connect (G_OBJECT (dtw->hadj), "value-changed", G_CALLBACK (sp_desktop_widget_adjustment_value_changed), dtw);
+ g_signal_connect (G_OBJECT (dtw->vadj), "value-changed", G_CALLBACK (sp_desktop_widget_adjustment_value_changed), dtw);
+
+ GtkWidget *statusbar_tail=gtk_statusbar_new();
+ gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(statusbar_tail), TRUE);
+ gtk_box_pack_end (GTK_BOX (dtw->statusbar), statusbar_tail, FALSE, FALSE, 0);
+
+ // zoom status spinbutton
+ dtw->zoom_status = gtk_spin_button_new_with_range (log(SP_DESKTOP_ZOOM_MIN)/log(2), log(SP_DESKTOP_ZOOM_MAX)/log(2), 0.1);
+ gtk_tooltips_set_tip (dtw->tt, dtw->zoom_status, _("Zoom"), NULL);
+ gtk_widget_set_size_request (dtw->zoom_status, STATUS_ZOOM_WIDTH, -1);
+ gtk_entry_set_width_chars (GTK_ENTRY (dtw->zoom_status), 6);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (dtw->zoom_status), FALSE);
+ gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (dtw->zoom_status), GTK_UPDATE_ALWAYS);
+ g_signal_connect (G_OBJECT (dtw->zoom_status), "input", G_CALLBACK (sp_dtw_zoom_input), dtw);
+ g_signal_connect (G_OBJECT (dtw->zoom_status), "output", G_CALLBACK (sp_dtw_zoom_output), dtw);
+ gtk_object_set_data (GTK_OBJECT (dtw->zoom_status), "dtw", dtw->canvas);
+ gtk_signal_connect (GTK_OBJECT (dtw->zoom_status), "focus-in-event", GTK_SIGNAL_FUNC (spinbutton_focus_in), dtw->zoom_status);
+ gtk_signal_connect (GTK_OBJECT (dtw->zoom_status), "key-press-event", GTK_SIGNAL_FUNC (spinbutton_keypress), dtw->zoom_status);
+ dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "value_changed", G_CALLBACK (sp_dtw_zoom_value_changed), dtw);
+ dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "populate_popup", G_CALLBACK (sp_dtw_zoom_populate_popup), dtw);
+
+ // cursor coordinates
+ dtw->coord_status = gtk_table_new (5, 2, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(dtw->coord_status), 0);
+ gtk_table_set_col_spacings(GTK_TABLE(dtw->coord_status), 2);
+ gtk_table_attach(GTK_TABLE(dtw->coord_status), gtk_vseparator_new(), 0,1, 0,2, GTK_FILL, GTK_FILL, 0, 0);
+ eventbox = gtk_event_box_new ();
+ gtk_container_add (GTK_CONTAINER (eventbox), dtw->coord_status);
+ gtk_tooltips_set_tip (dtw->tt, eventbox, _("Cursor coordinates"), NULL);
+ GtkWidget *label_x = gtk_label_new("X:");
+ gtk_misc_set_alignment (GTK_MISC(label_x), 0.0, 0.5);
+ gtk_table_attach(GTK_TABLE(dtw->coord_status), label_x, 1,2, 0,1, GTK_FILL, GTK_FILL, 0, 0);
+ GtkWidget *label_y = gtk_label_new("Y:");
+ gtk_misc_set_alignment (GTK_MISC(label_y), 0.0, 0.5);
+ gtk_table_attach(GTK_TABLE(dtw->coord_status), label_y, 1,2, 1,2, GTK_FILL, GTK_FILL, 0, 0);
+ dtw->coord_status_x = gtk_label_new("0.0");
+ gtk_misc_set_alignment (GTK_MISC(dtw->coord_status_x), 0.0, 0.5);
+ dtw->coord_status_y = gtk_label_new("0.0");
+ gtk_misc_set_alignment (GTK_MISC(dtw->coord_status_y), 0.0, 0.5);
+ gtk_table_attach(GTK_TABLE(dtw->coord_status), dtw->coord_status_x, 2,3, 0,1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_table_attach(GTK_TABLE(dtw->coord_status), dtw->coord_status_y, 2,3, 1,2, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_table_attach(GTK_TABLE(dtw->coord_status), gtk_label_new("Z:"), 3,4, 0,2, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_table_attach(GTK_TABLE(dtw->coord_status), dtw->zoom_status, 4,5, 0,2, GTK_FILL, GTK_FILL, 0, 0);
+ sp_set_font_size_smaller (dtw->coord_status);
+ gtk_box_pack_end (GTK_BOX (statusbar_tail), eventbox, FALSE, FALSE, 1);
+
+ dtw->layer_selector = new Inkscape::Widgets::LayerSelector(NULL);
+ // FIXME: need to unreference on container destruction to avoid leak
+ dtw->layer_selector->reference();
+ //dtw->layer_selector->set_size_request(-1, SP_ICON_SIZE_BUTTON);
+ gtk_box_pack_start(GTK_BOX(dtw->statusbar), GTK_WIDGET(dtw->layer_selector->gobj()), FALSE, FALSE, 1);
+
+ dtw->select_status_eventbox = gtk_event_box_new ();
+ dtw->select_status = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (dtw->select_status), 0.0, 0.5);
+ gtk_widget_set_size_request (dtw->select_status, 1, -1);
+ // display the initial welcome message in the statusbar
+ gtk_label_set_markup (GTK_LABEL (dtw->select_status), _("<b>Welcome to Inkscape!</b> Use shape or freehand tools to create objects; use selector (arrow) to move or transform them."));
+ // space label 2 pixels from left edge
+ gtk_container_add (GTK_CONTAINER (dtw->select_status_eventbox), dtw->select_status);
+ gtk_box_pack_start (GTK_BOX (dtw->statusbar), gtk_hbox_new(FALSE, 0), FALSE, FALSE, 2);
+ gtk_box_pack_start (GTK_BOX (dtw->statusbar), dtw->select_status_eventbox, TRUE, TRUE, 0);
+
+ gtk_widget_show_all (dtw->vbox);
+}
+
+/**
+ * Called before SPDesktopWidget destruction.
+ */
+static void
+sp_desktop_widget_destroy (GtkObject *object)
+{
+ SPDesktopWidget *dtw = SP_DESKTOP_WIDGET (object);
+
+ if (dtw->desktop) {
+ dtw->layer_selector->unreference();
+ inkscape_remove_desktop (dtw->desktop); // clears selection too
+ sp_signal_disconnect_by_data (G_OBJECT (dtw->desktop->namedview), dtw);
+ dtw->desktop->destroy();
+ Inkscape::GC::release (dtw->desktop);
+ dtw->desktop = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (dtw_parent_class)->destroy) {
+ (* GTK_OBJECT_CLASS (dtw_parent_class)->destroy) (object);
+ }
+}
+
+
+/**
+ * Set the title in the desktop-window (if desktop has an own window).
+ *
+ * The title has form file name: desktop number - Inkscape.
+ * The desktop number is only shown if it's 2 or higher,
+ */
+void
+SPDesktopWidget::updateTitle(gchar const* uri)
+{
+ GtkWindow *window = GTK_WINDOW (gtk_object_get_data (GTK_OBJECT(this), "window"));
+ if (window) {
+ gchar const *fname = ( TRUE
+ ? uri
+ : g_basename(uri) );
+ GString *name = g_string_new ("");
+ if (this->desktop->number > 1) {
+ g_string_printf (name, _("%s: %d - Inkscape"), fname, this->desktop->number);
+ } else {
+ g_string_printf (name, _("%s - Inkscape"), fname);
+ }
+ gtk_window_set_title (window, name->str);
+ g_string_free (name, TRUE);
+ }
+}
+
+/**
+ * Callback to allocate space for desktop widget.
+ */
+static void
+sp_desktop_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ SPDesktopWidget *dtw = SP_DESKTOP_WIDGET (widget);
+
+ if ((allocation->x == widget->allocation.x) &&
+ (allocation->y == widget->allocation.y) &&
+ (allocation->width == widget->allocation.width) &&
+ (allocation->height == widget->allocation.height)) {
+ if (GTK_WIDGET_CLASS (dtw_parent_class)->size_allocate)
+ GTK_WIDGET_CLASS (dtw_parent_class)->size_allocate (widget, allocation);
+ return;
+ }
+
+ if (GTK_WIDGET_REALIZED (widget)) {
+ NR::Rect const area = dtw->desktop->get_display_area();
+ double zoom = dtw->desktop->current_zoom();
+
+ if (GTK_WIDGET_CLASS(dtw_parent_class)->size_allocate) {
+ GTK_WIDGET_CLASS(dtw_parent_class)->size_allocate (widget, allocation);
+ }
+
+ if (SP_BUTTON_IS_DOWN(dtw->sticky_zoom)) {
+ /* Calculate zoom per pixel */
+ double const zpsp = zoom / hypot (area.dimensions()[NR::X], area.dimensions()[NR::Y]);
+ /* Find new visible area */
+ NR::Rect newarea = dtw->desktop->get_display_area();
+ /* Calculate adjusted zoom */
+ zoom = zpsp * hypot(newarea.dimensions()[NR::X], newarea.dimensions()[NR::Y]);
+ dtw->desktop->zoom_absolute(newarea.midpoint()[NR::X], newarea.midpoint()[NR::Y], zoom);
+ } else {
+ dtw->desktop->zoom_absolute(area.midpoint()[NR::X], area.midpoint()[NR::Y], zoom);
+ }
+
+ } else {
+ if (GTK_WIDGET_CLASS (dtw_parent_class)->size_allocate) {
+ GTK_WIDGET_CLASS (dtw_parent_class)->size_allocate (widget, allocation);
+ }
+// this->size_allocate (widget, allocation);
+ }
+}
+
+/**
+ * Callback to realize desktop widget.
+ */
+static void
+sp_desktop_widget_realize (GtkWidget *widget)
+{
+
+ SPDesktopWidget *dtw = SP_DESKTOP_WIDGET (widget);
+
+ if (GTK_WIDGET_CLASS (dtw_parent_class)->realize)
+ (* GTK_WIDGET_CLASS (dtw_parent_class)->realize) (widget);
+
+ NRRect d;
+ d.x0 = 0.0;
+ d.y0 = 0.0;
+ d.x1 = sp_document_width (dtw->desktop->doc());
+ d.y1 = sp_document_height (dtw->desktop->doc());
+
+ if ((fabs (d.x1 - d.x0) < 1.0) || (fabs (d.y1 - d.y0) < 1.0)) return;
+
+ dtw->desktop->set_display_area (d.x0, d.y0, d.x1, d.y1, 10);
+
+ /* Listen on namedview modification */
+ g_signal_connect (G_OBJECT (dtw->desktop->namedview), "modified", G_CALLBACK (sp_desktop_widget_namedview_modified), dtw);
+ sp_desktop_widget_namedview_modified (dtw->desktop->namedview, SP_OBJECT_MODIFIED_FLAG, dtw);
+
+ dtw->updateTitle(SP_DOCUMENT_NAME (dtw->desktop->doc()));
+}
+
+/**
+ * Callback to handle desktop widget event.
+ */
+static gint
+sp_desktop_widget_event (GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw)
+{
+ if (event->type == GDK_BUTTON_PRESS) {
+ // defocus any spinbuttons
+ gtk_widget_grab_focus (GTK_WIDGET(dtw->canvas));
+ }
+
+ if ((event->type == GDK_BUTTON_PRESS) && (event->button.button == 3)) {
+ if (event->button.state & GDK_SHIFT_MASK) {
+ sp_canvas_arena_set_sticky (SP_CANVAS_ARENA (dtw->desktop->drawing), TRUE);
+ } else {
+ sp_canvas_arena_set_sticky (SP_CANVAS_ARENA (dtw->desktop->drawing), FALSE);
+ }
+ }
+
+ if (GTK_WIDGET_CLASS (dtw_parent_class)->event) {
+ return (* GTK_WIDGET_CLASS (dtw_parent_class)->event) (widget, event);
+ } else {
+ // The keypress events need to be passed to desktop handler explicitly,
+ // because otherwise the event contexts only receive keypresses when the mouse cursor
+ // is over the canvas. This redirection is only done for keypresses and only if there's no
+ // current item on the canvas, because item events and all mouse events are caught
+ // and passed on by the canvas acetate (I think). --bb
+ if (event->type == GDK_KEY_PRESS && !dtw->canvas->current_item) {
+ return sp_desktop_root_handler (NULL, event, dtw->desktop);
+ }
+ }
+
+ return FALSE;
+}
+
+void
+sp_dtw_desktop_activate (SPDesktopWidget *dtw)
+{
+ /* update active desktop indicator */
+}
+
+void
+sp_dtw_desktop_deactivate (SPDesktopWidget *dtw)
+{
+ /* update inactive desktop indicator */
+}
+
+/**
+ * Shuts down the desktop object for the view being closed. It checks
+ * to see if the document has been edited, and if so prompts the user
+ * to save, discard, or cancel. Returns TRUE if the shutdown operation
+ * is cancelled or if the save is cancelled or fails, FALSE otherwise.
+ */
+bool
+SPDesktopWidget::shutdown()
+{
+ g_assert(desktop != NULL);
+ if (inkscape_is_sole_desktop_for_document(*desktop)) {
+ SPDocument *doc = desktop->doc();
+ if (sp_document_repr_root(doc)->attribute("sodipodi:modified") != NULL) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new(
+ GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(this))),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ "Document modified");
+
+ gchar *markup;
+ /** \todo
+ * FIXME !!! obviously this will have problems if the document
+ * name contains markup characters
+ */
+ markup = g_strdup_printf(
+ _("<span weight=\"bold\" size=\"larger\">Save changes to document \"%s\" before closing?</span>\n\n"
+ "If you close without saving, your changes will be discarded."),
+ SP_DOCUMENT_NAME(doc));
+
+ /** \todo
+ * FIXME !!! Gtk 2.3+ gives us gtk_message_dialog_set_markup()
+ * (and actually even
+ * gtk_message_dialog_new_with_markup(..., format, ...)!) --
+ * until then, we will have to be a little bit evil here and
+ * poke at GtkMessageDialog::label, which is private...
+ */
+
+ gtk_label_set_markup(GTK_LABEL(GTK_MESSAGE_DIALOG(dialog)->label), markup);
+ g_free(markup);
+
+ GtkWidget *close_button;
+ close_button = gtk_button_new_with_mnemonic(_("Close _without saving"));
+ gtk_widget_show(close_button);
+ gtk_dialog_add_action_widget(GTK_DIALOG(dialog), close_button, GTK_RESPONSE_NO);
+
+ gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
+
+ gint response;
+ response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ switch (response) {
+ case GTK_RESPONSE_YES:
+ sp_document_ref(doc);
+ if (sp_file_save_document(doc)) {
+ sp_document_unref(doc);
+ } else { // save dialog cancelled or save failed
+ sp_document_unref(doc);
+ return TRUE;
+ }
+ break;
+ case GTK_RESPONSE_NO:
+ break;
+ default: // cancel pressed, or dialog was closed
+ return TRUE;
+ break;
+ }
+ }
+ /* Code to check data loss */
+ bool allow_data_loss = FALSE;
+ while (sp_document_repr_root(doc)->attribute("inkscape:dataloss") != NULL && allow_data_loss == FALSE) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new(
+ GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(this))),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ "Document modified");
+
+ gchar *markup;
+ /** \todo
+ * FIXME !!! obviously this will have problems if the document
+ * name contains markup characters
+ */
+ markup = g_strdup_printf(
+ _("<span weight=\"bold\" size=\"larger\">The file \"%s\" was saved with a format (%s) that may cause data loss!</span>\n\n"
+ "Do you want to save this file in another format?"),
+ SP_DOCUMENT_NAME(doc),
+ Inkscape::Extension::db.get(sp_document_repr_root(doc)->attribute("inkscape:output_extension"))->get_name());
+
+ /** \todo
+ * FIXME !!! Gtk 2.3+ gives us gtk_message_dialog_set_markup()
+ * (and actually even
+ * gtk_message_dialog_new_with_markup(..., format, ...)!) --
+ * until then, we will have to be a little bit evil here and
+ * poke at GtkMessageDialog::label, which is private...
+ */
+
+ gtk_label_set_markup(GTK_LABEL(GTK_MESSAGE_DIALOG(dialog)->label), markup);
+ g_free(markup);
+
+ GtkWidget *close_button;
+ close_button = gtk_button_new_with_mnemonic(_("Close _without saving"));
+ gtk_widget_show(close_button);
+ gtk_dialog_add_action_widget(GTK_DIALOG(dialog), close_button, GTK_RESPONSE_NO);
+
+ gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
+
+ gint response;
+ response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ switch (response) {
+ case GTK_RESPONSE_YES:
+ sp_document_ref(doc);
+ if (sp_file_save_dialog(doc)) {
+ sp_document_unref(doc);
+ } else { // save dialog cancelled or save failed
+ sp_document_unref(doc);
+ return TRUE;
+ }
+ break;
+ case GTK_RESPONSE_NO:
+ allow_data_loss = TRUE;
+ break;
+ default: // cancel pressed, or dialog was closed
+ return TRUE;
+ break;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * \pre this->desktop->main != 0
+ */
+void
+SPDesktopWidget::requestCanvasUpdate() {
+ gtk_widget_queue_draw (GTK_WIDGET (SP_CANVAS_ITEM (this->desktop->main)->canvas));
+}
+
+void
+SPDesktopWidget::setCoordinateStatus(NR::Point p)
+{
+ gchar *cstr;
+ cstr=g_strdup_printf("%6.2f", dt2r * p[NR::X]);
+ gtk_label_set_text (GTK_LABEL (this->coord_status_x), cstr);
+ g_free(cstr);
+ cstr=g_strdup_printf("%6.2f", dt2r * p[NR::Y]);
+ gtk_label_set_text (GTK_LABEL (this->coord_status_y), cstr);
+ g_free(cstr);
+}
+
+void
+SPDesktopWidget::letZoomGrabFocus()
+{
+ if (zoom_status)
+ gtk_widget_grab_focus (zoom_status);
+}
+
+void
+SPDesktopWidget::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
+{
+ GtkWindow *window = GTK_WINDOW (gtk_object_get_data (GTK_OBJECT(this), "window"));
+ if (window)
+ {
+ gtk_window_get_size (window, &w, &h);
+ gtk_window_get_position (window, &x, &y);
+ }
+}
+
+void
+SPDesktopWidget::setWindowPosition (NR::Point p)
+{
+ GtkWindow *window = GTK_WINDOW (gtk_object_get_data (GTK_OBJECT(this), "window"));
+ if (window)
+ {
+ gtk_window_move (window, gint(round(p[NR::X])), gint(round(p[NR::Y])));
+ }
+}
+
+void
+SPDesktopWidget::setWindowSize (gint w, gint h)
+{
+ GtkWindow *window = GTK_WINDOW (gtk_object_get_data (GTK_OBJECT(this), "window"));
+ if (window)
+ {
+ gtk_window_set_default_size (window, w, h);
+ gtk_window_reshow_with_initial_size (window);
+ }
+}
+
+/**
+ * \note transientizing does not work on windows; when you minimize a document
+ * and then open it back, only its transient emerges and you cannot access
+ * the document window.
+ */
+void
+SPDesktopWidget::setWindowTransient (void *p, int transient_policy)
+{
+#ifndef WIN32
+ GtkWindow *w =GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(this)));
+ if (w)
+ {
+ gtk_window_set_transient_for (GTK_WINDOW(p), w);
+
+ /*
+ * This enables "aggressive" transientization,
+ * i.e. dialogs always emerging on top when you switch documents. Note
+ * however that this breaks "click to raise" policy of a window
+ * manager because the switched-to document will be raised at once
+ * (so that its transients also could raise)
+ */
+ if (transient_policy == 2)
+
+ // without this, a transient window not always emerges on top
+ gtk_window_present (w);
+ }
+#endif
+}
+
+void
+SPDesktopWidget::presentWindow()
+{
+ GtkWindow *w =GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(this)));
+ if (w)
+ gtk_window_present (w);
+}
+
+bool
+SPDesktopWidget::warnDialog (gchar* text)
+{
+ GtkWindow *w =GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(this)));
+ if (w)
+ {
+ GtkWidget *dialog = gtk_message_dialog_new(
+ w,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_YES_NO,
+ text);
+ gint response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ if (response == GTK_RESPONSE_YES)
+ return true;
+ }
+ return false;
+}
+
+void
+sp_desktop_widget_fullscreen(SPDesktopWidget *dtw)
+{
+#ifdef HAVE_GTK_WINDOW_FULLSCREEN
+ GtkWindow *topw = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(dtw->canvas)));
+ if (GTK_IS_WINDOW(topw)) {
+ if (dtw->desktop->is_fullscreen) {
+ dtw->desktop->is_fullscreen = FALSE;
+ gtk_window_unfullscreen(topw);
+ sp_desktop_widget_layout (dtw);
+ } else {
+ dtw->desktop->is_fullscreen = TRUE;
+ gtk_window_fullscreen(topw);
+ sp_desktop_widget_layout (dtw);
+ }
+ }
+#endif /* HAVE_GTK_WINDOW_FULLSCREEN */
+}
+
+/**
+ * Hide whatever the user does not want to see in the window
+ */
+void
+sp_desktop_widget_layout (SPDesktopWidget *dtw)
+{
+ bool fullscreen = dtw->desktop->is_fullscreen;
+
+ if (prefs_get_int_attribute (fullscreen ? "fullscreen.menu" : "window.menu", "state", 1) == 0) {
+ gtk_widget_hide_all (dtw->menubar);
+ } else {
+ gtk_widget_show_all (dtw->menubar);
+ }
+
+ if (prefs_get_int_attribute (fullscreen ? "fullscreen.commands" : "window.commands", "state", 1) == 0) {
+ gtk_widget_hide_all (dtw->commands_toolbox);
+ } else {
+ gtk_widget_show_all (dtw->commands_toolbox);
+ }
+
+ if (prefs_get_int_attribute (fullscreen ? "fullscreen.toppanel" : "window.toppanel", "state", 1) == 0) {
+ gtk_widget_hide_all (dtw->aux_toolbox);
+ } else {
+ // we cannot just show_all because that will show all tools' panels;
+ // this is a function from toolbox.cpp that shows only the current tool's panel
+ show_aux_toolbox (dtw->aux_toolbox);
+ }
+
+ if (prefs_get_int_attribute (fullscreen ? "fullscreen.toolbox" : "window.toolbox", "state", 1) == 0) {
+ gtk_widget_hide_all (dtw->tool_toolbox);
+ } else {
+ gtk_widget_show_all (dtw->tool_toolbox);
+ }
+
+ if (prefs_get_int_attribute (fullscreen ? "fullscreen.statusbar" : "window.statusbar", "state", 1) == 0) {
+ gtk_widget_hide_all (dtw->statusbar);
+ } else {
+ gtk_widget_show_all (dtw->statusbar);
+ }
+
+ if (prefs_get_int_attribute (fullscreen ? "fullscreen.panels" : "window.panels", "state", 1) == 0) {
+ gtk_widget_hide_all( dtw->panels );
+ GList* kids = gtk_container_get_children( GTK_CONTAINER( dtw->statusbar ) );
+ if ( kids )
+ {
+ GList* last = g_list_last( kids );
+ if ( last )
+ {
+ GtkStatusbar* tail = GTK_STATUSBAR( last->data );
+ gtk_statusbar_set_has_resize_grip( tail, TRUE );
+ }
+ g_list_free( kids );
+ }
+ } else {
+ gtk_widget_show_all( dtw->panels );
+ GList* kids = gtk_container_get_children( GTK_CONTAINER( dtw->statusbar ) );
+ if ( kids )
+ {
+ GList* last = g_list_last( kids );
+ if ( last )
+ {
+ GtkStatusbar* tail = GTK_STATUSBAR( last->data );
+ gtk_statusbar_set_has_resize_grip( tail, FALSE );
+ }
+ g_list_free( kids );
+ }
+ }
+
+ if (prefs_get_int_attribute (fullscreen ? "fullscreen.scrollbars" : "window.scrollbars", "state", 1) == 0) {
+ gtk_widget_hide_all (dtw->hscrollbar);
+ gtk_widget_hide_all (dtw->vscrollbar_box);
+ } else {
+ gtk_widget_show_all (dtw->hscrollbar);
+ gtk_widget_show_all (dtw->vscrollbar_box);
+ }
+
+ if (prefs_get_int_attribute (fullscreen ? "fullscreen.rulers" : "window.rulers", "state", 1) == 0) {
+ gtk_widget_hide_all (dtw->hruler);
+ gtk_widget_hide_all (dtw->vruler);
+ } else {
+ gtk_widget_show_all (dtw->hruler);
+ gtk_widget_show_all (dtw->vruler);
+ }
+}
+
+void
+SPDesktopWidget::setToolboxFocusTo (const gchar* label)
+{
+ gpointer hb = sp_search_by_data_recursive(aux_toolbox, (gpointer) label);
+ if (hb && GTK_IS_WIDGET(hb))
+ {
+ gtk_widget_grab_focus(GTK_WIDGET(hb));
+ }
+}
+
+void
+SPDesktopWidget::setToolboxAdjustmentValue (gchar const *id, double value)
+{
+ gpointer hb = sp_search_by_data_recursive (aux_toolbox, (gpointer) id);
+ if (hb && GTK_IS_WIDGET(hb) && GTK_IS_SPIN_BUTTON(hb)) {
+ GtkAdjustment *a = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(hb));
+ gtk_adjustment_set_value (a, value);
+ }
+}
+
+bool
+SPDesktopWidget::isToolboxButtonActive (const gchar* id)
+{
+ GtkToggleButton *b = (GtkToggleButton *) sp_search_by_data_recursive(aux_toolbox, (gpointer) id);
+
+ return gtk_toggle_button_get_active (b) != 0;
+}
+
+SPViewWidget *
+sp_desktop_widget_new (SPNamedView *namedview)
+{
+ SPDesktopWidget *dtw = (SPDesktopWidget*)gtk_type_new (SP_TYPE_DESKTOP_WIDGET);
+
+ dtw->dt2r = 1.0 / namedview->doc_units->unittobase;
+ dtw->ruler_origin = namedview->gridorigin;
+
+ dtw->desktop = new SPDesktop();
+ dtw->stub = new SPDesktopWidget::WidgetStub (dtw);
+ dtw->desktop->registerEditWidget (dtw->stub);
+ dtw->desktop->init (namedview, dtw->canvas);
+ inkscape_add_desktop (dtw->desktop);
+
+ dtw->selected_style->setDesktop(dtw->desktop);
+
+ /* Once desktop is set, we can update rulers */
+ sp_desktop_widget_update_rulers (dtw);
+
+ sp_view_widget_set_view (SP_VIEW_WIDGET (dtw), dtw->desktop);
+
+ /* Listen on namedview modification */
+ g_signal_connect (G_OBJECT (namedview), "modified", G_CALLBACK (sp_desktop_widget_namedview_modified), dtw);
+
+ dtw->layer_selector->setDesktop(dtw->desktop);
+
+ dtw->menubar = sp_ui_main_menubar (dtw->desktop);
+ gtk_widget_show_all (dtw->menubar);
+ gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, FALSE, FALSE, 0);
+
+ sp_desktop_widget_layout (dtw);
+
+ sp_tool_toolbox_set_desktop (dtw->tool_toolbox, dtw->desktop);
+ sp_aux_toolbox_set_desktop (dtw->aux_toolbox, dtw->desktop);
+ sp_commands_toolbox_set_desktop (dtw->commands_toolbox, dtw->desktop);
+
+ return SP_VIEW_WIDGET (dtw);
+}
+
+void
+SPDesktopWidget::viewSetPosition (NR::Point p)
+{
+ NR::Point const origin = ( p - ruler_origin );
+
+ /// \todo fixme:
+ GTK_RULER(hruler)->position = origin[NR::X];
+ gtk_ruler_draw_pos (GTK_RULER (hruler));
+ GTK_RULER(vruler)->position = origin[NR::Y];
+ gtk_ruler_draw_pos (GTK_RULER (vruler));
+}
+
+void
+sp_desktop_widget_update_rulers (SPDesktopWidget *dtw)
+{
+ NR::Rect const viewbox = dtw->canvas->getViewbox();
+ double const scale = dtw->desktop->current_zoom();
+ double s = viewbox.min()[NR::X] / scale - dtw->ruler_origin[NR::X];
+ double e = viewbox.max()[NR::X] / scale - dtw->ruler_origin[NR::X];
+ gtk_ruler_set_range(GTK_RULER(dtw->hruler), s, e, GTK_RULER(dtw->hruler)->position, (e - s));
+ s = viewbox.min()[NR::Y] / -scale - dtw->ruler_origin[NR::Y];
+ e = viewbox.max()[NR::Y] / -scale - dtw->ruler_origin[NR::Y];
+ gtk_ruler_set_range(GTK_RULER(dtw->vruler), s, e, GTK_RULER(dtw->vruler)->position, (e - s));
+}
+
+
+static void
+sp_desktop_widget_namedview_modified (SPNamedView *nv, guint flags, SPDesktopWidget *dtw)
+{
+ if (flags & SP_OBJECT_MODIFIED_FLAG) {
+ dtw->dt2r = 1.0 / nv->doc_units->unittobase;
+ dtw->ruler_origin = nv->gridorigin;
+
+ sp_ruler_set_metric (GTK_RULER (dtw->vruler), nv->getDefaultMetric());
+ sp_ruler_set_metric (GTK_RULER (dtw->hruler), nv->getDefaultMetric());
+
+ gtk_tooltips_set_tip (dtw->tt, dtw->hruler_box, gettext(sp_unit_get_plural (nv->doc_units)), NULL);
+ gtk_tooltips_set_tip (dtw->tt, dtw->vruler_box, gettext(sp_unit_get_plural (nv->doc_units)), NULL);
+
+ sp_desktop_widget_update_rulers (dtw);
+ }
+}
+
+static void
+sp_desktop_widget_adjustment_value_changed (GtkAdjustment *adj, SPDesktopWidget *dtw)
+{
+ if (dtw->update)
+ return;
+
+ dtw->update = 1;
+
+ sp_canvas_scroll_to (dtw->canvas, dtw->hadj->value, dtw->vadj->value, FALSE);
+ sp_desktop_widget_update_rulers (dtw);
+
+ dtw->update = 0;
+}
+
+/* we make the desktop window with focus active, signal is connected in interface.c */
+
+gint
+sp_desktop_widget_set_focus (GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw)
+{
+ inkscape_activate_desktop (dtw->desktop);
+
+ /* give focus to canvas widget */
+ gtk_widget_grab_focus (GTK_WIDGET (dtw->canvas));
+
+ return FALSE;
+}
+
+static gdouble
+sp_dtw_zoom_value_to_display (gdouble value)
+{
+ return floor (pow (2, value) * 100.0 + 0.5);
+}
+
+static gdouble
+sp_dtw_zoom_display_to_value (gdouble value)
+{
+ return log (value / 100.0) / log (2);
+}
+
+static gint
+sp_dtw_zoom_input (GtkSpinButton *spin, gdouble *new_val, gpointer data)
+{
+ gdouble new_scrolled = gtk_spin_button_get_value (spin);
+ const gchar *b = gtk_entry_get_text (GTK_ENTRY (spin));
+ gdouble new_typed = atof (b);
+
+ if (sp_dtw_zoom_value_to_display (new_scrolled) == new_typed) { // the new value is set by scrolling
+ *new_val = new_scrolled;
+ } else { // the new value is typed in
+ *new_val = sp_dtw_zoom_display_to_value (new_typed);
+ }
+
+ return TRUE;
+}
+
+static bool
+sp_dtw_zoom_output (GtkSpinButton *spin, gpointer data)
+{
+ gchar b[64];
+ g_snprintf (b, 64, "%4.0f%%", sp_dtw_zoom_value_to_display (gtk_spin_button_get_value (spin)));
+ gtk_entry_set_text (GTK_ENTRY (spin), b);
+ return TRUE;
+}
+
+static void
+sp_dtw_zoom_value_changed (GtkSpinButton *spin, gpointer data)
+{
+ double const zoom_factor = pow (2, gtk_spin_button_get_value (spin));
+
+ SPDesktopWidget *dtw = SP_DESKTOP_WIDGET (data);
+ SPDesktop *desktop = dtw->desktop;
+
+ NR::Rect const d = desktop->get_display_area();
+ g_signal_handler_block (spin, dtw->zoom_update);
+ desktop->zoom_absolute (d.midpoint()[NR::X], d.midpoint()[NR::Y], zoom_factor);
+ g_signal_handler_unblock (spin, dtw->zoom_update);
+
+ spinbutton_defocus (GTK_OBJECT (spin));
+}
+
+static void
+sp_dtw_zoom_populate_popup (GtkEntry *entry, GtkMenu *menu, gpointer data)
+{
+ GList *children, *iter;
+ GtkWidget *item;
+ SPDesktop *dt = SP_DESKTOP_WIDGET (data)->desktop;
+
+ children = gtk_container_get_children (GTK_CONTAINER (menu));
+ for ( iter = children ; iter ; iter = g_list_next (iter)) {
+ gtk_container_remove (GTK_CONTAINER (menu), GTK_WIDGET (iter->data));
+ }
+ g_list_free (children);
+
+ item = gtk_menu_item_new_with_label ("200%");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_200), dt);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ item = gtk_menu_item_new_with_label ("100%");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_100), dt);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ item = gtk_menu_item_new_with_label ("50%");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_50), dt);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_separator_menu_item_new ();
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label (_("Page"));
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_page), dt);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ item = gtk_menu_item_new_with_label (_("Drawing"));
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_drawing), dt);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ item = gtk_menu_item_new_with_label (_("Selection"));
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_selection), dt);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+}
+
+static void
+sp_dtw_zoom_menu_handler (SPDesktop *dt, gdouble factor)
+{
+ NR::Rect const d = dt->get_display_area();
+ dt->zoom_absolute(d.midpoint()[NR::X], d.midpoint()[NR::Y], factor);
+}
+
+static void
+sp_dtw_zoom_50 (GtkMenuItem *item, gpointer data)
+{
+ sp_dtw_zoom_menu_handler (static_cast<SPDesktop*>(data), 0.5);
+}
+
+static void
+sp_dtw_zoom_100 (GtkMenuItem *item, gpointer data)
+{
+ sp_dtw_zoom_menu_handler (static_cast<SPDesktop*>(data), 1.0);
+}
+
+static void
+sp_dtw_zoom_200 (GtkMenuItem *item, gpointer data)
+{
+ sp_dtw_zoom_menu_handler (static_cast<SPDesktop*>(data), 2.0);
+}
+
+static void
+sp_dtw_zoom_page (GtkMenuItem *item, gpointer data)
+{
+ static_cast<SPDesktop*>(data)->zoom_page();
+}
+
+static void
+sp_dtw_zoom_drawing (GtkMenuItem *item, gpointer data)
+{
+ static_cast<SPDesktop*>(data)->zoom_drawing();
+}
+
+static void
+sp_dtw_zoom_selection (GtkMenuItem *item, gpointer data)
+{
+ static_cast<SPDesktop*>(data)->zoom_selection();
+}
+
+
+
+void
+sp_desktop_widget_update_zoom (SPDesktopWidget *dtw)
+{
+ g_signal_handlers_block_by_func (G_OBJECT (dtw->zoom_status), (gpointer)G_CALLBACK (sp_dtw_zoom_value_changed), dtw);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (dtw->zoom_status), log(dtw->desktop->current_zoom()) / log(2));
+ g_signal_handlers_unblock_by_func (G_OBJECT (dtw->zoom_status), (gpointer)G_CALLBACK (sp_dtw_zoom_value_changed), dtw);
+}
+
+void
+sp_desktop_widget_toggle_rulers (SPDesktopWidget *dtw)
+{
+ if (GTK_WIDGET_VISIBLE (dtw->hruler)) {
+ gtk_widget_hide_all (dtw->hruler);
+ gtk_widget_hide_all (dtw->vruler);
+ prefs_set_int_attribute (dtw->desktop->is_fullscreen ? "fullscreen.rulers" : "window.rulers", "state", 0);
+ } else {
+ gtk_widget_show_all (dtw->hruler);
+ gtk_widget_show_all (dtw->vruler);
+ prefs_set_int_attribute (dtw->desktop->is_fullscreen ? "fullscreen.rulers" : "window.rulers", "state", 1);
+ }
+}
+
+void
+sp_desktop_widget_toggle_scrollbars (SPDesktopWidget *dtw)
+{
+ if (GTK_WIDGET_VISIBLE (dtw->hscrollbar)) {
+ gtk_widget_hide_all (dtw->hscrollbar);
+ gtk_widget_hide_all (dtw->vscrollbar_box);
+ prefs_set_int_attribute (dtw->desktop->is_fullscreen ? "fullscreen.scrollbars" : "window.scrollbars", "state", 0);
+ } else {
+ gtk_widget_show_all (dtw->hscrollbar);
+ gtk_widget_show_all (dtw->vscrollbar_box);
+ prefs_set_int_attribute (dtw->desktop->is_fullscreen ? "fullscreen.scrollbars" : "window.scrollbars", "state", 1);
+ }
+}
+
+/* Unused
+void
+sp_spw_toggle_menubar (SPDesktopWidget *dtw, bool is_fullscreen)
+{
+ if (GTK_WIDGET_VISIBLE (dtw->menubar)) {
+ gtk_widget_hide_all (dtw->menubar);
+ prefs_set_int_attribute (is_fullscreen ? "fullscreen.menu" : "window.menu", "state", 0);
+ } else {
+ gtk_widget_show_all (dtw->menubar);
+ prefs_set_int_attribute (is_fullscreen ? "fullscreen.menu" : "window.menu", "state", 1);
+ }
+}
+*/
+
+static void
+set_adjustment (GtkAdjustment *adj, double l, double u, double ps, double si, double pi)
+{
+ if ((l != adj->lower) ||
+ (u != adj->upper) ||
+ (ps != adj->page_size) ||
+ (si != adj->step_increment) ||
+ (pi != adj->page_increment)) {
+ adj->lower = l;
+ adj->upper = u;
+ adj->page_size = ps;
+ adj->step_increment = si;
+ adj->page_increment = pi;
+ gtk_adjustment_changed (adj);
+ }
+}
+
+void
+sp_desktop_widget_update_scrollbars (SPDesktopWidget *dtw, double scale)
+{
+ if (!dtw) return;
+ if (dtw->update) return;
+ dtw->update = 1;
+
+ /* The desktop region we always show unconditionally */
+ SPDocument *doc = dtw->desktop->doc();
+ NR::Rect const r = sp_item_bbox_desktop(SP_ITEM(SP_DOCUMENT_ROOT(doc)));
+ NR::Rect darea(NR::Point(MIN(r.min()[NR::X], -sp_document_width(doc)),
+ MIN(r.min()[NR::Y], -sp_document_height(doc))),
+ NR::Point(MAX(r.max()[NR::X], 2 * sp_document_width(doc)),
+ MAX(r.max()[NR::Y], 2 * sp_document_height(doc))));
+
+ /* Canvas region we always show unconditionally */
+ NR::Rect carea(NR::Point(darea.min()[NR::X] * scale - 64,
+ darea.max()[NR::Y] * -scale - 64),
+ NR::Point(darea.max()[NR::X] * scale + 64,
+ darea.min()[NR::Y] * -scale + 64));
+
+ NR::Rect viewbox = dtw->canvas->getViewbox();
+
+ /* Viewbox is always included into scrollable region */
+ carea = NR::Rect::union_bounds(carea, viewbox);
+
+ set_adjustment(dtw->hadj, carea.min()[NR::X], carea.max()[NR::X],
+ viewbox.dimensions()[NR::X],
+ 0.1 * viewbox.dimensions()[NR::X],
+ viewbox.dimensions()[NR::X]);
+ gtk_adjustment_set_value(dtw->hadj, viewbox.min()[NR::X]);
+
+ set_adjustment(dtw->vadj, carea.min()[NR::Y], carea.max()[NR::Y],
+ viewbox.dimensions()[NR::Y],
+ 0.1 * viewbox.dimensions()[NR::Y],
+ viewbox.dimensions()[NR::Y]);
+ gtk_adjustment_set_value(dtw->vadj, viewbox.min()[NR::Y]);
+
+ dtw->update = 0;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/widgets/desktop-widget.h b/src/widgets/desktop-widget.h
new file mode 100644
index 000000000..5129fbf49
--- /dev/null
+++ b/src/widgets/desktop-widget.h
@@ -0,0 +1,201 @@
+#ifndef SEEN_SP_DESKTOP_WIDGET_H
+#define SEEN_SP_DESKTOP_WIDGET_H
+
+/** \file
+ * SPDesktopWidget: handling Gtk events on a desktop.
+ *
+ * Authors:
+ * Ralf Stephan <ralf@ark.in-berlin.de> (c) 2005, distrib. under GPL2
+ * ? -2004
+ */
+
+#include <gtk/gtktooltips.h>
+#include <gtk/gtkwindow.h>
+
+#include "display/display-forward.h"
+#include "libnr/nr-point.h"
+#include "forward.h"
+#include "message.h"
+#include "ui/view/view-widget.h"
+#include "ui/view/edit-widget-interface.h"
+
+#define SP_TYPE_DESKTOP_WIDGET (sp_desktop_widget_get_type ())
+#define SP_DESKTOP_WIDGET(o) (GTK_CHECK_CAST ((o), SP_TYPE_DESKTOP_WIDGET, SPDesktopWidget))
+#define SP_DESKTOP_WIDGET_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_DESKTOP_WIDGET, SPDesktopWidgetClass))
+#define SP_IS_DESKTOP_WIDGET(o) (GTK_CHECK_TYPE ((o), SP_TYPE_DESKTOP_WIDGET))
+#define SP_IS_DESKTOP_WIDGET_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_DESKTOP_WIDGET))
+
+GtkType sp_desktop_widget_get_type();
+
+void sp_desktop_widget_destroy (SPDesktopWidget* dtw);
+
+gint sp_desktop_widget_set_focus(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw);
+
+void sp_desktop_widget_show_decorations(SPDesktopWidget *dtw, gboolean show);
+void sp_desktop_widget_fullscreen(SPDesktopWidget *dtw);
+void sp_desktop_widget_layout(SPDesktopWidget *dtw);
+void sp_desktop_widget_update_zoom(SPDesktopWidget *dtw);
+void sp_desktop_widget_update_rulers (SPDesktopWidget *dtw);
+
+/* Show/hide rulers & scrollbars */
+void sp_desktop_widget_toggle_rulers (SPDesktopWidget *dtw);
+void sp_desktop_widget_toggle_scrollbars (SPDesktopWidget *dtw);
+void sp_desktop_widget_update_scrollbars (SPDesktopWidget *dtw, double scale);
+
+void sp_dtw_desktop_activate (SPDesktopWidget *dtw);
+void sp_dtw_desktop_deactivate (SPDesktopWidget *dtw);
+
+namespace Inkscape { namespace Widgets { class LayerSelector; } }
+
+namespace Inkscape { namespace UI { namespace Widget { class SelectedStyle; } } }
+
+/// A GtkEventBox on an SPDesktop.
+struct SPDesktopWidget {
+ SPViewWidget viewwidget;
+
+ unsigned int update : 1;
+
+ GtkTooltips *tt;
+
+ SPDesktop *desktop;
+
+ GtkWindow *window;
+
+ // The root vbox of the window layout.
+ GtkWidget *vbox;
+
+ GtkWidget *menubar, *statusbar;
+
+ GtkWidget *panels;
+
+ GtkWidget *hscrollbar, *vscrollbar, *vscrollbar_box;
+
+ GtkWidget *tool_toolbox, *aux_toolbox, *commands_toolbox;
+
+ /* Rulers */
+ GtkWidget *hruler, *vruler;
+ GtkWidget *hruler_box, *vruler_box; // eventboxes for setting tooltips
+
+ GtkWidget *sticky_zoom;
+ GtkWidget *coord_status;
+ GtkWidget *coord_status_x;
+ GtkWidget *coord_status_y;
+ GtkWidget *select_status;
+ GtkWidget *select_status_eventbox;
+ GtkWidget *zoom_status;
+ gulong zoom_update;
+
+ Inkscape::UI::Widget::SelectedStyle *selected_style;
+
+ gint coord_status_id, select_status_id;
+
+ SPCanvas *canvas;
+ NR::Point ruler_origin;
+ double dt2r;
+
+ GtkAdjustment *hadj, *vadj;
+
+ Inkscape::Widgets::LayerSelector *layer_selector;
+
+ struct WidgetStub : public Inkscape::UI::View::EditWidgetInterface {
+ SPDesktopWidget *_dtw;
+ SPDesktop *_dt;
+ WidgetStub (SPDesktopWidget* dtw) : _dtw(dtw) {}
+
+ virtual void setTitle (gchar const *uri)
+ { _dtw->updateTitle (uri); }
+ virtual void* getWindow()
+ { return _dtw->window; }
+ virtual void layout()
+ { sp_desktop_widget_layout (_dtw); }
+ virtual void present()
+ { _dtw->presentWindow(); }
+ virtual void getGeometry (gint &x, gint &y, gint &w, gint &h)
+ { _dtw->getWindowGeometry (x, y, w, h); }
+ virtual void setSize (gint w, gint h)
+ { _dtw->setWindowSize (w, h); }
+ virtual void setPosition (NR::Point p)
+ { _dtw->setWindowPosition (p); }
+ virtual void setTransient (void* p, int transient_policy)
+ { _dtw->setWindowTransient (p, transient_policy); }
+ virtual NR::Point getPointer()
+ { return _dtw->window_get_pointer(); }
+ virtual void setFullscreen()
+ { sp_desktop_widget_fullscreen (_dtw); }
+ virtual bool shutdown()
+ { return _dtw->shutdown(); }
+ virtual void destroy()
+ { gtk_widget_destroy (static_cast<GtkWidget*>((void*)(_dtw->window)));}
+
+ virtual void requestCanvasUpdate()
+ { _dtw->requestCanvasUpdate(); }
+ virtual void activateDesktop()
+ { sp_dtw_desktop_activate (_dtw); }
+ virtual void deactivateDesktop()
+ { sp_dtw_desktop_deactivate (_dtw); }
+ virtual void viewSetPosition (NR::Point p)
+ { _dtw->viewSetPosition (p); }
+ virtual void updateRulers()
+ { sp_desktop_widget_update_rulers (_dtw); }
+ virtual void updateScrollbars (double scale)
+ { sp_desktop_widget_update_scrollbars (_dtw, scale); }
+ virtual void toggleRulers()
+ { sp_desktop_widget_toggle_rulers (_dtw); }
+ virtual void toggleScrollbars()
+ { sp_desktop_widget_toggle_scrollbars (_dtw); }
+ virtual void updateZoom()
+ { sp_desktop_widget_update_zoom (_dtw); }
+ virtual void letZoomGrabFocus()
+ { _dtw->letZoomGrabFocus(); }
+ virtual void setToolboxFocusTo (const gchar * id)
+ { _dtw->setToolboxFocusTo (id); }
+ virtual void setToolboxAdjustmentValue (const gchar *id, double val)
+ { _dtw->setToolboxAdjustmentValue (id, val); }
+ virtual bool isToolboxButtonActive (gchar const* id)
+ { return _dtw->isToolboxButtonActive (id); }
+ virtual void setCoordinateStatus (NR::Point p)
+ { _dtw->setCoordinateStatus (p); }
+ virtual void setMessage (Inkscape::MessageType type, gchar const* msg)
+ { _dtw->setMessage (type, msg); }
+ virtual bool warnDialog (gchar* text)
+ { return _dtw->warnDialog (text); }
+ };
+
+ WidgetStub *stub;
+
+ void setMessage(Inkscape::MessageType type, gchar const *message);
+ NR::Point window_get_pointer();
+ bool shutdown();
+ void viewSetPosition (NR::Point p);
+ void letZoomGrabFocus();
+ void getWindowGeometry (gint &x, gint &y, gint &w, gint &h);
+ void setWindowPosition (NR::Point p);
+ void setWindowSize (gint w, gint h);
+ void setWindowTransient (void *p, int transient_policy);
+ void presentWindow();
+ bool warnDialog (gchar *text);
+ void setToolboxFocusTo (gchar const *);
+ void setToolboxAdjustmentValue (gchar const * id, double value);
+ bool isToolboxButtonActive (gchar const *id);
+ void setCoordinateStatus(NR::Point p);
+ void requestCanvasUpdate();
+ void updateTitle(gchar const *uri);
+};
+
+/// The SPDesktopWidget vtable
+struct SPDesktopWidgetClass {
+ SPViewWidgetClass parent_class;
+};
+
+#endif /* !SEEN_SP_DESKTOP_WIDGET_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/widgets/font-selector.cpp b/src/widgets/font-selector.cpp
new file mode 100644
index 000000000..b73ef5fc1
--- /dev/null
+++ b/src/widgets/font-selector.cpp
@@ -0,0 +1,722 @@
+#define __SP_FONT_SELECTOR_C__
+
+/*
+ * Font selection widgets
+ *
+ * Authors:
+ * Chris Lahey <clahey@ximian.com>
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 1999-2001 Ximian, Inc.
+ * Copyright (C) 2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <libnr/nr-blit.h>
+#include <libnrtype/font-instance.h>
+#include <libnrtype/raster-glyph.h>
+#include <libnrtype/RasterFont.h>
+#include <libnrtype/TextWrapper.h>
+#include <libnrtype/one-glyph.h>
+
+#include <gtk/gtkframe.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtkclist.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkcombo.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtkdrawingarea.h>
+
+#include "../display/nr-plain-stuff-gdk.h"
+#include <glibmm/i18n.h>
+
+#include "font-selector.h"
+
+/* SPFontSelector */
+
+struct SPFontSelector
+{
+ GtkHBox hbox;
+
+ unsigned int block_emit : 1;
+
+ GtkWidget *family;
+ GtkWidget *style;
+ GtkWidget *size;
+
+ NRNameList families;
+ NRStyleList styles;
+ int familyidx;
+ int styleidx;
+ gfloat fontsize;
+ bool fontsize_dirty;
+ font_instance *font;
+};
+
+
+struct SPFontSelectorClass
+{
+ GtkHBoxClass parent_class;
+
+ void (* font_set) (SPFontSelector *fsel, font_instance *font);
+};
+
+enum {
+ FONT_SET,
+ LAST_SIGNAL
+};
+
+static void sp_font_selector_class_init(SPFontSelectorClass *c);
+static void sp_font_selector_init(SPFontSelector *fsel);
+static void sp_font_selector_destroy(GtkObject *object);
+
+static void sp_font_selector_family_select_row(GtkCList *clist, gint row, gint column,
+ GdkEvent *event, SPFontSelector *fsel);
+static void sp_font_selector_style_select_row(GtkCList *clist, gint row, gint column,
+ GdkEvent *event, SPFontSelector *fsel);
+static void sp_font_selector_size_changed(GtkEditable *editable, SPFontSelector *fsel);
+
+static void sp_font_selector_emit_set(SPFontSelector *fsel);
+
+static const gchar *sizes[] = {
+ "4", "6", "8", "9", "10", "11", "12", "13", "14",
+ "16", "18", "20", "22", "24", "28",
+ "32", "36", "40", "48", "56", "64", "72", "144",
+ NULL
+};
+
+static GtkHBoxClass *fs_parent_class = NULL;
+static guint fs_signals[LAST_SIGNAL] = { 0 };
+
+GtkType sp_font_selector_get_type()
+{
+ static GtkType type = 0;
+ if (!type) {
+ static const GtkTypeInfo info = {
+ "SPFontSelector",
+ sizeof(SPFontSelector),
+ sizeof(SPFontSelectorClass),
+ (GtkClassInitFunc) sp_font_selector_class_init,
+ (GtkObjectInitFunc) sp_font_selector_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique(GTK_TYPE_HBOX, &info);
+ }
+ return type;
+}
+
+static void sp_font_selector_class_init(SPFontSelectorClass *c)
+{
+ GtkObjectClass *object_class = (GtkObjectClass *) c;
+
+ fs_parent_class = (GtkHBoxClass* )gtk_type_class(GTK_TYPE_HBOX);
+
+ fs_signals[FONT_SET] = gtk_signal_new ("font_set",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET(SPFontSelectorClass, font_set),
+ gtk_marshal_NONE__POINTER,
+ GTK_TYPE_NONE,
+ 1, GTK_TYPE_POINTER);
+
+ object_class->destroy = sp_font_selector_destroy;
+}
+
+static void sp_font_selector_init(SPFontSelector *fsel)
+{
+ gtk_box_set_homogeneous(GTK_BOX(fsel), TRUE);
+ gtk_box_set_spacing(GTK_BOX(fsel), 4);
+
+ /* Family frame */
+ GtkWidget *f = gtk_frame_new(_("Font family"));
+ gtk_widget_show(f);
+ gtk_box_pack_start(GTK_BOX(fsel), f, TRUE, TRUE, 0);
+
+ GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
+ gtk_widget_show(sw);
+ gtk_container_set_border_width(GTK_CONTAINER (sw), 4);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(f), sw);
+
+ fsel->family = gtk_clist_new (1);
+ gtk_widget_show (fsel->family);
+ gtk_clist_set_selection_mode(GTK_CLIST(fsel->family), GTK_SELECTION_SINGLE);
+ gtk_clist_column_titles_hide(GTK_CLIST(fsel->family));
+ gtk_signal_connect(GTK_OBJECT(fsel->family), "select_row", GTK_SIGNAL_FUNC(sp_font_selector_family_select_row), fsel);
+ gtk_container_add(GTK_CONTAINER(sw), fsel->family);
+
+ if ((font_factory::Default())->Families(&fsel->families)) {
+ gtk_clist_freeze(GTK_CLIST(fsel->family));
+ for (guint i = 0; i < fsel->families.length; i++) {
+ gtk_clist_append(GTK_CLIST(fsel->family), (gchar **) fsel->families.names + i);
+ gtk_clist_set_row_data(GTK_CLIST(fsel->family), i, GUINT_TO_POINTER(i));
+ }
+ gtk_clist_thaw(GTK_CLIST(fsel->family));
+ }
+
+ /* Style frame */
+ f = gtk_frame_new(_("Style"));
+ gtk_widget_show(f);
+ gtk_box_pack_start(GTK_BOX (fsel), f, TRUE, TRUE, 0);
+
+ GtkWidget *vb = gtk_vbox_new(FALSE, 4);
+ gtk_widget_show(vb);
+ gtk_container_set_border_width(GTK_CONTAINER (vb), 4);
+ gtk_container_add(GTK_CONTAINER(f), vb);
+
+ sw = gtk_scrolled_window_new(NULL, NULL);
+ gtk_widget_show(sw);
+ gtk_container_set_border_width(GTK_CONTAINER (sw), 4);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start(GTK_BOX (vb), sw, TRUE, TRUE, 0);
+
+ fsel->style = gtk_clist_new (1);
+ gtk_widget_show (fsel->style);
+ gtk_clist_set_selection_mode(GTK_CLIST (fsel->style), GTK_SELECTION_SINGLE);
+ gtk_clist_column_titles_hide(GTK_CLIST (fsel->style));
+ gtk_signal_connect(GTK_OBJECT(fsel->style), "select_row", GTK_SIGNAL_FUNC (sp_font_selector_style_select_row), fsel);
+ gtk_container_add(GTK_CONTAINER(sw), fsel->style);
+
+ GtkWidget *hb = gtk_hbox_new(FALSE, 4);
+ gtk_widget_show(hb);
+ gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 0);
+
+ fsel->size = gtk_combo_new();
+ gtk_widget_show (fsel->size);
+ gtk_combo_set_value_in_list(GTK_COMBO (fsel->size), FALSE, FALSE);
+ gtk_combo_set_use_arrows(GTK_COMBO (fsel->size), TRUE);
+ gtk_combo_set_use_arrows_always(GTK_COMBO (fsel->size), TRUE);
+ gtk_widget_set_size_request(fsel->size, 90, -1);
+ gtk_signal_connect(GTK_OBJECT(GTK_COMBO(fsel->size)->entry), "changed", GTK_SIGNAL_FUNC(sp_font_selector_size_changed), fsel);
+ gtk_box_pack_end(GTK_BOX(hb), fsel->size, FALSE, FALSE, 0);
+
+ GtkWidget *l = gtk_label_new(_("Font size:"));
+ gtk_widget_show(l);
+ gtk_box_pack_end(GTK_BOX (hb), l, FALSE, FALSE, 0);
+
+ /* Setup strings */
+ GList *sl = NULL;
+ for (int i = 0; sizes[i] != NULL; i++) {
+ sl = g_list_prepend (sl, (gpointer) sizes[i]);
+ }
+ sl = g_list_reverse (sl);
+ gtk_combo_set_popdown_strings(GTK_COMBO(fsel->size), sl);
+ g_list_free (sl);
+
+ fsel->familyidx = 0;
+ fsel->styleidx = 0;
+ fsel->fontsize = 10.0;
+ fsel->fontsize_dirty = false;
+ fsel->font = NULL;
+}
+
+static void sp_font_selector_destroy(GtkObject *object)
+{
+ SPFontSelector *fsel = SP_FONT_SELECTOR (object);
+
+ if (fsel->font) {
+ fsel->font->Unref();
+ fsel->font = NULL;
+ }
+
+ if (fsel->families.length > 0) {
+ nr_name_list_release(&fsel->families);
+ fsel->families.length = 0;
+ }
+
+ if (fsel->styles.length > 0) {
+ nr_style_list_release(&fsel->styles);
+ fsel->styles.length = 0;
+ }
+
+ if (GTK_OBJECT_CLASS(fs_parent_class)->destroy) {
+ GTK_OBJECT_CLASS(fs_parent_class)->destroy(object);
+ }
+}
+
+static void sp_font_selector_family_select_row(GtkCList *clist, gint row, gint column,
+ GdkEvent *event, SPFontSelector *fsel)
+{
+ fsel->familyidx = GPOINTER_TO_UINT (gtk_clist_get_row_data (clist, row));
+
+ if (fsel->styles.length > 0) {
+ nr_style_list_release (&fsel->styles);
+ fsel->styles.length = 0;
+ fsel->styleidx = 0;
+ }
+ gtk_clist_clear (GTK_CLIST (fsel->style));
+
+ if ( static_cast<unsigned int> (fsel->familyidx) < fsel->families.length ) {
+
+ const gchar *family = (const gchar *) fsel->families.names[fsel->familyidx];
+
+ if ((font_factory::Default())->Styles(family, &fsel->styles)) {
+
+ gtk_clist_freeze(GTK_CLIST(fsel->style));
+ for (unsigned int i = 0; i < fsel->styles.length; i++) {
+
+ const gchar *p = (const gchar *) ((fsel->styles.records)[i].name);
+
+ gtk_clist_append(GTK_CLIST(fsel->style), (gchar **) &p);
+ gtk_clist_set_row_data(GTK_CLIST(fsel->style), static_cast<gint> (i), GUINT_TO_POINTER (i));
+ }
+ gtk_clist_thaw(GTK_CLIST(fsel->style));
+ gtk_clist_select_row(GTK_CLIST(fsel->style), 0, 0);
+ }
+ }
+}
+
+static void sp_font_selector_style_select_row(GtkCList *clist, gint row, gint column,
+ GdkEvent *event, SPFontSelector *fsel)
+{
+ fsel->styleidx = GPOINTER_TO_UINT(gtk_clist_get_row_data(clist, row));
+
+ if (!fsel->block_emit) {
+ sp_font_selector_emit_set (fsel);
+ }
+}
+
+static void sp_font_selector_size_changed(GtkEditable *editable, SPFontSelector *fsel)
+{
+ const gchar *sstr = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(fsel->size)->entry));
+ gfloat old_size = fsel->fontsize;
+ fsel->fontsize = MAX(atof(sstr), 0.1);
+ if ( fabs(fsel->fontsize-old_size) > 0.001) {
+ fsel->fontsize_dirty = true;
+ }
+
+ sp_font_selector_emit_set(fsel);
+}
+
+static void sp_font_selector_emit_set(SPFontSelector *fsel)
+{
+ font_instance *font;
+
+ if (static_cast<unsigned int>(fsel->styleidx) < fsel->styles.length
+ && static_cast<unsigned int>(fsel->familyidx) < fsel->families.length)
+ {
+ font = (font_factory::Default())->FaceFromDescr ((gchar *) ((fsel->families.names)[fsel->familyidx]),
+ (gchar *) ((fsel->styles.records)[fsel->styleidx].name));
+ } else {
+ font = NULL;
+ }
+
+ // FIXME: when a text object uses non-available font, font==NULL and we can't set size
+ // (and the size shown in the widget is invalid). To fix, here we must always get some
+ // default font, exactly the same as sptext uses for on-canvas display, so that
+ // font!=NULL ever.
+ if (font != fsel->font || ( font && fsel->fontsize_dirty ) ) {
+ if ( font ) {
+ font->Ref();
+ }
+ if ( fsel->font ) {
+ fsel->font->Unref();
+ }
+ fsel->font = font;
+ gtk_signal_emit(GTK_OBJECT(fsel), fs_signals[FONT_SET], fsel->font);
+ }
+ fsel->fontsize_dirty = false;
+ if (font) {
+ font->Unref();
+ }
+ font = NULL;
+}
+
+GtkWidget *sp_font_selector_new()
+{
+ SPFontSelector *fsel = (SPFontSelector*) gtk_type_new(SP_TYPE_FONT_SELECTOR);
+
+ return (GtkWidget *) fsel;
+}
+
+void sp_font_selector_set_font(SPFontSelector *fsel, font_instance *font, double size)
+{
+ GtkCList *fcl = GTK_CLIST(fsel->family);
+ GtkCList *scl = GTK_CLIST(fsel->style);
+
+ if (font && (fsel->font != font || size != fsel->fontsize)) {
+ { // select family in the list
+ gchar family[256];
+ font->Family (family, 256);
+
+ unsigned int i;
+ for (i = 0; i < fsel->families.length; i++) {
+ if (!strcasecmp (family, (gchar *)fsel->families.names[i])) {
+ break;
+ }
+ }
+
+ if (i >= fsel->families.length) {
+ return;
+ }
+
+ fsel->block_emit = TRUE;
+ gtk_clist_select_row(fcl, i, 0);
+ gtk_clist_moveto(fcl, i, 0, 0.66, 0.0);
+ fsel->block_emit = FALSE;
+ }
+
+ { // select best-matching style in the list
+ gchar descr[256];
+ font->Name(descr, 256);
+ PangoFontDescription *descr_ = pango_font_description_from_string(descr);
+ PangoFontDescription *best_ = pango_font_description_from_string((fsel->styles.records)[0].descr);
+ guint best_i = 0;
+
+ for (guint i = 0; i < fsel->styles.length; i++) {
+ PangoFontDescription *try_ = pango_font_description_from_string((fsel->styles.records)[i].descr);
+ if (pango_font_description_better_match(descr_, best_, try_)) {
+ pango_font_description_free(best_);
+ best_ = pango_font_description_from_string((fsel->styles.records)[i].descr);
+ best_i = i;
+ }
+ pango_font_description_free(try_);
+ }
+
+ gtk_clist_select_row(scl, best_i, 0);
+ gtk_clist_moveto(scl, best_i, 0, 0.66, 0.0);
+ }
+
+ if (size != fsel->fontsize) {
+ gchar s[8];
+ g_snprintf (s, 8, "%.5g", size); // UI, so printf is ok
+ gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (fsel->size)->entry), s);
+ fsel->fontsize = size;
+ }
+ }
+}
+
+font_instance* sp_font_selector_get_font(SPFontSelector *fsel)
+{
+ if (fsel->font) {
+ fsel->font->Ref();
+ }
+
+ return fsel->font;
+}
+
+double sp_font_selector_get_size(SPFontSelector *fsel)
+{
+ return fsel->fontsize;
+}
+
+/* SPFontPreview */
+
+struct SPFontPreview
+{
+ GtkDrawingArea darea;
+
+ font_instance *font;
+ raster_font *rfont;
+ gchar *phrase;
+ unsigned long rgba;
+};
+
+struct SPFontPreviewClass
+{
+ GtkDrawingAreaClass parent_class;
+};
+
+static void sp_font_preview_class_init(SPFontPreviewClass *c);
+static void sp_font_preview_init(SPFontPreview *fsel);
+static void sp_font_preview_destroy(GtkObject *object);
+
+void sp_font_preview_size_request(GtkWidget *widget, GtkRequisition *req);
+static gint sp_font_preview_expose(GtkWidget *widget, GdkEventExpose *event);
+
+static GtkDrawingAreaClass *fp_parent_class = NULL;
+
+GtkType sp_font_preview_get_type()
+{
+ static GtkType type = 0;
+ if (!type) {
+ static const GtkTypeInfo info = {
+ "SPFontPreview",
+ sizeof (SPFontPreview),
+ sizeof (SPFontPreviewClass),
+ (GtkClassInitFunc) sp_font_preview_class_init,
+ (GtkObjectInitFunc) sp_font_preview_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_DRAWING_AREA, &info);
+ }
+ return type;
+}
+
+static void sp_font_preview_class_init (SPFontPreviewClass *c)
+{
+ GtkObjectClass *object_class = (GtkObjectClass *) c;
+ GtkWidgetClass *widget_class = (GtkWidgetClass *) c;
+
+ fp_parent_class = (GtkDrawingAreaClass*) gtk_type_class(GTK_TYPE_DRAWING_AREA);
+
+ object_class->destroy = sp_font_preview_destroy;
+
+ widget_class->size_request = sp_font_preview_size_request;
+ widget_class->expose_event = sp_font_preview_expose;
+}
+
+static void sp_font_preview_init(SPFontPreview *fprev)
+{
+ fprev->rgba = 0x000000ff;
+}
+
+static void sp_font_preview_destroy(GtkObject *object)
+{
+ SPFontPreview *fprev = SP_FONT_PREVIEW (object);
+
+ if (fprev->rfont) {
+ fprev->rfont->Unref();
+ fprev->rfont = NULL;
+ }
+
+ if (fprev->font) {
+ fprev->font->Unref();
+ fprev->font = NULL;
+ }
+
+ g_free(fprev->phrase);
+ fprev->phrase = NULL;
+
+ if (GTK_OBJECT_CLASS (fp_parent_class)->destroy) {
+ GTK_OBJECT_CLASS (fp_parent_class)->destroy(object);
+ }
+}
+
+void sp_font_preview_size_request(GtkWidget *widget, GtkRequisition *req)
+{
+ req->width = 256;
+ req->height = 32;
+}
+
+#define SPFP_MAX_LEN 64
+
+static gint sp_font_preview_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+ SPFontPreview *fprev = SP_FONT_PREVIEW(widget);
+
+ if (GTK_WIDGET_DRAWABLE (widget)) {
+ if (fprev->rfont) {
+
+ int glyphs[SPFP_MAX_LEN];
+ double hpos[SPFP_MAX_LEN];
+
+ font_instance *tface = fprev->rfont->daddy;
+
+ double theSize = NR_MATRIX_DF_EXPANSION (&fprev->rfont->style.transform);
+
+ gchar const *p;
+ if (fprev->phrase) {
+ p = fprev->phrase;
+ } else {
+ /* TRANSLATORS: Test string used in text and font dialog (when no
+ * text has been entered) to get a preview of the font. Choose
+ * some representative characters that users of your locale will be
+ * interested in. */
+ p = _("AaBbCcIiPpQq12368.;/()");
+ }
+ int len = 0;
+
+ NRRect bbox;
+ bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0.0;
+
+ text_wrapper* str_text=new text_wrapper;
+ str_text->SetDefaultFont(tface);
+ str_text->AppendUTF8(p,-1);
+ if ( str_text->uni32_length > 0 ) {
+ str_text->DoLayout();
+ if ( str_text->glyph_length > 0 ) {
+ PangoFont *curPF = NULL;
+ font_instance *curF = NULL;
+ for (int i = 0; i < str_text->glyph_length && i < SPFP_MAX_LEN; i++) {
+ if ( str_text->glyph_text[i].font != curPF ) {
+ curPF = str_text->glyph_text[i].font;
+ if (curF) {
+ curF->Unref();
+ }
+ curF = NULL;
+ if ( curPF ) {
+ PangoFontDescription* pfd = pango_font_describe(curPF);
+ curF = (font_factory::Default())->Face(pfd);
+ pango_font_description_free(pfd);
+ }
+ }
+ NR::Point base_pt(str_text->glyph_text[i].x, str_text->glyph_text[i].y);
+ base_pt *= theSize;
+
+ glyphs[len] = str_text->glyph_text[i].gl;
+ hpos[len] = base_pt[0];
+ len++;
+ if ( curF ) {
+ NR::Rect nbbox = curF->BBox(str_text->glyph_text[i].gl);
+ bbox.x0 = MIN(bbox.x0, base_pt[NR::X] + theSize * (nbbox.min())[0]);
+ bbox.y0 = MIN(bbox.y0, base_pt[NR::Y] - theSize * (nbbox.max())[1]);
+ bbox.x1 = MAX(bbox.x1, base_pt[NR::X] + theSize * (nbbox.max())[0]);
+ bbox.y1 = MAX(bbox.y1, base_pt[NR::Y] - theSize * (nbbox.min())[1]);
+ }
+ }
+ if ( curF ) {
+ curF->Unref();
+ }
+ }
+ }
+
+ // XXX: FIXME: why does this code ignore adv.y
+ /* while (p && *p && (len < SPFP_MAX_LEN)) {
+ unsigned int unival;
+ NRRect gbox;
+ unival = g_utf8_get_char (p);
+ glyphs[len] = tface->MapUnicodeChar( unival);
+ hpos[len] = (int)px;
+ NR::Point adv = fprev->rfont->Advance(glyphs[len]);
+ fprev->rfont->BBox( glyphs[len], &gbox);
+ bbox.x0 = MIN (px + gbox.x0, bbox.x0);
+ bbox.y0 = MIN (py + gbox.y0, bbox.y0);
+ bbox.x1 = MAX (px + gbox.x1, bbox.x1);
+ bbox.y1 = MAX (py + gbox.y1, bbox.y1);
+ px += adv[NR::X];
+ len += 1;
+ p = g_utf8_next_char (p);
+ }*/
+
+ float startx = (widget->allocation.width - (bbox.x1 - bbox.x0)) / 2;
+ float starty = widget->allocation.height - (widget->allocation.height - (bbox.y1 - bbox.y0)) / 2 - bbox.y1;
+
+ for (int y = event->area.y; y < event->area.y + event->area.height; y += 64) {
+ for (int x = event->area.x; x < event->area.x + event->area.width; x += 64) {
+ NRPixBlock pb, m;
+ int x0 = x;
+ int y0 = y;
+ int x1 = MIN(x0 + 64, event->area.x + event->area.width);
+ int y1 = MIN(y0 + 64, event->area.y + event->area.height);
+ guchar *ps = nr_pixelstore_16K_new (TRUE, 0xff);
+ nr_pixblock_setup_extern(&pb, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, ps, 3 * (x1 - x0), FALSE, FALSE);
+ nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, x0, y0, x1, y1, TRUE);
+ pb.empty = FALSE;
+
+ PangoFont *curPF = NULL;
+ font_instance *curF = NULL;
+ raster_font *curRF = NULL;
+ for (int i=0; i < len; i++) {
+ if ( str_text->glyph_text[i].font != curPF ) {
+ curPF=str_text->glyph_text[i].font;
+ if ( curF ) {
+ curF->Unref();
+ }
+ curF = NULL;
+ if ( curPF ) {
+ PangoFontDescription* pfd = pango_font_describe(curPF);
+ curF=(font_factory::Default())->Face(pfd);
+ pango_font_description_free(pfd);
+ }
+ if ( curF ) {
+ if ( curRF ) {
+ curRF->Unref();
+ }
+ curRF = NULL;
+ curRF = curF->RasterFont(fprev->rfont->style);
+ }
+ }
+ raster_glyph *g = (curRF) ? curRF->GetGlyph(glyphs[i]) : NULL;
+ if ( g ) {
+ g->Blit(NR::Point(hpos[i] + startx, starty), m);
+ }
+ }
+ if (curRF) {
+ curRF->Unref();
+ }
+ if (curF) {
+ curF->Unref();
+ }
+
+ nr_blit_pixblock_mask_rgba32(&pb, &m, fprev->rgba);
+ gdk_draw_rgb_image(widget->window, widget->style->black_gc,
+ x0, y0, x1 - x0, y1 - y0,
+ GDK_RGB_DITHER_NONE, NR_PIXBLOCK_PX (&pb), pb.rs);
+ nr_pixblock_release(&m);
+ nr_pixblock_release(&pb);
+ nr_pixelstore_16K_free(ps);
+ }
+ }
+
+ delete str_text;
+
+ } else {
+ nr_gdk_draw_gray_garbage(widget->window, widget->style->black_gc,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+ }
+ }
+
+ return TRUE;
+}
+
+GtkWidget * sp_font_preview_new()
+{
+ GtkWidget *w = (GtkWidget*) gtk_type_new(SP_TYPE_FONT_PREVIEW);
+
+ return w;
+}
+
+void sp_font_preview_set_font(SPFontPreview *fprev, font_instance *font, SPFontSelector *fsel)
+{
+ if (font != fprev->font) {
+ if (font) {
+ font->Ref();
+ }
+ if (fprev->font) {
+ fprev->font->Unref();
+ }
+ fprev->font = font;
+
+ if (fprev->rfont) {
+ fprev->rfont->Unref();
+ fprev->rfont=NULL;
+ }
+ if (fprev->font) {
+ NRMatrix flip;
+ nr_matrix_set_scale (&flip, fsel->fontsize, -fsel->fontsize);
+ fprev->rfont = fprev->font->RasterFont(flip, 0);
+ }
+ if (GTK_WIDGET_DRAWABLE (fprev)) gtk_widget_queue_draw (GTK_WIDGET (fprev));
+ }
+}
+
+void sp_font_preview_set_rgba32(SPFontPreview *fprev, guint32 rgba)
+{
+ fprev->rgba = rgba;
+ if (GTK_WIDGET_DRAWABLE (fprev)) {
+ gtk_widget_queue_draw (GTK_WIDGET (fprev));
+ }
+}
+
+void sp_font_preview_set_phrase(SPFontPreview *fprev, const gchar *phrase)
+{
+ g_free (fprev->phrase);
+ if (phrase) {
+ fprev->phrase = g_strdup (phrase);
+ } else {
+ fprev->phrase = NULL;
+ }
+ if (GTK_WIDGET_DRAWABLE(fprev)) {
+ gtk_widget_queue_draw (GTK_WIDGET (fprev));
+ }
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/widgets/font-selector.h b/src/widgets/font-selector.h
new file mode 100644
index 000000000..094db0343
--- /dev/null
+++ b/src/widgets/font-selector.h
@@ -0,0 +1,67 @@
+#ifndef __SP_FONT_SELECTOR_H__
+#define __SP_FONT_SELECTOR_H__
+
+/*
+ * Font selection widgets
+ *
+ * Authors:
+ * Chris Lahey <clahey@ximian.com>
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 1999-2001 Ximian, Inc.
+ * Copyright (C) 2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib.h>
+
+struct SPFontSelector;
+struct SPFontPreview;
+
+#define SP_TYPE_FONT_SELECTOR (sp_font_selector_get_type ())
+#define SP_FONT_SELECTOR(o) (GTK_CHECK_CAST ((o), SP_TYPE_FONT_SELECTOR, SPFontSelector))
+#define SP_IS_FONT_SELECTOR(o) (GTK_CHECK_TYPE ((o), SP_TYPE_FONT_SELECTOR))
+
+#define SP_TYPE_FONT_PREVIEW (sp_font_preview_get_type ())
+#define SP_FONT_PREVIEW(o) (GTK_CHECK_CAST ((o), SP_TYPE_FONT_PREVIEW, SPFontPreview))
+#define SP_IS_FONT_PREVIEW(o) (GTK_CHECK_TYPE ((o), SP_TYPE_FONT_PREVIEW))
+
+#include <libnrtype/nrtype-forward.h>
+#include <gtk/gtkwidget.h>
+
+/* SPFontSelector */
+
+GtkType sp_font_selector_get_type (void);
+
+GtkWidget *sp_font_selector_new (void);
+
+void sp_font_selector_set_font (SPFontSelector *fsel, font_instance *font, double size);
+
+font_instance *sp_font_selector_get_font (SPFontSelector *fsel);
+double sp_font_selector_get_size (SPFontSelector *fsel);
+
+/* SPFontPreview */
+
+GtkType sp_font_preview_get_type (void);
+
+GtkWidget *sp_font_preview_new (void);
+
+void sp_font_preview_set_font (SPFontPreview *fprev, font_instance *font, SPFontSelector *fsel);
+void sp_font_preview_set_rgba32 (SPFontPreview *fprev, guint32 rgba);
+void sp_font_preview_set_phrase (SPFontPreview *fprev, const gchar *phrase);
+
+
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/gradient-image.cpp b/src/widgets/gradient-image.cpp
new file mode 100644
index 000000000..38300426d
--- /dev/null
+++ b/src/widgets/gradient-image.cpp
@@ -0,0 +1,277 @@
+#define __SP_GRADIENT_IMAGE_C__
+
+/*
+ * A simple gradient preview
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <libnr/nr-pixblock-pattern.h>
+#include "macros.h"
+#include "../display/nr-plain-stuff.h"
+#include "../display/nr-plain-stuff-gdk.h"
+#include "gradient-image.h"
+#include "sp-gradient-fns.h"
+
+#define VBLOCK 16
+
+static void sp_gradient_image_class_init (SPGradientImageClass *klass);
+static void sp_gradient_image_init (SPGradientImage *image);
+static void sp_gradient_image_destroy (GtkObject *object);
+
+static void sp_gradient_image_realize (GtkWidget *widget);
+static void sp_gradient_image_unrealize (GtkWidget *widget);
+static void sp_gradient_image_size_request (GtkWidget *widget, GtkRequisition *requisition);
+static void sp_gradient_image_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
+static gint sp_gradient_image_expose (GtkWidget *widget, GdkEventExpose *event);
+
+static void sp_gradient_image_gradient_release (SPGradient *gr, SPGradientImage *im);
+static void sp_gradient_image_gradient_modified (SPGradient *gr, guint flags, SPGradientImage *im);
+static void sp_gradient_image_update (SPGradientImage *img);
+
+static GtkWidgetClass *parent_class;
+
+GtkType
+sp_gradient_image_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GtkTypeInfo info = {
+ "SPGradientImage",
+ sizeof (SPGradientImage),
+ sizeof (SPGradientImageClass),
+ (GtkClassInitFunc) sp_gradient_image_class_init,
+ (GtkObjectInitFunc) sp_gradient_image_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_WIDGET, &info);
+ }
+ return type;
+}
+
+static void
+sp_gradient_image_class_init (SPGradientImageClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ parent_class = (GtkWidgetClass*)gtk_type_class (GTK_TYPE_WIDGET);
+
+ object_class->destroy = sp_gradient_image_destroy;
+
+ widget_class->realize = sp_gradient_image_realize;
+ widget_class->unrealize = sp_gradient_image_unrealize;
+ widget_class->size_request = sp_gradient_image_size_request;
+ widget_class->size_allocate = sp_gradient_image_size_allocate;
+ widget_class->expose_event = sp_gradient_image_expose;
+}
+
+static void
+sp_gradient_image_init (SPGradientImage *image)
+{
+ GTK_WIDGET_SET_FLAGS (image, GTK_NO_WINDOW);
+
+ image->gradient = NULL;
+ image->px = NULL;
+}
+
+static void
+sp_gradient_image_destroy (GtkObject *object)
+{
+ SPGradientImage *image;
+
+ image = SP_GRADIENT_IMAGE (object);
+
+ if (image->gradient) {
+ sp_signal_disconnect_by_data (image->gradient, image);
+ image->gradient = NULL;
+ }
+
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+static void
+sp_gradient_image_realize (GtkWidget *widget)
+{
+ SPGradientImage *image;
+
+ image = SP_GRADIENT_IMAGE (widget);
+
+ if (((GtkWidgetClass *) parent_class)->realize)
+ (* ((GtkWidgetClass *) parent_class)->realize) (widget);
+
+ g_assert (!image->px);
+ image->px = g_new (guchar, 3 * VBLOCK * widget->allocation.width);
+ sp_gradient_image_update (image);
+}
+
+static void
+sp_gradient_image_unrealize (GtkWidget *widget)
+{
+ SPGradientImage *image;
+
+ image = SP_GRADIENT_IMAGE (widget);
+
+ if (((GtkWidgetClass *) parent_class)->unrealize)
+ (* ((GtkWidgetClass *) parent_class)->unrealize) (widget);
+
+ g_assert (image->px);
+ g_free (image->px);
+ image->px = NULL;
+}
+
+static void
+sp_gradient_image_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+ SPGradientImage *slider;
+
+ slider = SP_GRADIENT_IMAGE (widget);
+
+ requisition->width = 64;
+ requisition->height = 16;
+}
+
+static void
+sp_gradient_image_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ SPGradientImage *image;
+
+ image = SP_GRADIENT_IMAGE (widget);
+
+ widget->allocation = *allocation;
+
+ if (GTK_WIDGET_REALIZED (widget)) {
+ g_free (image->px);
+ image->px = g_new (guchar, 3 * VBLOCK * allocation->width);
+ }
+
+ sp_gradient_image_update (image);
+}
+
+static gint
+sp_gradient_image_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+ SPGradientImage *image;
+
+ image = SP_GRADIENT_IMAGE (widget);
+
+ if (GTK_WIDGET_DRAWABLE (widget)) {
+ gint x0, y0, x1, y1;
+ x0 = MAX (event->area.x, widget->allocation.x);
+ y0 = MAX (event->area.y, widget->allocation.y);
+ x1 = MIN (event->area.x + event->area.width, widget->allocation.x + widget->allocation.width);
+ y1 = MIN (event->area.y + event->area.height, widget->allocation.y + widget->allocation.height);
+ if ((x1 > x0) && (y1 > y0)) {
+ if (image->px) {
+ if (image->gradient) {
+ gint y;
+ guchar *p;
+ p = image->px + 3 * (x0 - widget->allocation.x);
+ for (y = y0; y < y1; y += VBLOCK) {
+ gdk_draw_rgb_image (widget->window, widget->style->black_gc,
+ x0, y,
+ (x1 - x0), MIN (VBLOCK, y1 - y),
+ GDK_RGB_DITHER_MAX,
+ p, widget->allocation.width * 3);
+ }
+ } else {
+ nr_gdk_draw_gray_garbage (widget->window, widget->style->black_gc,
+ x0, y0,
+ x1 - x0, y1 - y0);
+ }
+ } else {
+ gdk_draw_rectangle (widget->window, widget->style->black_gc,
+ x0, y0,
+ (x1 - x0), (y1 - x0),
+ TRUE);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+GtkWidget *
+sp_gradient_image_new (SPGradient *gradient)
+{
+ SPGradientImage *image;
+
+ image = (SPGradientImage*)gtk_type_new (SP_TYPE_GRADIENT_IMAGE);
+
+ sp_gradient_image_set_gradient (image, gradient);
+
+ return (GtkWidget *) image;
+}
+
+void
+sp_gradient_image_set_gradient (SPGradientImage *image, SPGradient *gradient)
+{
+ if (image->gradient) {
+ sp_signal_disconnect_by_data (image->gradient, image);
+ }
+
+ image->gradient = gradient;
+
+ if (gradient) {
+ g_signal_connect (G_OBJECT (gradient), "release", G_CALLBACK (sp_gradient_image_gradient_release), image);
+ g_signal_connect (G_OBJECT (gradient), "modified", G_CALLBACK (sp_gradient_image_gradient_modified), image);
+ }
+
+ sp_gradient_image_update (image);
+}
+
+static void
+sp_gradient_image_gradient_release (SPGradient *gradient, SPGradientImage *image)
+{
+ if (image->gradient) {
+ sp_signal_disconnect_by_data (image->gradient, image);
+ }
+
+ image->gradient = NULL;
+
+ sp_gradient_image_update (image);
+}
+
+static void
+sp_gradient_image_gradient_modified (SPGradient *gradient, guint flags, SPGradientImage *image)
+{
+ sp_gradient_image_update (image);
+}
+
+static void
+sp_gradient_image_update (SPGradientImage *image)
+{
+ GtkAllocation *allocation;
+
+ if (!image->px) return;
+
+ allocation = &((GtkWidget *) image)->allocation;
+
+ if (image->gradient) {
+ nr_render_checkerboard_rgb (image->px, allocation->width, VBLOCK, 3 * allocation->width, 0, 0);
+ sp_gradient_render_vector_block_rgb (image->gradient,
+ image->px, allocation->width, VBLOCK, 3 * allocation->width,
+ 0, allocation->width, TRUE);
+ } else {
+ NRPixBlock pb;
+ nr_pixblock_setup_extern (&pb, NR_PIXBLOCK_MODE_R8G8B8,
+ 0, 0, allocation->width, VBLOCK,
+ image->px, 3 * allocation->width, TRUE, FALSE);
+ nr_pixblock_render_gray_noise (&pb, NULL);
+ nr_pixblock_release (&pb);
+ }
+
+ if (GTK_WIDGET_DRAWABLE (image)) {
+ gtk_widget_queue_draw (GTK_WIDGET (image));
+ }
+}
diff --git a/src/widgets/gradient-image.h b/src/widgets/gradient-image.h
new file mode 100644
index 000000000..0e0241ac7
--- /dev/null
+++ b/src/widgets/gradient-image.h
@@ -0,0 +1,46 @@
+#ifndef __SP_GRADIENT_IMAGE_H__
+#define __SP_GRADIENT_IMAGE_H__
+
+/*
+ * A simple gradient preview
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <gtk/gtkwidget.h>
+#include "../libnr/nr-matrix.h"
+class SPGradient;
+
+#include <glib.h>
+
+
+#define SP_TYPE_GRADIENT_IMAGE (sp_gradient_image_get_type ())
+#define SP_GRADIENT_IMAGE(o) (GTK_CHECK_CAST ((o), SP_TYPE_GRADIENT_IMAGE, SPGradientImage))
+#define SP_GRADIENT_IMAGE_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_GRADIENT_IMAGE, SPGradientImageClass))
+#define SP_IS_GRADIENT_IMAGE(o) (GTK_CHECK_TYPE ((o), SP_TYPE_GRADIENT_IMAGE))
+#define SP_IS_GRADIENT_IMAGE_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_GRADIENT_IMAGE))
+
+struct SPGradientImage {
+ GtkWidget widget;
+ SPGradient *gradient;
+ guchar *px;
+};
+
+struct SPGradientImageClass {
+ GtkWidgetClass parent_class;
+};
+
+GtkType sp_gradient_image_get_type (void);
+
+GtkWidget *sp_gradient_image_new (SPGradient *gradient);
+void sp_gradient_image_set_gradient (SPGradientImage *gi, SPGradient *gr);
+
+
+
+#endif
diff --git a/src/widgets/gradient-selector.cpp b/src/widgets/gradient-selector.cpp
new file mode 100644
index 000000000..5f09d6704
--- /dev/null
+++ b/src/widgets/gradient-selector.cpp
@@ -0,0 +1,342 @@
+#define __SP_GRADIENT_SELECTOR_C__
+
+/*
+ * Gradient vector widget
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <gtk/gtkhbox.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkoptionmenu.h>
+#include <gtk/gtkmenuitem.h>
+#include <gtk/gtktooltips.h>
+
+#include "../document-private.h"
+#include "../gradient-chemistry.h"
+
+#include <glibmm/i18n.h>
+#include <xml/repr.h>
+
+#include "gradient-vector.h"
+
+#include "gradient-selector.h"
+
+enum {
+ GRABBED,
+ DRAGGED,
+ RELEASED,
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static void sp_gradient_selector_class_init (SPGradientSelectorClass *klass);
+static void sp_gradient_selector_init (SPGradientSelector *selector);
+static void sp_gradient_selector_destroy (GtkObject *object);
+
+/* Signal handlers */
+static void sp_gradient_selector_vector_set (SPGradientVectorSelector *gvs, SPGradient *gr, SPGradientSelector *sel);
+static void sp_gradient_selector_edit_vector_clicked (GtkWidget *w, SPGradientSelector *sel);
+static void sp_gradient_selector_add_vector_clicked (GtkWidget *w, SPGradientSelector *sel);
+
+static void sp_gradient_selector_spread_activate (GtkWidget *widget, SPGradientSelector *sel);
+
+static GtkVBoxClass *parent_class;
+static guint signals[LAST_SIGNAL] = {0};
+
+GtkType
+sp_gradient_selector_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GtkTypeInfo info = {
+ "SPGradientSelector",
+ sizeof (SPGradientSelector),
+ sizeof (SPGradientSelectorClass),
+ (GtkClassInitFunc) sp_gradient_selector_class_init,
+ (GtkObjectInitFunc) sp_gradient_selector_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_VBOX, &info);
+ }
+ return type;
+}
+
+static void
+sp_gradient_selector_class_init (SPGradientSelectorClass *klass)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass *) klass;
+
+ parent_class = (GtkVBoxClass*)gtk_type_class (GTK_TYPE_VBOX);
+
+ signals[GRABBED] = gtk_signal_new ("grabbed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPGradientSelectorClass, grabbed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ signals[DRAGGED] = gtk_signal_new ("dragged",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPGradientSelectorClass, dragged),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ signals[RELEASED] = gtk_signal_new ("released",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPGradientSelectorClass, released),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ signals[CHANGED] = gtk_signal_new ("changed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPGradientSelectorClass, changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ object_class->destroy = sp_gradient_selector_destroy;
+}
+
+static void
+sp_gradient_selector_init (SPGradientSelector *sel)
+{
+ GtkWidget *hb, *l, *m, *mi;
+
+ sel->mode = SP_GRADIENT_SELECTOR_MODE_LINEAR;
+
+ sel->gradientUnits = SP_GRADIENT_UNITS_USERSPACEONUSE;
+ sel->gradientSpread = SP_GRADIENT_SPREAD_PAD;
+
+ /* Vectors */
+ sel->vectors = sp_gradient_vector_selector_new (NULL, NULL);
+ gtk_widget_show (sel->vectors);
+ gtk_box_pack_start (GTK_BOX (sel), sel->vectors, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (sel->vectors), "vector_set", G_CALLBACK (sp_gradient_selector_vector_set), sel);
+
+ /* Create box for buttons */
+ hb = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (sel), hb, FALSE, FALSE, 0);
+ GtkTooltips *ttips = gtk_tooltips_new ();
+
+ sel->add = gtk_button_new_with_label (_("Duplicate"));
+ gtk_box_pack_start (GTK_BOX (hb), sel->add, TRUE, TRUE, 0);
+ g_signal_connect (G_OBJECT (sel->add), "clicked", G_CALLBACK (sp_gradient_selector_add_vector_clicked), sel);
+ gtk_widget_set_sensitive (sel->add, FALSE);
+
+ sel->edit = gtk_button_new_with_label (_("Edit..."));
+ gtk_box_pack_start (GTK_BOX (hb), sel->edit, TRUE, TRUE, 0);
+ g_signal_connect (G_OBJECT (sel->edit), "clicked", G_CALLBACK (sp_gradient_selector_edit_vector_clicked), sel);
+ gtk_widget_set_sensitive (sel->edit, FALSE);
+
+ gtk_widget_show_all (hb);
+
+ /* Spread selector */
+ hb = gtk_hbox_new (FALSE, 0);
+ gtk_widget_show (hb);
+ gtk_box_pack_start (GTK_BOX (sel), hb, FALSE, FALSE, 0);
+
+ sel->spread = gtk_option_menu_new ();
+ gtk_widget_show (sel->spread);
+ gtk_box_pack_end (GTK_BOX (hb), sel->spread, FALSE, FALSE, 0);
+ gtk_tooltips_set_tip (ttips, sel->spread,
+ // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/pservers.html#LinearGradientSpreadMethodAttribute
+ _("Whether to fill with flat color beyond the ends of the gradient vector "
+ "(spreadMethod=\"pad\"), or repeat the gradient in the same direction "
+ "(spreadMethod=\"repeat\"), or repeat the gradient in alternating opposite "
+ "directions (spreadMethod=\"reflect\")"), NULL);
+
+ m = gtk_menu_new ();
+ mi = gtk_menu_item_new_with_label (_("none"));
+ gtk_menu_append (GTK_MENU (m), mi);
+ g_object_set_data (G_OBJECT (mi), "gradientSpread", GUINT_TO_POINTER (SP_GRADIENT_SPREAD_PAD));
+ g_signal_connect (G_OBJECT (mi), "activate", G_CALLBACK (sp_gradient_selector_spread_activate), sel);
+ mi = gtk_menu_item_new_with_label (_("reflected"));
+ g_object_set_data (G_OBJECT (mi), "gradientSpread", GUINT_TO_POINTER (SP_GRADIENT_SPREAD_REFLECT));
+ g_signal_connect (G_OBJECT (mi), "activate", G_CALLBACK (sp_gradient_selector_spread_activate), sel);
+ gtk_menu_append (GTK_MENU (m), mi);
+ mi = gtk_menu_item_new_with_label (_("direct"));
+ g_object_set_data (G_OBJECT (mi), "gradientSpread", GUINT_TO_POINTER (SP_GRADIENT_SPREAD_REPEAT));
+ g_signal_connect (G_OBJECT (mi), "activate", G_CALLBACK (sp_gradient_selector_spread_activate), sel);
+ gtk_menu_append (GTK_MENU (m), mi);
+ gtk_widget_show_all (m);
+
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (sel->spread), m);
+
+ l = gtk_label_new (_("Repeat:"));
+ gtk_widget_show (l);
+ gtk_box_pack_end (GTK_BOX (hb), l, FALSE, FALSE, 4);
+}
+
+static void
+sp_gradient_selector_destroy (GtkObject *object)
+{
+ SPGradientSelector *sel;
+
+ sel = SP_GRADIENT_SELECTOR (object);
+
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+GtkWidget *
+sp_gradient_selector_new (void)
+{
+ SPGradientSelector *sel;
+
+ sel = (SPGradientSelector*)gtk_type_new (SP_TYPE_GRADIENT_SELECTOR);
+
+ return (GtkWidget *) sel;
+}
+
+void
+sp_gradient_selector_set_mode (SPGradientSelector *sel, guint mode)
+{
+ g_return_if_fail (sel != NULL);
+ g_return_if_fail (SP_IS_GRADIENT_SELECTOR (sel));
+
+ sel->mode = mode;
+}
+
+void
+sp_gradient_selector_set_units (SPGradientSelector *sel, guint units)
+{
+ g_return_if_fail (sel != NULL);
+ g_return_if_fail (SP_IS_GRADIENT_SELECTOR (sel));
+
+ sel->gradientUnits = (SPGradientUnits)units;
+}
+
+void
+sp_gradient_selector_set_spread (SPGradientSelector *sel, guint spread)
+{
+ g_return_if_fail (sel != NULL);
+ g_return_if_fail (SP_IS_GRADIENT_SELECTOR (sel));
+
+ sel->gradientSpread = (SPGradientSpread)spread;
+
+ gtk_option_menu_set_history (GTK_OPTION_MENU (sel->spread), sel->gradientSpread);
+}
+
+SPGradientUnits
+sp_gradient_selector_get_units (SPGradientSelector *sel)
+{
+ return (SPGradientUnits) sel->gradientUnits;
+}
+
+SPGradientSpread
+sp_gradient_selector_get_spread (SPGradientSelector *sel)
+{
+ return (SPGradientSpread) sel->gradientSpread;
+}
+
+void
+sp_gradient_selector_set_vector (SPGradientSelector *sel, SPDocument *doc, SPGradient *vector)
+{
+ g_return_if_fail (sel != NULL);
+ g_return_if_fail (SP_IS_GRADIENT_SELECTOR (sel));
+ g_return_if_fail (!vector || SP_IS_GRADIENT (vector));
+ g_return_if_fail (!vector || (SP_OBJECT_DOCUMENT (vector) == doc));
+
+ if (vector && !SP_GRADIENT_HAS_STOPS (vector))
+ return;
+
+ sp_gradient_vector_selector_set_gradient (SP_GRADIENT_VECTOR_SELECTOR (sel->vectors), doc, vector);
+
+ if (vector) {
+ gtk_widget_set_sensitive (sel->edit, TRUE);
+ gtk_widget_set_sensitive (sel->add, TRUE);
+ } else {
+ gtk_widget_set_sensitive (sel->edit, FALSE);
+ gtk_widget_set_sensitive (sel->add, (doc != NULL));
+ }
+}
+
+SPGradient *
+sp_gradient_selector_get_vector (SPGradientSelector *sel)
+{
+ if (sel == NULL || !SP_IS_GRADIENT_SELECTOR (sel))
+ return NULL;
+
+ /* fixme: */
+ return SP_GRADIENT_VECTOR_SELECTOR (sel->vectors)->gr;
+}
+
+static void
+sp_gradient_selector_vector_set (SPGradientVectorSelector *gvs, SPGradient *gr, SPGradientSelector *sel)
+{
+ static gboolean blocked = FALSE;
+
+ if (!blocked) {
+ blocked = TRUE;
+ gr = sp_gradient_ensure_vector_normalized (gr);
+ sp_gradient_selector_set_vector (sel, (gr) ? SP_OBJECT_DOCUMENT (gr) : NULL, gr);
+ g_signal_emit (G_OBJECT (sel), signals[CHANGED], 0, gr);
+ blocked = FALSE;
+ }
+}
+
+static void
+sp_gradient_selector_edit_vector_clicked (GtkWidget *w, SPGradientSelector *sel)
+{
+ GtkWidget *dialog;
+
+ /* fixme: */
+ dialog = sp_gradient_vector_editor_new (SP_GRADIENT_VECTOR_SELECTOR (sel->vectors)->gr);
+
+ gtk_widget_show (dialog);
+}
+
+static void
+sp_gradient_selector_add_vector_clicked (GtkWidget *w, SPGradientSelector *sel)
+{
+ SPDocument *doc;
+ SPGradient *gr;
+ Inkscape::XML::Node *repr;
+
+ doc = sp_gradient_vector_selector_get_document (SP_GRADIENT_VECTOR_SELECTOR (sel->vectors));
+ if (!doc) return;
+ gr = sp_gradient_vector_selector_get_gradient (SP_GRADIENT_VECTOR_SELECTOR (sel->vectors));
+
+ if (gr) {
+ repr = SP_OBJECT_REPR (gr)->duplicate();
+ } else {
+ Inkscape::XML::Node *stop;
+ repr = sp_repr_new ("svg:linearGradient");
+ stop = sp_repr_new ("svg:stop");
+ stop->setAttribute("offset", "0");
+ stop->setAttribute("style", "stop-color:#000;stop-opacity:1;");
+ repr->appendChild(stop);
+ Inkscape::GC::release(stop);
+ stop = sp_repr_new ("svg:stop");
+ stop->setAttribute("offset", "1");
+ stop->setAttribute("style", "stop-color:#fff;stop-opacity:1;");
+ repr->appendChild(stop);
+ Inkscape::GC::release(stop);
+ }
+
+ SP_OBJECT_REPR (SP_DOCUMENT_DEFS (doc))->addChild(repr, NULL);
+ Inkscape::GC::release(repr);
+
+ gr = (SPGradient *) doc->getObjectByRepr(repr);
+ sp_gradient_vector_selector_set_gradient (SP_GRADIENT_VECTOR_SELECTOR (sel->vectors), doc, gr);
+}
+
+static void
+sp_gradient_selector_spread_activate (GtkWidget *widget, SPGradientSelector *sel)
+{
+ sel->gradientSpread = (SPGradientSpread)GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "gradientSpread"));
+
+ g_signal_emit (G_OBJECT (sel), signals[CHANGED], 0);
+}
diff --git a/src/widgets/gradient-selector.h b/src/widgets/gradient-selector.h
new file mode 100644
index 000000000..6e3204422
--- /dev/null
+++ b/src/widgets/gradient-selector.h
@@ -0,0 +1,79 @@
+#ifndef __SP_GRADIENT_SELECTOR_H__
+#define __SP_GRADIENT_SELECTOR_H__
+
+/*
+ * Gradient vector and position widget
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib.h>
+#include "sp-gradient-spread.h"
+#include "sp-gradient-units.h"
+class SPGradient;
+
+#define SP_TYPE_GRADIENT_SELECTOR (sp_gradient_selector_get_type ())
+#define SP_GRADIENT_SELECTOR(o) (GTK_CHECK_CAST ((o), SP_TYPE_GRADIENT_SELECTOR, SPGradientSelector))
+#define SP_GRADIENT_SELECTOR_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_GRADIENT_SELECTOR, SPGradientSelectorClass))
+#define SP_IS_GRADIENT_SELECTOR(o) (GTK_CHECK_TYPE ((o), SP_TYPE_GRADIENT_SELECTOR))
+#define SP_IS_GRADIENT_SELECTOR_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_GRADIENT_SELECTOR))
+
+#include <libnr/nr-forward.h>
+#include <gtk/gtkvbox.h>
+#include "../forward.h"
+#include <sp-gradient.h>
+
+enum {
+ SP_GRADIENT_SELECTOR_MODE_LINEAR,
+ SP_GRADIENT_SELECTOR_MODE_RADIAL
+};
+
+struct SPGradientSelector {
+ GtkVBox vbox;
+
+ guint mode : 1;
+
+ SPGradientUnits gradientUnits : 1;
+ SPGradientSpread gradientSpread : 2;
+
+ /* Vector selector */
+ GtkWidget *vectors;
+ /* Editing buttons */
+ GtkWidget *edit, *add;
+ /* Position widget */
+ GtkWidget *position;
+ /* Spread selector */
+ GtkWidget *spread;
+};
+
+struct SPGradientSelectorClass {
+ GtkVBoxClass parent_class;
+
+ void (* grabbed) (SPGradientSelector *sel);
+ void (* dragged) (SPGradientSelector *sel);
+ void (* released) (SPGradientSelector *sel);
+ void (* changed) (SPGradientSelector *sel);
+};
+
+GtkType sp_gradient_selector_get_type (void);
+
+GtkWidget *sp_gradient_selector_new (void);
+
+void sp_gradient_selector_set_mode (SPGradientSelector *sel, guint mode);
+void sp_gradient_selector_set_units (SPGradientSelector *sel, guint units);
+void sp_gradient_selector_set_spread (SPGradientSelector *sel, guint spread);
+void sp_gradient_selector_set_vector (SPGradientSelector *sel, SPDocument *doc, SPGradient *vector);
+void sp_gradient_selector_set_bbox (SPGradientSelector *sel, gdouble x0, gdouble y0, gdouble x1, gdouble y1);
+
+SPGradientUnits sp_gradient_selector_get_units (SPGradientSelector *sel);
+SPGradientSpread sp_gradient_selector_get_spread (SPGradientSelector *sel);
+
+SPGradient *sp_gradient_selector_get_vector (SPGradientSelector *sel);
+
+#endif
diff --git a/src/widgets/gradient-toolbar.cpp b/src/widgets/gradient-toolbar.cpp
new file mode 100644
index 000000000..53e194989
--- /dev/null
+++ b/src/widgets/gradient-toolbar.cpp
@@ -0,0 +1,665 @@
+/*
+ * Gradient aux toolbar
+ *
+ * Authors:
+ * bulia byak <bulia@dr.com>
+ *
+ * Copyright (C) 2005 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "macros.h"
+#include "widgets/button.h"
+#include "widgets/widget-sizes.h"
+#include "widgets/spw-utilities.h"
+#include "widgets/spinbutton-events.h"
+#include "widgets/gradient-vector.h"
+#include "widgets/gradient-image.h"
+#include "style.h"
+
+#include "prefs-utils.h"
+#include "document-private.h"
+#include "desktop.h"
+#include "desktop-handles.h"
+#include <glibmm/i18n.h>
+
+#include "gradient-context.h"
+#include "gradient-drag.h"
+#include "sp-linear-gradient.h"
+#include "sp-radial-gradient.h"
+#include "gradient-chemistry.h"
+#include "selection.h"
+
+#include "toolbox.h"
+
+
+//########################
+//## Gradient ##
+//########################
+
+static void gr_toggle_type (GtkWidget *button, gpointer data) {
+ GtkWidget *linear = (GtkWidget *) g_object_get_data (G_OBJECT(data), "linear");
+ GtkWidget *radial = (GtkWidget *) g_object_get_data (G_OBJECT(data), "radial");
+ if (button == linear && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (linear))) {
+ prefs_set_int_attribute ("tools.gradient", "newgradient", SP_GRADIENT_TYPE_LINEAR);
+ if (radial) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radial), FALSE);
+ } else if (button == radial && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radial))) {
+ prefs_set_int_attribute ("tools.gradient", "newgradient", SP_GRADIENT_TYPE_RADIAL);
+ if (linear) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (linear), FALSE);
+ }
+}
+
+static void gr_toggle_fillstroke (GtkWidget *button, gpointer data) {
+ GtkWidget *fill = (GtkWidget *) g_object_get_data (G_OBJECT(data), "fill");
+ GtkWidget *stroke = (GtkWidget *) g_object_get_data (G_OBJECT(data), "stroke");
+ if (button == fill && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fill))) {
+ prefs_set_int_attribute ("tools.gradient", "newfillorstroke", 1);
+ if (stroke) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (stroke), FALSE);
+ } else if (button == stroke && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (stroke))) {
+ prefs_set_int_attribute ("tools.gradient", "newfillorstroke", 0);
+ if (fill) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fill), FALSE);
+ }
+}
+
+void
+gr_apply_gradient_to_item (SPItem *item, SPGradient *gr, SPGradientType new_type, guint new_fill, bool do_fill, bool do_stroke)
+{
+ SPStyle *style = SP_OBJECT_STYLE (item);
+
+ if (do_fill) {
+ if (style && (style->fill.type == SP_PAINT_TYPE_PAINTSERVER) &&
+ SP_IS_GRADIENT (SP_OBJECT_STYLE_FILL_SERVER (item))) {
+ SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item);
+ if (SP_IS_LINEARGRADIENT (server)) {
+ sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_LINEAR, true);
+ } else if (SP_IS_RADIALGRADIENT (server)) {
+ sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_RADIAL, true);
+ }
+ } else if (new_fill) {
+ sp_item_set_gradient(item, gr, new_type, true);
+ }
+ }
+
+ if (do_stroke) {
+ if (style && (style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) &&
+ SP_IS_GRADIENT (SP_OBJECT_STYLE_STROKE_SERVER (item))) {
+ SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item);
+ if (SP_IS_LINEARGRADIENT (server)) {
+ sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_LINEAR, false);
+ } else if (SP_IS_RADIALGRADIENT (server)) {
+ sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_RADIAL, false);
+ }
+ } else if (!new_fill) {
+ sp_item_set_gradient(item, gr, new_type, false);
+ }
+ }
+}
+
+/**
+Applies gradient vector gr to the gradients attached to the selected dragger of drag, or if none,
+to all objects in selection. If there was no previous gradient on an item, uses gradient type and
+fill/stroke setting from preferences to create new default (linear: left/right; radial: centered)
+gradient.
+*/
+void
+gr_apply_gradient (Inkscape::Selection *selection, GrDrag *drag, SPGradient *gr)
+{
+ SPGradientType new_type = (SPGradientType) prefs_get_int_attribute ("tools.gradient", "newgradient", SP_GRADIENT_TYPE_LINEAR);
+ guint new_fill = prefs_get_int_attribute ("tools.gradient", "newfillorstroke", 1);
+
+ // First try selected dragger
+ if (drag && drag->selected) {
+ GrDragger *dragger = drag->selected;
+ for (GSList const* i = dragger->draggables; i != NULL; i = i->next) { // for all draggables of dragger
+ GrDraggable *draggable = (GrDraggable *) i->data;
+ gr_apply_gradient_to_item (draggable->item, gr, new_type, new_fill, draggable->fill_or_stroke, !draggable->fill_or_stroke);
+ }
+ return;
+ }
+
+ // If no drag or no dragger selected, act on selection
+ for (GSList const* i = selection->itemList(); i != NULL; i = i->next) {
+ gr_apply_gradient_to_item (SP_ITEM(i->data), gr, new_type, new_fill, true, true);
+ }
+}
+
+void
+gr_item_activate (GtkMenuItem *menuitem, gpointer data)
+{
+ SPGradient *gr = (SPGradient *) g_object_get_data (G_OBJECT (menuitem), "gradient");
+ gr = sp_gradient_ensure_vector_normalized(gr);
+
+ SPDesktop *desktop = (SPDesktop *) data;
+ Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
+ SPEventContext *ev = SP_DT_EVENTCONTEXT (desktop);
+
+ gr_apply_gradient (selection, ev? ev->get_drag() : NULL, gr);
+
+ sp_document_done (SP_DT_DOCUMENT (desktop));
+}
+
+gchar *
+gr_prepare_label (SPObject *obj)
+{
+ const gchar *id = obj->defaultLabel();
+ if (strlen(id) > 15 && (!strncmp (id, "#linearGradient", 15) || !strncmp (id, "#radialGradient", 15)))
+ return g_strdup_printf ("<small>#%s</small>", id+15);
+ return g_strdup_printf ("<small>%s</small>", id);
+}
+
+GtkWidget *
+gr_vector_list (SPDesktop *desktop, bool selection_empty, SPGradient *gr_selected, bool gr_multi)
+{
+ SPDocument *document = SP_DT_DOCUMENT (desktop);
+
+ GtkWidget *om = gtk_option_menu_new ();
+ GtkWidget *m = gtk_menu_new ();
+
+ GSList *gl = NULL;
+ const GSList *gradients = sp_document_get_resource_list (document, "gradient");
+ for (const GSList *i = gradients; i != NULL; i = i->next) {
+ if (SP_GRADIENT_HAS_STOPS (i->data)) {
+ gl = g_slist_prepend (gl, i->data);
+ }
+ }
+ gl = g_slist_reverse (gl);
+
+ guint pos = 0;
+ guint idx = 0;
+
+ if (!gl) {
+ GtkWidget *l = gtk_label_new("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>No gradients</small>"));
+ GtkWidget *i = gtk_menu_item_new ();
+ gtk_container_add (GTK_CONTAINER (i), l);
+
+ gtk_widget_show (i);
+ gtk_menu_append (GTK_MENU (m), i);
+ gtk_widget_set_sensitive (om, FALSE);
+ } else if (selection_empty) {
+ GtkWidget *l = gtk_label_new("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Nothing selected</small>"));
+ GtkWidget *i = gtk_menu_item_new ();
+ gtk_container_add (GTK_CONTAINER (i), l);
+
+ gtk_widget_show (i);
+ gtk_menu_append (GTK_MENU (m), i);
+ gtk_widget_set_sensitive (om, FALSE);
+ } else {
+
+ if (gr_selected == NULL) {
+ GtkWidget *l = gtk_label_new("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>No gradients in selection</small>"));
+ GtkWidget *i = gtk_menu_item_new ();
+ gtk_container_add (GTK_CONTAINER (i), l);
+
+ gtk_widget_show (i);
+ gtk_menu_append (GTK_MENU (m), i);
+ }
+
+ if (gr_multi) {
+ GtkWidget *l = gtk_label_new("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Multiple gradients</small>"));
+ GtkWidget *i = gtk_menu_item_new ();
+ gtk_container_add (GTK_CONTAINER (i), l);
+
+ gtk_widget_show (i);
+ gtk_menu_append (GTK_MENU (m), i);
+ }
+
+ while (gl) {
+ SPGradient *gradient = SP_GRADIENT (gl->data);
+ gl = g_slist_remove (gl, gradient);
+
+ GtkWidget *i = gtk_menu_item_new ();
+ g_object_set_data (G_OBJECT (i), "gradient", gradient);
+ g_signal_connect (G_OBJECT (i), "activate", G_CALLBACK (gr_item_activate), desktop);
+
+ GtkWidget *image = sp_gradient_image_new (gradient);
+
+ GtkWidget *hb = gtk_hbox_new (FALSE, 4);
+ GtkWidget *l = gtk_label_new ("");
+ gchar *label = gr_prepare_label (SP_OBJECT(gradient));
+ gtk_label_set_markup (GTK_LABEL(l), label);
+ g_free (label);
+ gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hb), image, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (i);
+
+ gtk_container_add (GTK_CONTAINER (i), hb);
+
+ gtk_menu_append (GTK_MENU (m), i);
+
+ if (gradient == gr_selected) {
+ pos = idx;
+ }
+ idx ++;
+ }
+ gtk_widget_set_sensitive (om, TRUE);
+ }
+
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (om), m);
+ /* Select the current gradient, or the Multi/Nothing line */
+ if (gr_multi || gr_selected == NULL)
+ gtk_option_menu_set_history (GTK_OPTION_MENU (om), 0);
+ else
+ gtk_option_menu_set_history (GTK_OPTION_MENU (om), pos);
+
+ return om;
+}
+
+
+void
+gr_read_selection (Inkscape::Selection *selection, GrDrag *drag, SPGradient **gr_selected, bool *gr_multi, SPGradientSpread *spr_selected, bool *spr_multi)
+{
+ if (drag && drag->selected) {
+ GrDragger *dragger = drag->selected;
+ for (GSList const* i = dragger->draggables; i != NULL; i = i->next) { // for all draggables of dragger
+ GrDraggable *draggable = (GrDraggable *) i->data;
+ SPGradient *gradient = sp_item_gradient_get_vector (draggable->item, draggable->fill_or_stroke);
+ SPGradientSpread spread = sp_item_gradient_get_spread (draggable->item, draggable->fill_or_stroke);
+
+ if (gradient != *gr_selected) {
+ if (*gr_selected != NULL) {
+ *gr_multi = true;
+ } else {
+ *gr_selected = gradient;
+ }
+ }
+ if (spread != *spr_selected) {
+ if (*spr_selected != INT_MAX) {
+ *spr_multi = true;
+ } else {
+ *spr_selected = spread;
+ }
+ }
+ }
+ return;
+ }
+
+ // If no selected dragger, read desktop selection
+ for (GSList const* i = selection->itemList(); i != NULL; i = i->next) {
+ SPItem *item = SP_ITEM(i->data);
+ SPStyle *style = SP_OBJECT_STYLE (item);
+
+ if (style && (style->fill.type == SP_PAINT_TYPE_PAINTSERVER)) {
+ SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item);
+ if (SP_IS_GRADIENT (server)) {
+ SPGradient *gradient = sp_gradient_get_vector (SP_GRADIENT (server), false);
+ SPGradientSpread spread = sp_gradient_get_spread (SP_GRADIENT (server));
+ if (gradient != *gr_selected) {
+ if (*gr_selected != NULL) {
+ *gr_multi = true;
+ } else {
+ *gr_selected = gradient;
+ }
+ }
+ if (spread != *spr_selected) {
+ if (*spr_selected != INT_MAX) {
+ *spr_multi = true;
+ } else {
+ *spr_selected = spread;
+ }
+ }
+ }
+ }
+ if (style && (style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)) {
+ SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item);
+ if (SP_IS_GRADIENT (server)) {
+ SPGradient *gradient = sp_gradient_get_vector (SP_GRADIENT (server), false);
+ SPGradientSpread spread = sp_gradient_get_spread (SP_GRADIENT (server));
+ if (gradient != *gr_selected) {
+ if (*gr_selected != NULL) {
+ *gr_multi = true;
+ } else {
+ *gr_selected = gradient;
+ }
+ }
+ if (spread != *spr_selected) {
+ if (*spr_selected != INT_MAX) {
+ *spr_multi = true;
+ } else {
+ *spr_selected = spread;
+ }
+ }
+ }
+ }
+ }
+ }
+
+static void
+gr_tb_selection_changed (Inkscape::Selection *, gpointer data)
+{
+ GtkWidget *widget = (GtkWidget *) data;
+
+ SPDesktop *desktop = (SPDesktop *) g_object_get_data (G_OBJECT(widget), "desktop");
+ if (!desktop)
+ return;
+
+ Inkscape::Selection *selection = SP_DT_SELECTION (desktop); // take from desktop, not from args
+ if (!selection)
+ return;
+
+ SPEventContext *ev = SP_DT_EVENTCONTEXT (desktop);
+
+ GtkWidget *om = (GtkWidget *) g_object_get_data (G_OBJECT (widget), "menu");
+ if (om) gtk_widget_destroy (om);
+
+ SPGradient *gr_selected = NULL;
+ bool gr_multi = false;
+
+ SPGradientSpread spr_selected = (SPGradientSpread) INT_MAX; // meaning undefined
+ bool spr_multi = false;
+
+ gr_read_selection (selection, ev? ev->get_drag() : NULL, &gr_selected, &gr_multi, &spr_selected, &spr_multi);
+
+ om = gr_vector_list (desktop, selection->isEmpty(), gr_selected, gr_multi);
+ g_object_set_data (G_OBJECT (widget), "menu", om);
+
+ GtkWidget *buttons = (GtkWidget *) g_object_get_data (G_OBJECT(widget), "buttons");
+ gtk_widget_set_sensitive (buttons, (gr_selected && !gr_multi));
+
+ gtk_box_pack_start (GTK_BOX (widget), om, TRUE, TRUE, 0);
+
+ gtk_widget_show_all (widget);
+}
+
+static void
+gr_tb_selection_modified (Inkscape::Selection *selection, guint flags, gpointer data)
+{
+ gr_tb_selection_changed (selection, data);
+}
+
+static void
+gr_drag_selection_changed (gpointer dragger, gpointer data)
+{
+ gr_tb_selection_changed (NULL, data);
+}
+
+static void
+gr_defs_release (SPObject *defs, GtkWidget *widget)
+{
+ gr_tb_selection_changed (NULL, (gpointer) widget);
+}
+
+static void
+gr_defs_modified (SPObject *defs, guint flags, GtkWidget *widget)
+{
+ gr_tb_selection_changed (NULL, (gpointer) widget);
+}
+
+static void
+gr_fork (GtkWidget *button, GtkWidget *widget)
+{
+ SPDesktop *desktop = (SPDesktop *) g_object_get_data (G_OBJECT(widget), "desktop");
+ SPDocument *document = SP_DT_DOCUMENT (desktop);
+ Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
+ SPEventContext *ev = SP_DT_EVENTCONTEXT (desktop);
+ GtkWidget *om = (GtkWidget *) g_object_get_data (G_OBJECT(widget), "menu");
+
+ if (om && document) {
+ GtkWidget *i = gtk_menu_get_active (GTK_MENU (gtk_option_menu_get_menu (GTK_OPTION_MENU (om))));
+ SPGradient *gr = (SPGradient *) g_object_get_data (G_OBJECT(i), "gradient");
+
+ if (gr) {
+ SPGradient *gr_new = sp_gradient_fork_vector_if_necessary (gr);
+ if (gr_new != gr) {
+ gr_apply_gradient (selection, ev? ev->get_drag() : NULL, gr_new);
+ sp_document_done (document);
+ }
+ }
+ }
+
+ spinbutton_defocus(GTK_OBJECT(widget));
+}
+
+static void gr_disconnect_sigc (GObject *obj, sigc::connection *connection) {
+ connection->disconnect();
+ delete connection;
+}
+
+static void gr_disconnect_gsignal (GObject *widget, gpointer defs) {
+ if (defs && G_IS_OBJECT(defs))
+ sp_signal_disconnect_by_data (defs, widget);
+}
+
+
+static void
+gr_edit (GtkWidget *button, GtkWidget *widget)
+{
+ GtkWidget *om = (GtkWidget *) g_object_get_data (G_OBJECT(widget), "menu");
+
+ spinbutton_defocus(GTK_OBJECT(widget));
+
+ if (om) {
+ GtkWidget *i = gtk_menu_get_active (GTK_MENU (gtk_option_menu_get_menu (GTK_OPTION_MENU (om))));
+ SPGradient *gr = (SPGradient *) g_object_get_data (G_OBJECT(i), "gradient");
+
+ if (gr) {
+ GtkWidget *dialog = sp_gradient_vector_editor_new (gr);
+ gtk_widget_show (dialog);
+ }
+ }
+}
+
+GtkWidget *
+gr_change_widget (SPDesktop *desktop)
+{
+ Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
+ SPDocument *document = SP_DT_DOCUMENT (desktop);
+ SPEventContext *ev = SP_DT_EVENTCONTEXT (desktop);
+
+ SPGradient *gr_selected = NULL;
+ bool gr_multi = false;
+
+ SPGradientSpread spr_selected = (SPGradientSpread) INT_MAX; // meaning undefined
+ bool spr_multi = false;
+
+ GtkTooltips *tt = gtk_tooltips_new();
+
+ gr_read_selection (selection, ev? ev->get_drag() : NULL, &gr_selected, &gr_multi, &spr_selected, &spr_multi);
+
+ GtkWidget *widget = gtk_hbox_new(FALSE, FALSE);
+ gtk_object_set_data(GTK_OBJECT(widget), "dtw", desktop->canvas);
+ g_object_set_data (G_OBJECT (widget), "desktop", desktop);
+
+ GtkWidget *om = gr_vector_list (desktop, selection->isEmpty(), gr_selected, gr_multi);
+ g_object_set_data (G_OBJECT (widget), "menu", om);
+
+ gtk_box_pack_start (GTK_BOX (widget), om, TRUE, TRUE, 0);
+
+ {
+ GtkWidget *buttons = gtk_hbox_new(FALSE, 1);
+
+ /* Fork */
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *b = gtk_button_new_with_label(_("Duplicate"));
+ gtk_tooltips_set_tip(tt, b, _("If the gradient is used by more than one object, create a copy of it for the selected object(s)"), NULL);
+ gtk_widget_show(b);
+ gtk_container_add(GTK_CONTAINER(hb), b);
+ gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(gr_fork), widget);
+ gtk_box_pack_start (GTK_BOX(buttons), hb, FALSE, FALSE, 0);
+ }
+
+ /* Edit... */
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *b = gtk_button_new_with_label(_("Edit..."));
+ gtk_tooltips_set_tip(tt, b, _("Edit the stops of the gradient"), NULL);
+ gtk_widget_show(b);
+ gtk_container_add(GTK_CONTAINER(hb), b);
+ gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(gr_edit), widget);
+ gtk_box_pack_start (GTK_BOX(buttons), hb, FALSE, FALSE, 0);
+ }
+
+ gtk_box_pack_end (GTK_BOX(widget), buttons, FALSE, FALSE, 0);
+ g_object_set_data (G_OBJECT(widget), "buttons", buttons);
+ gtk_widget_set_sensitive (buttons, (gr_selected && !gr_multi));
+ }
+
+ // connect to selection modified and changed signals
+ sigc::connection *conn1 = new sigc::connection (selection->connectChanged(
+ sigc::bind (
+ sigc::ptr_fun(&gr_tb_selection_changed),
+ (gpointer)widget )
+ ));
+ sigc::connection *conn2 = new sigc::connection (selection->connectModified(
+ sigc::bind (
+ sigc::ptr_fun(&gr_tb_selection_modified),
+ (gpointer)widget )
+ ));
+
+ sigc::connection *conn3 = new sigc::connection (desktop->connectToolSubselectionChanged(
+ sigc::bind (
+ sigc::ptr_fun(&gr_drag_selection_changed),
+ (gpointer)widget )
+ ));
+
+ // when widget is destroyed, disconnect
+ g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(gr_disconnect_sigc), conn1);
+ g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(gr_disconnect_sigc), conn2);
+ g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(gr_disconnect_sigc), conn3);
+
+ // connect to release and modified signals of the defs (i.e. when someone changes gradient)
+ g_signal_connect (G_OBJECT (SP_DOCUMENT_DEFS (document)), "release", G_CALLBACK (gr_defs_release), widget);
+ g_signal_connect (G_OBJECT (SP_DOCUMENT_DEFS (document)), "modified", G_CALLBACK (gr_defs_modified), widget);
+
+ // when widget is destroyed, disconnect
+ g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(gr_disconnect_gsignal), G_OBJECT (SP_DOCUMENT_DEFS (document)));
+ g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(gr_disconnect_gsignal), G_OBJECT (SP_DOCUMENT_DEFS (document)));
+
+ gtk_widget_show_all (widget);
+ return widget;
+}
+
+GtkWidget *
+sp_gradient_toolbox_new(SPDesktop *desktop)
+{
+ GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
+
+ gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
+ gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
+
+ GtkTooltips *tt = gtk_tooltips_new();
+
+ sp_toolbox_add_label(tbl, _("<b>New:</b>"));
+
+ aux_toolbox_space(tbl, AUX_SPACING);
+
+ {
+ GtkWidget *cvbox = gtk_vbox_new (FALSE, 0);
+ GtkWidget *cbox = gtk_hbox_new (FALSE, 0);
+
+ {
+ GtkWidget *button = sp_button_new_from_data( GTK_ICON_SIZE_SMALL_TOOLBAR,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "fill_gradient",
+ _("Create linear gradient"),
+ tt);
+ g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (gr_toggle_type), tbl);
+ g_object_set_data(G_OBJECT(tbl), "linear", button);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ prefs_get_int_attribute ("tools.gradient", "newgradient", SP_GRADIENT_TYPE_LINEAR) == SP_GRADIENT_TYPE_LINEAR);
+ gtk_box_pack_start(GTK_BOX(cbox), button, FALSE, FALSE, 0);
+ }
+
+ {
+ GtkWidget *button = sp_button_new_from_data( GTK_ICON_SIZE_SMALL_TOOLBAR,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "fill_radial",
+ _("Create radial (elliptic or circular) gradient"),
+ tt);
+ g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (gr_toggle_type), tbl);
+ g_object_set_data(G_OBJECT(tbl), "radial", button);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ prefs_get_int_attribute ("tools.gradient", "newgradient", SP_GRADIENT_TYPE_LINEAR) == SP_GRADIENT_TYPE_RADIAL);
+ gtk_box_pack_start(GTK_BOX(cbox), button, FALSE, FALSE, 0);
+ }
+
+ gtk_box_pack_start(GTK_BOX(cvbox), cbox, TRUE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(tbl), cvbox, FALSE, FALSE, 0);
+ }
+
+ aux_toolbox_space(tbl, AUX_SPACING);
+
+ sp_toolbox_add_label(tbl, _("on"), false);
+
+ aux_toolbox_space(tbl, AUX_SPACING);
+
+ {
+ GtkWidget *cvbox = gtk_vbox_new (FALSE, 0);
+ GtkWidget *cbox = gtk_hbox_new (FALSE, 0);
+
+ {
+ GtkWidget *button = sp_button_new_from_data( GTK_ICON_SIZE_SMALL_TOOLBAR,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "controls_fill",
+ _("Create gradient in the fill"),
+ tt);
+ g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (gr_toggle_fillstroke), tbl);
+ g_object_set_data(G_OBJECT(tbl), "fill", button);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ prefs_get_int_attribute ("tools.gradient", "newfillorstroke", 1) == 1);
+ gtk_box_pack_start(GTK_BOX(cbox), button, FALSE, FALSE, 0);
+ }
+
+ {
+ GtkWidget *button = sp_button_new_from_data( GTK_ICON_SIZE_SMALL_TOOLBAR,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "controls_stroke",
+ _("Create gradient in the stroke"),
+ tt);
+ g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (gr_toggle_fillstroke), tbl);
+ g_object_set_data(G_OBJECT(tbl), "stroke", button);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ prefs_get_int_attribute ("tools.gradient", "newfillorstroke", 1) == 0);
+ gtk_box_pack_start(GTK_BOX(cbox), button, FALSE, FALSE, 0);
+ }
+
+ gtk_box_pack_start(GTK_BOX(cvbox), cbox, TRUE, TRUE, 3);
+ gtk_box_pack_start(GTK_BOX(tbl), cvbox, FALSE, FALSE, 0);
+ }
+
+
+ sp_toolbox_add_label(tbl, _("<b>Change:</b>"));
+
+ aux_toolbox_space(tbl, AUX_SPACING);
+
+ {
+ GtkWidget *vectors = gr_change_widget (desktop);
+ gtk_box_pack_start (GTK_BOX (tbl), vectors, FALSE, FALSE, 0);
+ }
+
+ gtk_widget_show_all(tbl);
+ sp_set_font_size_smaller (tbl);
+
+ return tbl;
+}
+
+
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/widgets/gradient-toolbar.h b/src/widgets/gradient-toolbar.h
new file mode 100644
index 000000000..41138724a
--- /dev/null
+++ b/src/widgets/gradient-toolbar.h
@@ -0,0 +1,21 @@
+#ifndef SEEN_GRADIENT_TOOLBAR_H
+#define SEEN_GRADIENT_TOOLBAR_H
+
+/*
+ * Gradient aux toolbar
+ *
+ * Authors:
+ * bulia byak <bulia@dr.com>
+ *
+ * Copyright (C) 2005 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <gtk/gtkwidget.h>
+struct SPDesktop;
+
+GtkWidget *sp_gradient_toolbox_new (SPDesktop *desktop);
+
+
+#endif /* !SEEN_SELECT_TOOLBAR_H */
diff --git a/src/widgets/gradient-vector.cpp b/src/widgets/gradient-vector.cpp
new file mode 100644
index 000000000..4aac04a3c
--- /dev/null
+++ b/src/widgets/gradient-vector.cpp
@@ -0,0 +1,1115 @@
+#define __SP_GRADIENT_VECTOR_C__
+
+/*
+ * Gradient vector selection widget
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ * Copyright (C) 2004 Monash University
+ * Copyright (C) 2004 David Turner
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#ifdef HAVE_STRING_H
+#endif
+#include <gtk/gtk.h>
+#include "macros.h"
+#include <glibmm/i18n.h>
+#include "../widgets/gradient-image.h"
+#include "../inkscape.h"
+#include "../document-private.h"
+#include "../gradient-chemistry.h"
+#include "gradient-vector.h"
+#include "../helper/window.h"
+
+#include "xml/repr.h"
+
+#include "../dialogs/dialog-events.h"
+#include "../prefs-utils.h"
+#include "svg/css-ostringstream.h"
+#include "sp-stop.h"
+
+enum {
+ VECTOR_SET,
+ LAST_SIGNAL
+};
+
+static void sp_gradient_vector_selector_class_init (SPGradientVectorSelectorClass *klass);
+static void sp_gradient_vector_selector_init (SPGradientVectorSelector *gvs);
+static void sp_gradient_vector_selector_destroy (GtkObject *object);
+
+static void sp_gvs_gradient_release (SPGradient *gr, SPGradientVectorSelector *gvs);
+static void sp_gvs_defs_release (SPObject *defs, SPGradientVectorSelector *gvs);
+static void sp_gvs_defs_modified (SPObject *defs, guint flags, SPGradientVectorSelector *gvs);
+
+static void sp_gvs_rebuild_gui_full (SPGradientVectorSelector *gvs);
+static void sp_gvs_gradient_activate (GtkMenuItem *mi, SPGradientVectorSelector *gvs);
+
+static GtkVBoxClass *parent_class;
+static guint signals[LAST_SIGNAL] = {0};
+
+static GtkWidget *dlg = NULL;
+static win_data wd;
+static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
+static gchar const *prefs_path = "dialogs.gradienteditor";
+
+GtkType
+sp_gradient_vector_selector_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GtkTypeInfo info = {
+ "SPGradientVectorSelector",
+ sizeof (SPGradientVectorSelector),
+ sizeof (SPGradientVectorSelectorClass),
+ (GtkClassInitFunc) sp_gradient_vector_selector_class_init,
+ (GtkObjectInitFunc) sp_gradient_vector_selector_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_VBOX, &info);
+ }
+ return type;
+}
+
+static void
+sp_gradient_vector_selector_class_init (SPGradientVectorSelectorClass *klass)
+{
+ GtkObjectClass *object_class;
+
+ object_class = GTK_OBJECT_CLASS (klass);
+
+ parent_class = (GtkVBoxClass*)gtk_type_class (GTK_TYPE_VBOX);
+
+ signals[VECTOR_SET] = gtk_signal_new ("vector_set",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPGradientVectorSelectorClass, vector_set),
+ gtk_marshal_NONE__POINTER,
+ GTK_TYPE_NONE, 1,
+ GTK_TYPE_POINTER);
+
+ object_class->destroy = sp_gradient_vector_selector_destroy;
+}
+
+static void
+sp_gradient_vector_selector_init (SPGradientVectorSelector *gvs)
+{
+ gvs->idlabel = TRUE;
+
+ gvs->doc = NULL;
+ gvs->gr = NULL;
+
+ gvs->menu = gtk_option_menu_new ();
+ gtk_widget_show (gvs->menu);
+ gtk_box_pack_start (GTK_BOX (gvs), gvs->menu, TRUE, TRUE, 0);
+}
+
+static void
+sp_gradient_vector_selector_destroy (GtkObject *object)
+{
+ SPGradientVectorSelector *gvs;
+
+ gvs = SP_GRADIENT_VECTOR_SELECTOR (object);
+
+ if (gvs->gr) {
+ sp_signal_disconnect_by_data (gvs->gr, gvs);
+ gvs->gr = NULL;
+ }
+
+ if (gvs->doc) {
+ sp_signal_disconnect_by_data (SP_DOCUMENT_DEFS (gvs->doc), gvs);
+ gvs->doc = NULL;
+ }
+
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+GtkWidget *
+sp_gradient_vector_selector_new (SPDocument *doc, SPGradient *gr)
+{
+ GtkWidget *gvs;
+
+ g_return_val_if_fail (!gr || SP_IS_GRADIENT (gr), NULL);
+ g_return_val_if_fail (!gr || (SP_OBJECT_DOCUMENT (gr) == doc), NULL);
+
+ gvs = (GtkWidget*)gtk_type_new (SP_TYPE_GRADIENT_VECTOR_SELECTOR);
+
+ if (doc) {
+ sp_gradient_vector_selector_set_gradient (SP_GRADIENT_VECTOR_SELECTOR (gvs), doc, gr);
+ } else {
+ sp_gvs_rebuild_gui_full (SP_GRADIENT_VECTOR_SELECTOR (gvs));
+ }
+
+ return gvs;
+}
+
+void
+sp_gradient_vector_selector_set_gradient (SPGradientVectorSelector *gvs, SPDocument *doc, SPGradient *gr)
+{
+ static gboolean suppress = FALSE;
+
+ g_return_if_fail (gvs != NULL);
+ g_return_if_fail (SP_IS_GRADIENT_VECTOR_SELECTOR (gvs));
+ g_return_if_fail (!gr || (doc != NULL));
+ g_return_if_fail (!gr || SP_IS_GRADIENT (gr));
+ g_return_if_fail (!gr || (SP_OBJECT_DOCUMENT (gr) == doc));
+ g_return_if_fail (!gr || SP_GRADIENT_HAS_STOPS (gr));
+
+ if (doc != gvs->doc) {
+ /* Disconnect signals */
+ if (gvs->gr) {
+ sp_signal_disconnect_by_data (gvs->gr, gvs);
+ g_signal_handlers_disconnect_matched (G_OBJECT(gvs->gr), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, gvs);
+ gvs->gr = NULL;
+ }
+ if (gvs->doc) {
+ sp_signal_disconnect_by_data (SP_DOCUMENT_DEFS (gvs->doc), gvs);
+ gvs->doc = NULL;
+ }
+ /* Connect signals */
+ if (doc) {
+ g_signal_connect (G_OBJECT (SP_DOCUMENT_DEFS (doc)), "release", G_CALLBACK (sp_gvs_defs_release), gvs);
+ g_signal_connect (G_OBJECT (SP_DOCUMENT_DEFS (doc)), "modified", G_CALLBACK (sp_gvs_defs_modified), gvs);
+ }
+ if (gr) {
+ g_signal_connect (G_OBJECT (gr), "release", G_CALLBACK (sp_gvs_gradient_release), gvs);
+ }
+ gvs->doc = doc;
+ gvs->gr = gr;
+ sp_gvs_rebuild_gui_full (gvs);
+ if (!suppress) g_signal_emit (G_OBJECT (gvs), signals[VECTOR_SET], 0, gr);
+ } else if (gr != gvs->gr) {
+ /* Harder case - keep document, rebuild menus and stuff */
+ /* fixme: (Lauris) */
+ suppress = TRUE;
+ sp_gradient_vector_selector_set_gradient (gvs, NULL, NULL);
+ sp_gradient_vector_selector_set_gradient (gvs, doc, gr);
+ suppress = FALSE;
+ g_signal_emit (G_OBJECT (gvs), signals[VECTOR_SET], 0, gr);
+ }
+ /* The case of setting NULL -> NULL is not very interesting */
+}
+
+SPDocument *
+sp_gradient_vector_selector_get_document (SPGradientVectorSelector *gvs)
+{
+ g_return_val_if_fail (gvs != NULL, NULL);
+ g_return_val_if_fail (SP_IS_GRADIENT_VECTOR_SELECTOR (gvs), NULL);
+
+ return gvs->doc;
+}
+
+SPGradient *
+sp_gradient_vector_selector_get_gradient (SPGradientVectorSelector *gvs)
+{
+ g_return_val_if_fail (gvs != NULL, NULL);
+ g_return_val_if_fail (SP_IS_GRADIENT_VECTOR_SELECTOR (gvs), NULL);
+
+ return gvs->gr;
+}
+
+static void
+sp_gvs_rebuild_gui_full (SPGradientVectorSelector *gvs)
+{
+ /* Clear old menu, if there is any */
+ if (gtk_option_menu_get_menu (GTK_OPTION_MENU (gvs->menu))) {
+ gtk_option_menu_remove_menu (GTK_OPTION_MENU (gvs->menu));
+ }
+
+ /* Create new menu widget */
+ GtkWidget *m = gtk_menu_new ();
+ gtk_widget_show (m);
+
+ /* Pick up all gradients with vectors */
+ GSList *gl = NULL;
+ if (gvs->gr) {
+ const GSList *gradients = sp_document_get_resource_list (SP_OBJECT_DOCUMENT (gvs->gr), "gradient");
+ for (const GSList *l = gradients; l != NULL; l = l->next) {
+ if (SP_GRADIENT_HAS_STOPS (l->data)) {
+ gl = g_slist_prepend (gl, l->data);
+ }
+ }
+ }
+ gl = g_slist_reverse (gl);
+
+ gint pos = 0;
+ gint idx = 0;
+
+ if (!gvs->doc) {
+ GtkWidget *i;
+ i = gtk_menu_item_new_with_label (_("No document selected"));
+ gtk_widget_show (i);
+ gtk_menu_append (GTK_MENU (m), i);
+ gtk_widget_set_sensitive (gvs->menu, FALSE);
+ } else if (!gl) {
+ GtkWidget *i;
+ i = gtk_menu_item_new_with_label (_("No gradients in document"));
+ gtk_widget_show (i);
+ gtk_menu_append (GTK_MENU (m), i);
+ gtk_widget_set_sensitive (gvs->menu, FALSE);
+ } else if (!gvs->gr) {
+ GtkWidget *i;
+ i = gtk_menu_item_new_with_label (_("No gradient selected"));
+ gtk_widget_show (i);
+ gtk_menu_append (GTK_MENU (m), i);
+ gtk_widget_set_sensitive (gvs->menu, FALSE);
+ } else {
+ while (gl) {
+ SPGradient *gr;
+ GtkWidget *i, *w;
+ gr = SP_GRADIENT (gl->data);
+ gl = g_slist_remove (gl, gr);
+
+ /* We have to know: */
+ /* Gradient destroy */
+ /* Gradient name change */
+ i = gtk_menu_item_new ();
+ gtk_widget_show (i);
+ g_object_set_data (G_OBJECT (i), "gradient", gr);
+ g_signal_connect (G_OBJECT (i), "activate", G_CALLBACK (sp_gvs_gradient_activate), gvs);
+
+ w = sp_gradient_image_new (gr);
+ gtk_widget_show (w);
+
+ if (gvs->idlabel) {
+ GtkWidget *hb, *l;
+ hb = gtk_hbox_new (FALSE, 4);
+ gtk_widget_show (hb);
+ l = gtk_label_new (SP_OBJECT_ID (gr));
+ gtk_widget_show (l);
+ gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hb), w, FALSE, FALSE, 0);
+ w = hb;
+ }
+
+ gtk_container_add (GTK_CONTAINER (i), w);
+
+ gtk_menu_append (GTK_MENU (m), i);
+
+ if (gr == gvs->gr) pos = idx;
+ idx += 1;
+ }
+ gtk_widget_set_sensitive (gvs->menu, TRUE);
+ }
+
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (gvs->menu), m);
+ /* Set history */
+ gtk_option_menu_set_history (GTK_OPTION_MENU (gvs->menu), pos);
+}
+
+static void
+sp_gvs_gradient_activate (GtkMenuItem *mi, SPGradientVectorSelector *gvs)
+{
+ SPGradient *gr, *norm;
+
+ gr = (SPGradient*)g_object_get_data (G_OBJECT (mi), "gradient");
+ /* Hmmm... bad things may happen here, if actual gradient is something new */
+ /* Namely - menuitems etc. will be fucked up */
+ /* Hmmm - probably we can just re-set it as menuitem data (Lauris) */
+
+ //g_print ("SPGradientVectorSelector: gradient %s activated\n", SP_OBJECT_ID (gr));
+
+ norm = sp_gradient_ensure_vector_normalized (gr);
+ if (norm != gr) {
+ //g_print ("SPGradientVectorSelector: become %s after normalization\n", SP_OBJECT_ID (norm));
+ /* But be careful that we do not have gradient saved anywhere else */
+ g_object_set_data (G_OBJECT (mi), "gradient", norm);
+ }
+
+ /* fixme: Really we would want to use _set_vector */
+ /* Detach old */
+ if (gvs->gr) {
+ sp_signal_disconnect_by_data (gvs->gr, gvs);
+ gvs->gr = NULL;
+ }
+ /* Attach new */
+ if (norm) {
+ g_signal_connect (G_OBJECT (norm), "release", G_CALLBACK (sp_gvs_gradient_release), gvs);
+ /* fixme: Connect 'modified'? (Lauris) */
+ /* fixme: I think we do not need it (Lauris) */
+ gvs->gr = norm;
+ }
+
+ g_signal_emit (G_OBJECT (gvs), signals[VECTOR_SET], 0, norm);
+
+ if (norm != gr) {
+ /* We do extra undo push here */
+ /* If handler has already done it, it is just NOP */
+ sp_document_done (SP_OBJECT_DOCUMENT (norm));
+ }
+}
+
+static void
+sp_gvs_gradient_release (SPGradient *gr, SPGradientVectorSelector *gvs)
+{
+ /* Disconnect gradient */
+ if (gvs->gr) {
+ sp_signal_disconnect_by_data (gvs->gr, gvs);
+ gvs->gr = NULL;
+ }
+
+ /* Rebuild GUI */
+ sp_gvs_rebuild_gui_full (gvs);
+}
+
+static void
+sp_gvs_defs_release (SPObject *defs, SPGradientVectorSelector *gvs)
+{
+ gvs->doc = NULL;
+ /* Disconnect gradient as well */
+ if (gvs->gr) {
+ sp_signal_disconnect_by_data (gvs->gr, gvs);
+ gvs->gr = NULL;
+ }
+
+ /* Rebuild GUI */
+ sp_gvs_rebuild_gui_full (gvs);
+}
+
+static void
+sp_gvs_defs_modified (SPObject *defs, guint flags, SPGradientVectorSelector *gvs)
+{
+ /* fixme: We probably have to check some flags here (Lauris) */
+
+ sp_gvs_rebuild_gui_full (gvs);
+}
+
+/*##################################################################
+### Vector Editing Widget
+##################################################################*/
+
+#include "../widgets/sp-color-notebook.h"
+#include "../widgets/sp-color-preview.h"
+#include "../widgets/widget-sizes.h"
+#include "../xml/node-event-vector.h"
+#include "../svg/svg.h"
+
+
+#define PAD 4
+
+static GtkWidget *sp_gradient_vector_widget_new (SPGradient *gradient, SPStop *stop);
+
+static void sp_gradient_vector_widget_load_gradient (GtkWidget *widget, SPGradient *gradient);
+static gint sp_gradient_vector_dialog_delete (GtkWidget *widget, GdkEvent *event, GtkWidget *dialog);
+static void sp_gradient_vector_dialog_destroy (GtkObject *object, gpointer data);
+
+static void sp_gradient_vector_widget_destroy (GtkObject *object, gpointer data);
+static void sp_gradient_vector_gradient_release (SPGradient *gradient, GtkWidget *widget);
+static void sp_gradient_vector_gradient_modified (SPGradient *gradient, guint flags, GtkWidget *widget);
+static void sp_gradient_vector_color_dragged (SPColorSelector *csel, GtkObject *object);
+static void sp_gradient_vector_color_changed (SPColorSelector *csel, GtkObject *object);
+static void update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop);
+
+static gboolean blocked = FALSE;
+
+static void grad_edit_dia_stop_added_or_removed (Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
+{
+ GtkWidget *vb = GTK_WIDGET(data);
+ GtkWidget *mnu = (GtkWidget *)g_object_get_data (G_OBJECT(vb), "stopmenu");
+ SPGradient *gradient = (SPGradient *)g_object_get_data (G_OBJECT(vb), "gradient");
+ update_stop_list (mnu, gradient, NULL);
+}
+
+//FIXME!!! We must also listen to attr changes on all children (i.e. stops) too,
+//otherwise the dialog does not reflect undoing color or offset change. This is a major
+//hassle, unless we have a "one of the descendants changed in some way" signal.
+static Inkscape::XML::NodeEventVector grad_edit_dia_repr_events =
+{
+ grad_edit_dia_stop_added_or_removed, /* child_added */
+ grad_edit_dia_stop_added_or_removed, /* child_removed */
+ NULL, /* attr_changed*/
+ NULL, /* content_changed */
+ NULL /* order_changed */
+};
+
+static void
+verify_grad(SPGradient *gradient)
+{
+ int i = 0;
+ SPStop *stop = NULL;
+ /* count stops */
+ for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) {
+ if (SP_IS_STOP (ochild)) {
+ i++;
+ stop = SP_STOP(ochild);
+ }
+ }
+
+ if (i < 1) {
+ gchar c[64];
+ sp_svg_write_color (c, 64, 0x00000000);
+
+ Inkscape::CSSOStringStream os;
+ os << "stop-color:" << c << ";stop-opacity:" << 1.0 << ";";
+
+ Inkscape::XML::Node *child;
+
+ child = sp_repr_new ("svg:stop");
+ sp_repr_set_css_double(child, "offset", 0.0);
+ child->setAttribute("style", os.str().c_str());
+ SP_OBJECT_REPR (gradient)->addChild(child, NULL);
+
+ child = sp_repr_new ("svg:stop");
+ sp_repr_set_css_double(child, "offset", 1.0);
+ child->setAttribute("style", os.str().c_str());
+ SP_OBJECT_REPR (gradient)->addChild(child, NULL);
+ }
+ if (i < 2) {
+ sp_repr_set_css_double(SP_OBJECT_REPR(stop), "offset", 0.0);
+ Inkscape::XML::Node *child = SP_OBJECT_REPR(stop)->duplicate();
+ sp_repr_set_css_double(child, "offset", 1.0);
+ SP_OBJECT_REPR(gradient)->addChild(child, SP_OBJECT_REPR (stop));
+ }
+}
+
+static void
+select_stop_in_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop)
+{
+ int i = 0;
+ for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) {
+ if (SP_IS_STOP (ochild)) {
+ if (SP_OBJECT (ochild) == SP_OBJECT(new_stop)) {
+ gtk_option_menu_set_history (GTK_OPTION_MENU (mnu), i);
+ break;
+ }
+ i++;
+ }
+ }
+}
+
+static void
+update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop)
+{
+
+ if (!SP_IS_GRADIENT (gradient))
+ return;
+
+ blocked = TRUE;
+
+ /* Clear old menu, if there is any */
+ if (gtk_option_menu_get_menu (GTK_OPTION_MENU (mnu))) {
+ gtk_option_menu_remove_menu (GTK_OPTION_MENU (mnu));
+ }
+
+ /* Create new menu widget */
+ GtkWidget *m = gtk_menu_new ();
+ gtk_widget_show (m);
+ GSList *sl = NULL;
+ if (gradient->has_stops) {
+ for ( SPObject *ochild = sp_object_first_child (SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) {
+ if (SP_IS_STOP (ochild)) {
+ sl = g_slist_append (sl, ochild);
+ }
+ }
+ }
+ if (!sl) {
+ GtkWidget *i = gtk_menu_item_new_with_label (_("No stops in gradient"));
+ gtk_widget_show (i);
+ gtk_menu_append (GTK_MENU (m), i);
+ gtk_widget_set_sensitive (mnu, FALSE);
+ } else {
+
+ for (; sl != NULL; sl = sl->next){
+ SPStop *stop;
+ GtkWidget *i;
+ if (SP_IS_STOP(sl->data)){
+ stop = SP_STOP (sl->data);
+ i = gtk_menu_item_new ();
+ gtk_widget_show (i);
+ g_object_set_data (G_OBJECT (i), "stop", stop);
+ GtkWidget *hb = gtk_hbox_new (FALSE, 4);
+ GtkWidget *cpv = sp_color_preview_new(sp_stop_get_rgba32(stop));
+ gtk_widget_show (cpv);
+ gtk_container_add ( GTK_CONTAINER (hb), cpv );
+ g_object_set_data ( G_OBJECT (i), "preview", cpv );
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) sl->data);
+ GtkWidget *l = gtk_label_new (repr->attribute("id"));
+ gtk_widget_show (l);
+ gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0);
+ gtk_widget_show (hb);
+ gtk_container_add (GTK_CONTAINER (i), hb);
+ gtk_menu_append (GTK_MENU (m), i);
+ }
+ }
+
+ gtk_widget_set_sensitive (mnu, TRUE);
+ }
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (mnu), m);
+
+ /* Set history */
+ if (new_stop == NULL) {
+ gtk_option_menu_set_history (GTK_OPTION_MENU (mnu), 0);
+ } else {
+ select_stop_in_list (mnu, gradient, new_stop);
+ }
+
+ blocked = FALSE;
+}
+
+
+/*user selected existing stop from list*/
+static void
+sp_grad_edit_select (GtkOptionMenu *mnu, GtkWidget *tbl)
+{
+ SPGradient *gradient = (SPGradient *)g_object_get_data (G_OBJECT(tbl), "gradient");
+
+ GObject *item = G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu))));
+ SPStop *stop = SP_STOP (g_object_get_data (item, "stop"));
+ if (!stop) return;
+
+ blocked = TRUE;
+
+ SPColorSelector *csel = (SPColorSelector*)g_object_get_data (G_OBJECT (tbl), "cselector");
+ guint32 const c = sp_stop_get_rgba32(stop);
+ csel->base->setAlpha(SP_RGBA32_A_F (c));
+ SPColor color;
+ sp_color_set_rgb_float (&color, SP_RGBA32_R_F (c), SP_RGBA32_G_F (c), SP_RGBA32_B_F (c));
+ // set its color, from the stored array
+ csel->base->setColor( color );
+ GtkWidget *offspin = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "offspn"));
+ GtkWidget *offslide =GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "offslide"));
+
+ GtkAdjustment *adj = (GtkAdjustment*)gtk_object_get_data (GTK_OBJECT (tbl), "offset");
+
+ bool isEndStop = false;
+
+ SPStop *prev = NULL;
+ prev = sp_prev_stop(stop, gradient);
+ if (prev != NULL ) {
+ adj->lower = prev->offset;
+ } else {
+ isEndStop = true;
+ adj->lower = 0;
+ }
+
+ SPStop *next = NULL;
+ next = sp_next_stop(stop);
+ if (next != NULL ) {
+ adj->upper = next->offset;
+ } else {
+ isEndStop = true;
+ adj->upper = 1.0;
+ }
+
+ //fixme: does this work on all possible input gradients?
+ if (!isEndStop) {
+ gtk_widget_set_sensitive (offslide, TRUE);
+ gtk_widget_set_sensitive (GTK_WIDGET (offspin), TRUE);
+ } else {
+ gtk_widget_set_sensitive (offslide, FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (offspin), FALSE);
+ }
+
+ gtk_adjustment_set_value (adj, stop->offset);
+
+ gtk_adjustment_changed (adj);
+
+ blocked = FALSE;
+}
+
+
+
+
+static void
+offadjustmentChanged( GtkAdjustment *adjustment, GtkWidget *vb)
+{
+ if (blocked)
+ return;
+
+ blocked = TRUE;
+
+ GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(vb), "stopmenu");
+ if (!g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop")) return;
+ SPStop *stop = SP_STOP(g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop"));
+
+ stop->offset = adjustment->value;
+ sp_repr_set_css_double(SP_OBJECT_REPR(stop), "offset", stop->offset);
+
+ sp_document_done (SP_OBJECT_DOCUMENT (stop));
+
+ blocked = FALSE;
+}
+
+guint32
+sp_average_color (guint32 c1, guint32 c2, gdouble p = 0.5)
+{
+ guint32 r = (guint32) (SP_RGBA32_R_U (c1) * p + SP_RGBA32_R_U (c2) * (1 - p));
+ guint32 g = (guint32) (SP_RGBA32_G_U (c1) * p + SP_RGBA32_G_U (c2) * (1 - p));
+ guint32 b = (guint32) (SP_RGBA32_B_U (c1) * p + SP_RGBA32_B_U (c2) * (1 - p));
+ guint32 a = (guint32) (SP_RGBA32_A_U (c1) * p + SP_RGBA32_A_U (c2) * (1 - p));
+
+ return SP_RGBA32_U_COMPOSE (r, g, b, a);
+}
+
+
+static void
+sp_grd_ed_add_stop (GtkWidget *widget, GtkWidget *vb)
+{
+ SPGradient *gradient = (SPGradient *) g_object_get_data (G_OBJECT(vb), "gradient");
+ verify_grad (gradient);
+ GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(vb), "stopmenu");
+
+ SPStop *stop = (SPStop *) g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop");
+
+ if (stop == NULL)
+ return;
+
+ Inkscape::XML::Node *new_stop_repr = NULL;
+
+ SPStop *next = sp_next_stop (stop);
+
+ if (next == NULL) {
+ SPStop *prev = sp_prev_stop (stop, gradient);
+ if (prev != NULL) {
+ next = stop;
+ stop = prev;
+ }
+ }
+
+ if (next != NULL) {
+ new_stop_repr = SP_OBJECT_REPR(stop)->duplicate();
+ SP_OBJECT_REPR(gradient)->addChild(new_stop_repr, SP_OBJECT_REPR(stop));
+ } else {
+ next = stop;
+ new_stop_repr = SP_OBJECT_REPR(sp_prev_stop(stop, gradient))->duplicate();
+ SP_OBJECT_REPR(gradient)->addChild(new_stop_repr, SP_OBJECT_REPR(sp_prev_stop(stop, gradient)));
+ }
+
+ SPStop *newstop = (SPStop *) SP_OBJECT_DOCUMENT(gradient)->getObjectByRepr(new_stop_repr);
+
+ newstop->offset = (stop->offset + next->offset) * 0.5 ;
+
+ guint32 const c1 = sp_stop_get_rgba32(stop);
+ guint32 const c2 = sp_stop_get_rgba32(next);
+ guint32 cnew = sp_average_color (c1, c2);
+
+ Inkscape::CSSOStringStream os;
+ gchar c[64];
+ sp_svg_write_color (c, 64, cnew);
+ gdouble opacity = (gdouble) SP_RGBA32_A_F (cnew);
+ os << "stop-color:" << c << ";stop-opacity:" << opacity <<";";
+ SP_OBJECT_REPR (newstop)->setAttribute("style", os.str().c_str());
+
+ sp_gradient_vector_widget_load_gradient (vb, gradient);
+ Inkscape::GC::release(new_stop_repr);
+ update_stop_list(GTK_WIDGET(mnu), gradient, newstop);
+ GtkWidget *offspin = GTK_WIDGET (g_object_get_data (G_OBJECT (vb), "offspn"));
+ GtkWidget *offslide =GTK_WIDGET (g_object_get_data (G_OBJECT (vb), "offslide"));
+ gtk_widget_set_sensitive (offslide, TRUE);
+ gtk_widget_set_sensitive (GTK_WIDGET (offspin), TRUE);
+ sp_document_done (SP_OBJECT_DOCUMENT (gradient));
+}
+
+static void
+sp_grd_ed_del_stop (GtkWidget *widget, GtkWidget *vb)
+{
+ SPGradient *gradient = (SPGradient *)g_object_get_data (G_OBJECT(vb), "gradient");
+
+ GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(vb), "stopmenu");
+ if (!g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop")) return;
+ SPStop *stop = SP_STOP(g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop"));
+ if (gradient->vector.stops.size() > 2) { // 2 is the minimum
+
+ // if we delete first or last stop, move the next/previous to the edge
+ if (stop->offset == 0) {
+ SPStop *next = sp_next_stop (stop);
+ if (next) {
+ next->offset = 0;
+ sp_repr_set_css_double (SP_OBJECT_REPR (next), "offset", 0);
+ }
+ } else if (stop->offset == 1) {
+ SPStop *prev = sp_prev_stop (stop, gradient);
+ if (prev) {
+ prev->offset = 1;
+ sp_repr_set_css_double (SP_OBJECT_REPR (prev), "offset", 1);
+ }
+ }
+
+ SP_OBJECT_REPR(gradient)->removeChild(SP_OBJECT_REPR(stop));
+ sp_gradient_vector_widget_load_gradient (vb, gradient);
+ update_stop_list(GTK_WIDGET(mnu), gradient, NULL);
+ sp_document_done (SP_OBJECT_DOCUMENT (gradient));
+ }
+
+}
+
+static GtkWidget *
+sp_gradient_vector_widget_new (SPGradient *gradient, SPStop *select_stop)
+{
+ GtkWidget *vb, *w, *f, *csel;
+
+ g_return_val_if_fail (!gradient || SP_IS_GRADIENT (gradient), NULL);
+
+ vb = gtk_vbox_new (FALSE, PAD);
+ g_signal_connect (G_OBJECT (vb), "destroy", G_CALLBACK (sp_gradient_vector_widget_destroy), NULL);
+
+ w = sp_gradient_image_new (gradient);
+ g_object_set_data (G_OBJECT (vb), "preview", w);
+ gtk_widget_show (w);
+ gtk_box_pack_start (GTK_BOX (vb), w, TRUE, TRUE, PAD);
+
+ gtk_object_set_data (GTK_OBJECT (vb), "gradient", gradient);
+ sp_repr_add_listener (SP_OBJECT_REPR(gradient), &grad_edit_dia_repr_events, vb);
+ GtkTooltips *tt = gtk_tooltips_new ();
+
+ /* Stop list */
+ GtkWidget *mnu = gtk_option_menu_new ();
+ /* Create new menu widget */
+ update_stop_list (GTK_WIDGET(mnu), gradient, NULL);
+ gtk_signal_connect (GTK_OBJECT (mnu), "changed", GTK_SIGNAL_FUNC (sp_grad_edit_select), vb);
+ gtk_widget_show (mnu);
+ gtk_object_set_data (GTK_OBJECT (vb), "stopmenu", mnu);
+ gtk_box_pack_start (GTK_BOX (vb), mnu, FALSE, FALSE, 0);
+
+ /* Add and Remove buttons */
+ GtkWidget *hb = gtk_hbox_new (FALSE, 1);
+ // TRANSLATORS: "Stop" means: a "phase" of a gradient
+ GtkWidget *b = gtk_button_new_with_label (_("Add stop"));
+ gtk_widget_show (b);
+ gtk_container_add (GTK_CONTAINER (hb), b);
+ gtk_tooltips_set_tip (tt, b, _("Add another control stop to gradient"), NULL);
+ gtk_signal_connect (GTK_OBJECT (b), "clicked", GTK_SIGNAL_FUNC (sp_grd_ed_add_stop), vb);
+ b = gtk_button_new_with_label (_("Delete stop"));
+ gtk_widget_show (b);
+ gtk_container_add (GTK_CONTAINER (hb), b);
+ gtk_tooltips_set_tip (tt, b, _("Delete current control stop from gradient"), NULL);
+ gtk_signal_connect (GTK_OBJECT (b), "clicked", GTK_SIGNAL_FUNC (sp_grd_ed_del_stop), vb);
+
+ gtk_widget_show (hb);
+ gtk_box_pack_start (GTK_BOX (vb),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+
+ /* Offset Slider and stuff */
+ hb = gtk_hbox_new (FALSE, 0);
+
+ /* Label */
+ GtkWidget *l = gtk_label_new (_("Offset:"));
+ gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hb),l, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ gtk_widget_show (l);
+
+ /* Adjustment */
+ GtkAdjustment *Offset_adj = NULL;
+ Offset_adj= (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 1.0, 0.01, 0.01, 0.0);
+ gtk_object_set_data (GTK_OBJECT (vb), "offset", Offset_adj);
+ GtkMenu *m = GTK_MENU(gtk_option_menu_get_menu (GTK_OPTION_MENU(mnu)));
+ SPStop *stop = SP_STOP (g_object_get_data (G_OBJECT (gtk_menu_get_active (m)), "stop"));
+ gtk_adjustment_set_value (Offset_adj, stop->offset);
+
+ /* Slider */
+ GtkWidget *slider = gtk_hscale_new(Offset_adj);
+ gtk_scale_set_draw_value( GTK_SCALE(slider), FALSE );
+ gtk_widget_show (slider);
+ gtk_box_pack_start (GTK_BOX (hb),slider, TRUE, TRUE, AUX_BETWEEN_BUTTON_GROUPS);
+ gtk_object_set_data (GTK_OBJECT (vb), "offslide", slider);
+
+ /* Spinbutton */
+ GtkWidget *sbtn = gtk_spin_button_new (GTK_ADJUSTMENT (Offset_adj), 0.01, 2);
+ sp_dialog_defocus_on_enter (sbtn);
+ gtk_widget_show (sbtn);
+ gtk_box_pack_start (GTK_BOX (hb),sbtn, FALSE, TRUE, AUX_BETWEEN_BUTTON_GROUPS);
+ gtk_object_set_data (GTK_OBJECT (vb), "offspn", sbtn);
+
+ if (stop->offset>0 && stop->offset<1) {
+ gtk_widget_set_sensitive (slider, TRUE);
+ gtk_widget_set_sensitive (GTK_WIDGET (sbtn), TRUE);
+ } else {
+ gtk_widget_set_sensitive (slider, FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (sbtn), FALSE);
+ }
+
+
+ /* Signals */
+ gtk_signal_connect (GTK_OBJECT (Offset_adj), "value_changed",
+ GTK_SIGNAL_FUNC (offadjustmentChanged), vb);
+
+ // gtk_signal_connect (GTK_OBJECT (slider), "changed", GTK_SIGNAL_FUNC (offsliderChanged), vb);
+ gtk_widget_show (hb);
+ gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, PAD);
+
+ // TRANSLATORS: "Stop" means: a "phase" of a gradient
+ f = gtk_frame_new (_("Stop Color"));
+ gtk_widget_show (f);
+ gtk_box_pack_start (GTK_BOX (vb), f, TRUE, TRUE, PAD);
+ csel = (GtkWidget*)sp_color_selector_new (SP_TYPE_COLOR_NOTEBOOK, SP_COLORSPACE_TYPE_NONE);
+ g_object_set_data (G_OBJECT (vb), "cselector", csel);
+ gtk_widget_show (csel);
+ gtk_container_add (GTK_CONTAINER (f), csel);
+ g_signal_connect (G_OBJECT (csel), "dragged", G_CALLBACK (sp_gradient_vector_color_dragged), vb);
+ g_signal_connect (G_OBJECT (csel), "changed", G_CALLBACK (sp_gradient_vector_color_changed), vb);
+
+ gtk_widget_show (vb);
+
+ sp_gradient_vector_widget_load_gradient (vb, gradient);
+
+ if (select_stop)
+ select_stop_in_list (GTK_WIDGET(mnu), gradient, select_stop);
+
+ return vb;
+}
+
+
+
+GtkWidget *
+sp_gradient_vector_editor_new (SPGradient *gradient, SPStop *stop)
+{
+ GtkWidget *wid;
+
+ if (dlg == NULL) {
+
+ dlg = sp_window_new (_("Gradient editor"), TRUE);
+ if (x == -1000 || y == -1000) {
+ x = prefs_get_int_attribute (prefs_path, "x", 0);
+ y = prefs_get_int_attribute (prefs_path, "y", 0);
+ }
+ if (w ==0 || h == 0) {
+ w = prefs_get_int_attribute (prefs_path, "w", 0);
+ h = prefs_get_int_attribute (prefs_path, "h", 0);
+ }
+ if (x != 0 || y != 0)
+ gtk_window_move ((GtkWindow *) dlg, x, y);
+ else
+ gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
+ if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
+ sp_transientize (dlg);
+ wd.win = dlg;
+ wd.stop = 0;
+ g_signal_connect (G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd);
+ gtk_signal_connect (GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
+ gtk_signal_connect (GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_gradient_vector_dialog_destroy), dlg);
+ gtk_signal_connect (GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_gradient_vector_dialog_delete), dlg);
+ g_signal_connect (G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_gradient_vector_dialog_delete), dlg);
+ g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg );
+ g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg );
+
+ gtk_container_set_border_width (GTK_CONTAINER (dlg), PAD);
+
+ wid = (GtkWidget*)sp_gradient_vector_widget_new (gradient, stop);
+ g_object_set_data (G_OBJECT (dlg), "gradient-vector-widget", wid);
+ /* Connect signals */
+ gtk_widget_show (wid);
+ gtk_container_add (GTK_CONTAINER (dlg), wid);
+ } else {
+ // FIXME: temp fix for 0.38
+ // Simply load_gradient into the editor does not work for multi-stop gradients,
+ // as the stop list and other widgets are in a wrong state and crash readily.
+ // Instead we just delete the window (by sending the delete signal)
+ // and call sp_gradient_vector_editor_new again, so it creates the window anew.
+
+ GdkEventAny event;
+ GtkWidget *widget = (GtkWidget *) dlg;
+ event.type = GDK_DELETE;
+ event.window = widget->window;
+ event.send_event = TRUE;
+ g_object_ref (G_OBJECT (event.window));
+ gtk_main_do_event ((GdkEvent*)&event);
+ g_object_unref (G_OBJECT (event.window));
+
+ g_assert (dlg == NULL);
+ sp_gradient_vector_editor_new (gradient, stop);
+ }
+
+ return dlg;
+}
+
+static void
+sp_gradient_vector_widget_load_gradient (GtkWidget *widget, SPGradient *gradient)
+{
+ blocked = TRUE;
+
+ SPGradient *old;
+
+ old = (SPGradient*)g_object_get_data (G_OBJECT (widget), "gradient");
+ if (old != gradient) {
+ if (old) {
+ sp_signal_disconnect_by_data (old, widget);
+ }
+ if (gradient) {
+ g_signal_connect (G_OBJECT (gradient), "release", G_CALLBACK (sp_gradient_vector_gradient_release), widget);
+ g_signal_connect (G_OBJECT (gradient), "modified", G_CALLBACK (sp_gradient_vector_gradient_modified), widget);
+ }
+ }
+
+ g_object_set_data (G_OBJECT (widget), "gradient", gradient);
+
+ if (gradient) {
+ sp_gradient_ensure_vector (gradient);
+
+ GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(widget), "stopmenu");
+ SPStop *stop = SP_STOP(g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop"));
+ guint32 const c = sp_stop_get_rgba32(stop);
+
+ /// get the color selector
+ SPColorSelector *csel = SP_COLOR_SELECTOR(g_object_get_data (G_OBJECT (widget), "cselector"));
+ // set alpha
+ csel->base->setAlpha(SP_RGBA32_A_F (c));
+ SPColor color;
+ sp_color_set_rgb_float (&color, SP_RGBA32_R_F (c), SP_RGBA32_G_F (c), SP_RGBA32_B_F (c));
+ // set color
+ csel->base->setColor( color );
+ }
+
+ /* Fill preview */
+ GtkWidget *w = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(widget), "preview"));
+ sp_gradient_image_set_gradient (SP_GRADIENT_IMAGE (w), gradient);
+
+ GtkWidget *mnu = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(widget), "stopmenu"));
+ update_stop_list (GTK_WIDGET(mnu), gradient, NULL);
+
+ // Once the user edits a gradient, it stops being auto-collectable
+ if (SP_OBJECT_REPR(gradient)->attribute("inkscape:collect")) {
+ SPDocument *document = SP_OBJECT_DOCUMENT (gradient);
+ gboolean saved = sp_document_get_undo_sensitive(document);
+ sp_document_set_undo_sensitive (document, FALSE);
+ SP_OBJECT_REPR(gradient)->setAttribute("inkscape:collect", NULL);
+ sp_document_set_undo_sensitive (document, saved);
+ }
+
+ blocked = FALSE;
+}
+
+static void
+sp_gradient_vector_dialog_destroy (GtkObject *object, gpointer data)
+{
+ sp_signal_disconnect_by_data (INKSCAPE, dlg);
+ wd.win = dlg = NULL;
+ wd.stop = 0;
+}
+
+static gboolean
+sp_gradient_vector_dialog_delete (GtkWidget *widget, GdkEvent *event, GtkWidget *dialog)
+{
+ gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
+ gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
+
+ prefs_set_int_attribute (prefs_path, "x", x);
+ prefs_set_int_attribute (prefs_path, "y", y);
+ prefs_set_int_attribute (prefs_path, "w", w);
+ prefs_set_int_attribute (prefs_path, "h", h);
+
+ return FALSE; // which means, go ahead and destroy it
+}
+
+/* Widget destroy handler */
+
+static void
+sp_gradient_vector_widget_destroy (GtkObject *object, gpointer data)
+{
+ GObject *gradient;
+
+ gradient = (GObject*)g_object_get_data (G_OBJECT (object), "gradient");
+
+ if (gradient && SP_OBJECT_REPR(gradient)) {
+ /* Remove signals connected to us */
+ /* fixme: may use _connect_while_alive as well */
+ sp_signal_disconnect_by_data (gradient, object);
+ sp_repr_remove_listener_by_data (SP_OBJECT_REPR(gradient), object);
+ }
+}
+
+static void
+sp_gradient_vector_gradient_release (SPGradient *gradient, GtkWidget *widget)
+{
+ sp_gradient_vector_widget_load_gradient (widget, NULL);
+}
+
+static void
+sp_gradient_vector_gradient_modified (SPGradient *gradient, guint flags, GtkWidget *widget)
+{
+ if (!blocked) {
+ blocked = TRUE;
+ sp_gradient_vector_widget_load_gradient (widget, gradient);
+ blocked = FALSE;
+ }
+}
+
+static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GtkObject *object)
+{
+ SPGradient *gradient, *ngr;
+
+ if (blocked) return;
+
+ gradient = (SPGradient*)g_object_get_data (G_OBJECT (object), "gradient");
+ if (!gradient) return;
+
+ blocked = TRUE;
+
+ ngr = sp_gradient_ensure_vector_normalized (gradient);
+ if (ngr != gradient) {
+ /* Our master gradient has changed */
+ sp_gradient_vector_widget_load_gradient (GTK_WIDGET (object), ngr);
+ }
+
+ sp_gradient_ensure_vector (ngr);
+
+ GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(object), "stopmenu");
+ SPStop *stop = SP_STOP(g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop"));
+
+
+ csel->base->getColorAlpha(stop->specified_color, &stop->opacity);
+ stop->currentColor = false;
+
+ blocked = FALSE;
+ SPColorPreview *cpv = (SPColorPreview *)g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "preview");
+ sp_color_preview_set_rgba32(cpv, sp_stop_get_rgba32(stop));
+
+}
+
+static void
+sp_gradient_vector_color_changed (SPColorSelector *csel, GtkObject *object)
+{
+ SPColor color;
+ float alpha;
+ guint32 rgb;
+
+ if (blocked) return;
+
+ SPGradient *gradient = (SPGradient*)g_object_get_data (G_OBJECT (object), "gradient");
+ if (!gradient) return;
+
+ blocked = TRUE;
+
+ SPGradient *ngr = sp_gradient_ensure_vector_normalized (gradient);
+ if (ngr != gradient) {
+ /* Our master gradient has changed */
+ sp_gradient_vector_widget_load_gradient (GTK_WIDGET (object), ngr);
+ }
+
+ sp_gradient_ensure_vector (ngr);
+
+ /* Set start parameters */
+ /* We rely on normalized vector, i.e. stops HAVE to exist */
+ g_return_if_fail (sp_first_stop(ngr) != NULL);
+
+ GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(object), "stopmenu");
+ SPStop *stop = SP_STOP(g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop"));
+
+ csel = (SPColorSelector*)g_object_get_data (G_OBJECT (object), "cselector");
+ csel->base->getColorAlpha( color, &alpha );
+ rgb = sp_color_get_rgba32_ualpha (&color, 0x00);
+
+ sp_repr_set_css_double (SP_OBJECT_REPR (stop), "offset", stop->offset);
+ Inkscape::CSSOStringStream os;
+ gchar c[64];
+ sp_svg_write_color (c, 64, rgb);
+ os << "stop-color:" << c << ";stop-opacity:" << (gdouble) alpha <<";";
+ SP_OBJECT_REPR (stop)->setAttribute("style", os.str().c_str());
+ // g_snprintf (c, 256, "stop-color:#%06x;stop-opacity:%g;", rgb >> 8, (gdouble) alpha);
+ //SP_OBJECT_REPR (stop)->setAttribute("style", c);
+
+ sp_document_done (SP_OBJECT_DOCUMENT (ngr));
+
+ blocked = FALSE;
+}
diff --git a/src/widgets/gradient-vector.h b/src/widgets/gradient-vector.h
new file mode 100644
index 000000000..9d754b8a0
--- /dev/null
+++ b/src/widgets/gradient-vector.h
@@ -0,0 +1,61 @@
+#ifndef __SP_GRADIENT_VECTOR_H__
+#define __SP_GRADIENT_VECTOR_H__
+
+/*
+ * Gradient vector selection widget
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib.h>
+
+
+
+#include <gtk/gtkvbox.h>
+#include "../forward.h"
+
+#define SP_TYPE_GRADIENT_VECTOR_SELECTOR (sp_gradient_vector_selector_get_type ())
+#define SP_GRADIENT_VECTOR_SELECTOR(o) (GTK_CHECK_CAST ((o), SP_TYPE_GRADIENT_VECTOR_SELECTOR, SPGradientVectorSelector))
+#define SP_GRADIENT_VECTOR_SELECTOR_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_GRADIENT_VECTOR_SELECTOR, SPGradientVectorSelectorClass))
+#define SP_IS_GRADIENT_VECTOR_SELECTOR(o) (GTK_CHECK_TYPE ((o), SP_TYPE_GRADIENT_VECTOR_SELECTOR))
+#define SP_IS_GRADIENT_VECTOR_SELECTOR_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_GRADIENT_VECTOR_SELECTOR))
+
+struct SPGradientVectorSelector {
+ GtkVBox vbox;
+
+ guint idlabel : 1;
+
+ SPDocument *doc;
+ SPGradient *gr;
+
+ /* Vector menu */
+ GtkWidget *menu;
+};
+
+struct SPGradientVectorSelectorClass {
+ GtkVBoxClass parent_class;
+
+ void (* vector_set) (SPGradientVectorSelector *gvs, SPGradient *gr);
+};
+
+GtkType sp_gradient_vector_selector_get_type (void);
+
+GtkWidget *sp_gradient_vector_selector_new (SPDocument *doc, SPGradient *gradient);
+
+void sp_gradient_vector_selector_set_gradient (SPGradientVectorSelector *gvs, SPDocument *doc, SPGradient *gr);
+
+SPDocument *sp_gradient_vector_selector_get_document (SPGradientVectorSelector *gvs);
+SPGradient *sp_gradient_vector_selector_get_gradient (SPGradientVectorSelector *gvs);
+
+/* fixme: rethink this (Lauris) */
+GtkWidget *sp_gradient_vector_editor_new (SPGradient *gradient, SPStop *stop = NULL);
+
+
+
+#endif
diff --git a/src/widgets/icon.cpp b/src/widgets/icon.cpp
new file mode 100644
index 000000000..32ebb4c57
--- /dev/null
+++ b/src/widgets/icon.cpp
@@ -0,0 +1,924 @@
+/** \file
+ * SPIcon: Generic icon widget
+ */
+/*
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "path-prefix.h"
+
+
+
+#include <gtk/gtkiconfactory.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtkimage.h>
+
+#include <gtkmm/image.h>
+
+#include "prefs-utils.h"
+#include "inkscape.h"
+#include "document.h"
+#include "sp-item.h"
+#include "display/nr-arena.h"
+#include "display/nr-arena-item.h"
+#include "io/sys.h"
+
+#include "icon.h"
+
+static gboolean icon_prerender_task(gpointer data);
+
+static void addPreRender( GtkIconSize lsize, gchar const *name );
+
+static void sp_icon_class_init(SPIconClass *klass);
+static void sp_icon_init(SPIcon *icon);
+static void sp_icon_destroy(GtkObject *object);
+
+static void sp_icon_reset(SPIcon *icon);
+static void sp_icon_clear(SPIcon *icon);
+
+static void sp_icon_size_request(GtkWidget *widget, GtkRequisition *requisition);
+static void sp_icon_size_allocate(GtkWidget *widget, GtkAllocation *allocation);
+static int sp_icon_expose(GtkWidget *widget, GdkEventExpose *event);
+
+static void sp_icon_paint(SPIcon *icon, GdkRectangle const *area);
+
+static void sp_icon_screen_changed( GtkWidget *widget, GdkScreen *previous_screen );
+static void sp_icon_style_set( GtkWidget *widget, GtkStyle *previous_style );
+static void sp_icon_theme_changed( SPIcon *icon );
+
+static guchar *sp_icon_image_load_pixmap(gchar const *name, unsigned lsize, unsigned psize);
+static guchar *sp_icon_image_load_svg(gchar const *name, unsigned lsize, unsigned psize);
+
+static guchar *sp_icon_image_load(SPIcon *icon, gchar const *name);
+
+static int sp_icon_get_phys_size(int size);
+
+static void sp_icon_overlay_pixels( guchar *px, int width, int height, int stride,
+ unsigned r, unsigned g, unsigned b );
+
+static GtkWidgetClass *parent_class;
+
+static bool sizeDirty = true;
+
+GtkType
+sp_icon_get_type()
+{
+ static GtkType type = 0;
+ if (!type) {
+ GtkTypeInfo info = {
+ "SPIcon",
+ sizeof(SPIcon),
+ sizeof(SPIconClass),
+ (GtkClassInitFunc) sp_icon_class_init,
+ (GtkObjectInitFunc) sp_icon_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique(GTK_TYPE_WIDGET, &info);
+ }
+ return type;
+}
+
+static void
+sp_icon_class_init(SPIconClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ parent_class = (GtkWidgetClass*)g_type_class_peek_parent(klass);
+
+ object_class->destroy = sp_icon_destroy;
+
+ widget_class->size_request = sp_icon_size_request;
+ widget_class->size_allocate = sp_icon_size_allocate;
+ widget_class->expose_event = sp_icon_expose;
+ widget_class->screen_changed = sp_icon_screen_changed;
+ widget_class->style_set = sp_icon_style_set;
+}
+
+
+static void
+sp_icon_init(SPIcon *icon)
+{
+ GTK_WIDGET_FLAGS(icon) |= GTK_NO_WINDOW;
+ icon->lsize = GTK_ICON_SIZE_BUTTON;
+ icon->psize = 0;
+ icon->name = 0;
+ icon->pb = 0;
+ icon->pb_faded = 0;
+}
+
+static void
+sp_icon_destroy(GtkObject *object)
+{
+ SPIcon *icon = SP_ICON(object);
+ sp_icon_clear(icon);
+ if ( icon->name ) {
+ g_free( icon->name );
+ icon->name = 0;
+ }
+
+ ((GtkObjectClass *) (parent_class))->destroy(object);
+}
+
+static void sp_icon_reset( SPIcon *icon ) {
+ icon->psize = 0;
+ sp_icon_clear(icon);
+}
+
+static void sp_icon_clear( SPIcon *icon ) {
+ if (icon->pb) {
+ g_object_unref(G_OBJECT(icon->pb));
+ icon->pb = NULL;
+ }
+ if (icon->pb_faded) {
+ g_object_unref(G_OBJECT(icon->pb_faded));
+ icon->pb_faded = NULL;
+ }
+}
+
+static void
+sp_icon_size_request(GtkWidget *widget, GtkRequisition *requisition)
+{
+ SPIcon const *icon = SP_ICON(widget);
+
+ int const size = ( icon->psize
+ ? icon->psize
+ : sp_icon_get_phys_size(icon->lsize) );
+ requisition->width = size;
+ requisition->height = size;
+}
+
+static void
+sp_icon_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+ widget->allocation = *allocation;
+
+ if (GTK_WIDGET_DRAWABLE(widget)) {
+ gtk_widget_queue_draw(widget);
+ }
+}
+
+static int sp_icon_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+ if ( GTK_WIDGET_DRAWABLE(widget) ) {
+ SPIcon *icon = SP_ICON(widget);
+ if ( !icon->pb ) {
+ guchar *pixels = 0;
+
+ icon->psize = sp_icon_get_phys_size(icon->lsize);
+
+ pixels = sp_icon_image_load( icon, icon->name );
+
+ if (pixels) {
+ // don't pass the nr_free because we're caching the pixel
+ // space loaded through ...
+ // I just changed this. make sure sp_icon_image_load still does the right thing.
+ icon->pb = gdk_pixbuf_new_from_data(pixels, GDK_COLORSPACE_RGB, TRUE, 8,
+ icon->psize, icon->psize, icon->psize * 4,
+ /*(GdkPixbufDestroyNotify)nr_free*/NULL, NULL);
+ icon->pb_faded = gdk_pixbuf_copy(icon->pb);
+
+ pixels = gdk_pixbuf_get_pixels(icon->pb_faded);
+ size_t stride = gdk_pixbuf_get_rowstride(icon->pb_faded);
+ pixels += 3; // alpha
+ for ( int row = 0 ; row < icon->psize ; row++ ) {
+ guchar *row_pixels = pixels;
+ for ( int column = 0 ; column < icon->psize ; column++ ) {
+ *row_pixels = *row_pixels >> 1;
+ row_pixels += 4;
+ }
+ pixels += stride;
+ }
+ } else {
+ /* TODO: We should do something more useful if we can't load the image. */
+ g_warning ("failed to load icon '%s'", icon->name);
+ }
+ }
+
+ sp_icon_paint(SP_ICON(widget), &event->area);
+ }
+
+ return TRUE;
+}
+
+
+static void sp_icon_screen_changed( GtkWidget *widget, GdkScreen *previous_screen )
+{
+ if ( GTK_WIDGET_CLASS( parent_class )->screen_changed ) {
+ GTK_WIDGET_CLASS( parent_class )->screen_changed( widget, previous_screen );
+ }
+ SPIcon *icon = SP_ICON(widget);
+ sp_icon_theme_changed(icon);
+}
+
+static void sp_icon_style_set( GtkWidget *widget, GtkStyle *previous_style )
+{
+ if ( GTK_WIDGET_CLASS( parent_class )->style_set ) {
+ GTK_WIDGET_CLASS( parent_class )->style_set( widget, previous_style );
+ }
+ SPIcon *icon = SP_ICON(widget);
+ sp_icon_theme_changed(icon);
+}
+
+static void sp_icon_theme_changed( SPIcon *icon )
+{
+ //g_message("Got a change bump for this icon");
+ sizeDirty = true;
+ sp_icon_reset(icon);
+ gtk_widget_queue_draw( GTK_WIDGET(icon) );
+}
+
+
+static GtkWidget *
+sp_icon_new_full( GtkIconSize lsize, gchar const *name )
+{
+ static gint dump = prefs_get_int_attribute_limited( "debug.icons", "dumpGtk", 0, 0, 1 );
+ static gint fallback = prefs_get_int_attribute_limited( "debug.icons", "checkNames", 0, 0, 1 );
+
+ addPreRender( lsize, name );
+
+ GtkStockItem stock;
+ gboolean tryLoad = gtk_stock_lookup( name, &stock );
+ if ( !tryLoad && fallback ) {
+ tryLoad |= strncmp("gtk-", name, 4 ) == 0;
+ }
+ if ( !tryLoad && fallback ) {
+ tryLoad |= strncmp("gnome-", name, 6 ) == 0;
+ }
+
+ GtkWidget *widget = 0;
+ if ( tryLoad ) {
+ GtkWidget *img = gtk_image_new_from_stock( name, lsize );
+ if ( img ) {
+ GtkImageType type = gtk_image_get_storage_type( GTK_IMAGE(img) );
+ if ( type == GTK_IMAGE_STOCK ) {
+ widget = GTK_WIDGET(img);
+ img = 0;
+
+ if ( dump ) {
+ g_message( "loaded gtk '%s' %d (GTK_IMAGE_STOCK)", name, lsize );
+ }
+ } else {
+ if ( dump ) {
+ g_message( "skipped gtk '%s' %d (not GTK_IMAGE_STOCK)", name, lsize );
+ }
+ g_object_unref( (GObject *)img );
+ img = 0;
+ }
+ }
+ }
+
+ if ( !widget ) {
+ SPIcon *icon = (SPIcon *)g_object_new(SP_TYPE_ICON, NULL);
+ icon->lsize = lsize;
+ icon->name = g_strdup(name);
+ icon->psize = sp_icon_get_phys_size(lsize);
+
+ widget = GTK_WIDGET(icon);
+ }
+
+ return widget;
+}
+
+GtkWidget *
+sp_icon_new( GtkIconSize lsize, gchar const *name )
+{
+ return sp_icon_new_full( lsize, name );
+}
+
+Gtk::Widget *sp_icon_get_icon( Glib::ustring const &oid, GtkIconSize size )
+{
+ Gtk::Widget *result = 0;
+ GtkWidget *widget = sp_icon_new_full( size, oid.c_str() );
+
+ if ( widget ) {
+ if ( GTK_IS_IMAGE(widget) ) {
+ GtkImage *img = GTK_IMAGE(widget);
+ result = Glib::wrap( img );
+ } else {
+ result = Glib::wrap( widget );
+ }
+ }
+
+ return result;
+}
+
+// Try to load the named svg, falling back to pixmaps
+guchar *
+sp_icon_image_load( SPIcon *icon, gchar const *name )
+{
+ guchar *px = sp_icon_image_load_svg( name, icon->lsize, icon->psize );
+ if (!px) {
+ px = sp_icon_image_load_pixmap(name, icon->lsize, icon->psize);
+ }
+
+ return px;
+}
+
+GtkIconSize
+sp_icon_get_gtk_size(int size)
+{
+ static GtkIconSize map[64] = {(GtkIconSize)0};
+ size = CLAMP(size, 4, 63);
+ if (!map[size]) {
+ static int count = 0;
+ char c[64];
+ g_snprintf(c, 64, "InkscapeIcon%d", count++);
+ map[size] = gtk_icon_size_register(c, size, size);
+ }
+ return map[size];
+}
+
+static int sp_icon_get_phys_size(int size)
+{
+ static bool init = false;
+ static int lastSys[GTK_ICON_SIZE_DIALOG + 1];
+ static int vals[GTK_ICON_SIZE_DIALOG + 1];
+
+ size = CLAMP( size, GTK_ICON_SIZE_MENU, GTK_ICON_SIZE_DIALOG );
+
+ if ( sizeDirty && init ) {
+ GtkIconSize const gtkSizes[] = {
+ GTK_ICON_SIZE_MENU,
+ GTK_ICON_SIZE_SMALL_TOOLBAR,
+ GTK_ICON_SIZE_LARGE_TOOLBAR,
+ GTK_ICON_SIZE_BUTTON,
+ GTK_ICON_SIZE_DND,
+ GTK_ICON_SIZE_DIALOG
+ };
+ for (unsigned i = 0; i < G_N_ELEMENTS(gtkSizes) && init; ++i) {
+ unsigned const val_ix(gtkSizes[i]);
+ g_assert( val_ix < G_N_ELEMENTS(vals) );
+
+ gint width = 0;
+ gint height = 0;
+ if ( gtk_icon_size_lookup(gtkSizes[i], &width, &height ) ) {
+ init &= (lastSys[val_ix] == std::max(width, height));
+ }
+ }
+ }
+
+ if ( !init ) {
+ sizeDirty = false;
+ gint dump = prefs_get_int_attribute_limited( "debug.icons", "dumpDefault", 0, 0, 1 );
+ if ( dump ) {
+ g_message( "Default icon sizes:" );
+ }
+ memset( vals, 0, sizeof(vals) );
+ memset( lastSys, 0, sizeof(lastSys) );
+ GtkIconSize const gtkSizes[] = {
+ GTK_ICON_SIZE_MENU,
+ GTK_ICON_SIZE_SMALL_TOOLBAR,
+ GTK_ICON_SIZE_LARGE_TOOLBAR,
+ GTK_ICON_SIZE_BUTTON,
+ GTK_ICON_SIZE_DND,
+ GTK_ICON_SIZE_DIALOG
+ };
+ gchar const *const names[] = {
+ "GTK_ICON_SIZE_MENU",
+ "GTK_ICON_SIZE_SMALL_TOOLBAR",
+ "GTK_ICON_SIZE_LARGE_TOOLBAR",
+ "GTK_ICON_SIZE_BUTTON",
+ "GTK_ICON_SIZE_DND",
+ "GTK_ICON_SIZE_DIALOG"
+ };
+
+ GtkWidget *icon = (GtkWidget *)g_object_new(SP_TYPE_ICON, NULL);
+
+ for (unsigned i = 0; i < G_N_ELEMENTS(gtkSizes); ++i) {
+ unsigned const val_ix(gtkSizes[i]);
+ g_assert( val_ix < G_N_ELEMENTS(vals) );
+
+ gint width = 0;
+ gint height = 0;
+ bool used = false;
+ if ( gtk_icon_size_lookup(gtkSizes[i], &width, &height ) ) {
+ vals[val_ix] = std::max(width, height);
+ lastSys[val_ix] = vals[val_ix];
+ used = true;
+ }
+ if (dump) {
+ g_message(" =-- %u size:%d %c(%d, %d) '%s'",
+ i, gtkSizes[i],
+ ( used ? ' ' : 'X' ), width, height, names[i]);
+ }
+ gchar const *id = GTK_STOCK_OPEN;
+ GdkPixbuf *pb = gtk_widget_render_icon( icon, id, gtkSizes[i], NULL);
+ if (pb) {
+ width = gdk_pixbuf_get_width(pb);
+ height = gdk_pixbuf_get_height(pb);
+ int newSize = std::max( width, height );
+ // TODO perhaps check a few more stock icons to get a range on sizes.
+ if ( newSize > 0 ) {
+ vals[val_ix] = newSize;
+ }
+ if (dump) {
+ g_message(" %u size:%d (%d, %d)", i, gtkSizes[i], width, height);
+ }
+
+ g_object_unref(G_OBJECT(pb));
+ }
+ }
+ //g_object_unref(icon);
+ init = true;
+ }
+
+ return vals[size];
+}
+
+static void sp_icon_paint(SPIcon *icon, GdkRectangle const *area)
+{
+ GtkWidget &widget = *GTK_WIDGET(icon);
+
+ GdkPixbuf *image = GTK_WIDGET_IS_SENSITIVE(&widget) ? icon->pb : icon->pb_faded;
+
+ if (image) {
+ int const padx = ( ( widget.allocation.width > icon->psize )
+ ? ( widget.allocation.width - icon->psize ) / 2
+ : 0 );
+ int const pady = ( ( widget.allocation.height > icon->psize )
+ ? ( widget.allocation.height - icon->psize ) / 2
+ : 0 );
+
+ int const x0 = std::max(area->x, widget.allocation.x + padx);
+ int const y0 = std::max(area->y, widget.allocation.y + pady);
+ int const x1 = std::min(area->x + area->width, widget.allocation.x + padx + static_cast<int>(icon->psize) );
+ int const y1 = std::min(area->y + area->height, widget.allocation.y + pady + static_cast<int>(icon->psize) );
+
+ gdk_draw_pixbuf(GDK_DRAWABLE(widget.window), NULL, image,
+ x0 - widget.allocation.x - padx,
+ y0 - widget.allocation.y - pady,
+ x0, y0,
+ x1 - x0, y1 - y0,
+ GDK_RGB_DITHER_NORMAL, x0, y0);
+ }
+}
+
+static guchar *
+sp_icon_image_load_pixmap(gchar const *name, unsigned lsize, unsigned psize)
+{
+ gchar *path;
+ guchar *px;
+ GdkPixbuf *pb;
+
+ path = (gchar *) g_strdup_printf("%s/%s.png", INKSCAPE_PIXMAPDIR, name);
+ // TODO: bulia, please look over
+ gsize bytesRead = 0;
+ gsize bytesWritten = 0;
+ GError *error = NULL;
+ gchar *localFilename = g_filename_from_utf8( path,
+ -1,
+ &bytesRead,
+ &bytesWritten,
+ &error);
+ pb = gdk_pixbuf_new_from_file(localFilename, NULL);
+ g_free(localFilename);
+ g_free(path);
+ if (!pb) {
+ path = (gchar *) g_strdup_printf("%s/%s.xpm", INKSCAPE_PIXMAPDIR, name);
+ // TODO: bulia, please look over
+ gsize bytesRead = 0;
+ gsize bytesWritten = 0;
+ GError *error = NULL;
+ gchar *localFilename = g_filename_from_utf8( path,
+ -1,
+ &bytesRead,
+ &bytesWritten,
+ &error);
+ pb = gdk_pixbuf_new_from_file(localFilename, NULL);
+ g_free(localFilename);
+ g_free(path);
+ }
+ if (pb) {
+ if (!gdk_pixbuf_get_has_alpha(pb))
+ gdk_pixbuf_add_alpha(pb, FALSE, 0, 0, 0);
+ if ( ( static_cast<unsigned>(gdk_pixbuf_get_width(pb)) != psize )
+ || ( static_cast<unsigned>(gdk_pixbuf_get_height(pb)) != psize ) ) {
+ GdkPixbuf *spb = gdk_pixbuf_scale_simple(pb, psize, psize, GDK_INTERP_HYPER);
+ g_object_unref(G_OBJECT(pb));
+ pb = spb;
+ }
+ guchar *spx = gdk_pixbuf_get_pixels(pb);
+ int srs = gdk_pixbuf_get_rowstride(pb);
+ px = nr_new(guchar, 4 * psize * psize);
+ for (unsigned y = 0; y < psize; y++) {
+ memcpy(px + 4 * y * psize, spx + y * srs, 4 * psize);
+ }
+ g_object_unref(G_OBJECT(pb));
+
+ return px;
+ }
+
+ return NULL;
+}
+
+// takes doc, root, icon, and icon name to produce pixels
+extern "C" guchar *
+sp_icon_doc_icon( SPDocument *doc, NRArenaItem *root,
+ gchar const *name, unsigned psize )
+{
+ gint const dump = prefs_get_int_attribute_limited( "debug.icons", "dumpSvg", 0, 0, 1 );
+ guchar *px = NULL;
+
+ if (doc) {
+ SPObject *object = doc->getObjectById(name);
+ if (object && SP_IS_ITEM(object)) {
+ /* Find bbox in document */
+ NR::Matrix const i2doc(sp_item_i2doc_affine(SP_ITEM(object)));
+ NR::Rect dbox = SP_ITEM(object)->invokeBbox(i2doc);
+
+ if ( SP_OBJECT_PARENT(object) == NULL )
+ {
+ dbox = NR::Rect(NR::Point(0, 0),
+ NR::Point(sp_document_width(doc), sp_document_height(doc)));
+ }
+
+ /* This is in document coordinates, i.e. pixels */
+ if (dbox.isEmpty() == false) {
+ NRGC gc(NULL);
+ /* Update to renderable state */
+ double sf = 1.0;
+ NRMatrix t;
+ nr_matrix_set_scale(&t, sf, sf);
+ nr_arena_item_set_transform(root, &t);
+ nr_matrix_set_identity(&gc.transform);
+ nr_arena_item_invoke_update( root, NULL, &gc,
+ NR_ARENA_ITEM_STATE_ALL,
+ NR_ARENA_ITEM_STATE_NONE );
+ /* Item integer bbox in points */
+ NRRectL ibox;
+ ibox.x0 = (int) floor(sf * dbox.min()[NR::X] + 0.5);
+ ibox.y0 = (int) floor(sf * dbox.min()[NR::Y] + 0.5);
+ ibox.x1 = (int) floor(sf * dbox.max()[NR::X] + 0.5);
+ ibox.y1 = (int) floor(sf * dbox.max()[NR::Y] + 0.5);
+
+ if ( dump ) {
+ g_message( " box --'%s' (%f,%f)-(%f,%f)", name, (double)ibox.x0, (double)ibox.y0, (double)ibox.x1, (double)ibox.y1 );
+ }
+
+ /* Find button visible area */
+ int width = ibox.x1 - ibox.x0;
+ int height = ibox.y1 - ibox.y0;
+
+ if ( dump ) {
+ g_message( " vis --'%s' (%d,%d)", name, width, height );
+ }
+
+ {
+ int block = std::max(width, height);
+ if (block != static_cast<int>(psize) ) {
+ if ( dump ) {
+ g_message(" resizing" );
+ }
+ sf = (double)psize / (double)block;
+
+ nr_matrix_set_scale(&t, sf, sf);
+ nr_arena_item_set_transform(root, &t);
+ nr_matrix_set_identity(&gc.transform);
+ nr_arena_item_invoke_update( root, NULL, &gc,
+ NR_ARENA_ITEM_STATE_ALL,
+ NR_ARENA_ITEM_STATE_NONE );
+ /* Item integer bbox in points */
+ ibox.x0 = (int) floor(sf * dbox.min()[NR::X] + 0.5);
+ ibox.y0 = (int) floor(sf * dbox.min()[NR::Y] + 0.5);
+ ibox.x1 = (int) floor(sf * dbox.max()[NR::X] + 0.5);
+ ibox.y1 = (int) floor(sf * dbox.max()[NR::Y] + 0.5);
+
+ if ( dump ) {
+ g_message( " box2 --'%s' (%f,%f)-(%f,%f)", name, (double)ibox.x0, (double)ibox.y0, (double)ibox.x1, (double)ibox.y1 );
+ }
+
+ /* Find button visible area */
+ width = ibox.x1 - ibox.x0;
+ height = ibox.y1 - ibox.y0;
+ if ( dump ) {
+ g_message( " vis2 --'%s' (%d,%d)", name, width, height );
+ }
+ }
+ }
+
+ int dx, dy;
+ //dx = (psize - width) / 2;
+ //dy = (psize - height) / 2;
+ dx=dy=psize;
+ dx=(dx-width)/2; // watch out for psize, since 'unsigned'-'signed' can cause problems if the result is negative
+ dy=(dy-height)/2;
+ NRRectL area;
+ area.x0 = ibox.x0 - dx;
+ area.y0 = ibox.y0 - dy;
+ area.x1 = area.x0 + psize;
+ area.y1 = area.y0 + psize;
+ /* Actual renderable area */
+ NRRectL ua;
+ ua.x0 = MAX(ibox.x0, area.x0);
+ ua.y0 = MAX(ibox.y0, area.y0);
+ ua.x1 = MIN(ibox.x1, area.x1);
+ ua.y1 = MIN(ibox.y1, area.y1);
+
+ if ( dump ) {
+ g_message( " area --'%s' (%f,%f)-(%f,%f)", name, (double)area.x0, (double)area.y0, (double)area.x1, (double)area.y1 );
+ g_message( " ua --'%s' (%f,%f)-(%f,%f)", name, (double)ua.x0, (double)ua.y0, (double)ua.x1, (double)ua.y1 );
+ }
+ /* Set up pixblock */
+ px = nr_new(guchar, 4 * psize * psize);
+ memset(px, 0x00, 4 * psize * psize);
+ /* Render */
+ NRPixBlock B;
+ nr_pixblock_setup_extern( &B, NR_PIXBLOCK_MODE_R8G8B8A8N,
+ ua.x0, ua.y0, ua.x1, ua.y1,
+ px + 4 * psize * (ua.y0 - area.y0) +
+ 4 * (ua.x0 - area.x0),
+ 4 * psize, FALSE, FALSE );
+ nr_arena_item_invoke_render( root, &ua, &B,
+ NR_ARENA_ITEM_RENDER_NO_CACHE );
+ nr_pixblock_release(&B);
+
+ gint useOverlay = prefs_get_int_attribute_limited( "debug.icons", "overlaySvg", 0, 0, 1 );
+ if ( useOverlay ) {
+ sp_icon_overlay_pixels( px, psize, psize, 4 * psize, 0x00, 0x00, 0xff );
+ }
+ }
+ }
+ }
+
+ return px;
+} // end of sp_icon_doc_icon()
+
+
+
+struct svg_doc_cache_t
+{
+ SPDocument *doc;
+ NRArenaItem *root;
+};
+
+static std::map<Glib::ustring, svg_doc_cache_t *> doc_cache;
+static std::map<Glib::ustring, guchar *> px_cache;
+
+static Glib::ustring icon_cache_key(gchar const *name,
+ unsigned lsize, unsigned psize)
+{
+ Glib::ustring key=name;
+ key += ":";
+ key += lsize;
+ key += ":";
+ key += psize;
+ return key;
+}
+
+static guchar *get_cached_pixels(Glib::ustring const &key) {
+ std::map<Glib::ustring, guchar *>::iterator found=px_cache.find(key);
+ if ( found != px_cache.end() ) {
+ return found->second;
+ }
+ return NULL;
+}
+
+static guchar *load_svg_pixels(gchar const *name,
+ unsigned lsize, unsigned psize)
+{
+ SPDocument *doc = NULL;
+ NRArenaItem *root = NULL;
+ svg_doc_cache_t *info = NULL;
+
+ // Fall back from user prefs dir into system locations.
+ Glib::ustring iconsvg = name;
+ iconsvg += ".svg";
+ std::list<gchar *> sources;
+ sources.push_back(g_build_filename(profile_path("icons"),iconsvg.c_str(), NULL));
+ sources.push_back(g_build_filename(profile_path("icons"),"icons.svg", NULL));
+ sources.push_back(g_build_filename(INKSCAPE_PIXMAPDIR, iconsvg.c_str(), NULL));
+ sources.push_back(g_build_filename(INKSCAPE_PIXMAPDIR, "icons.svg", NULL));
+
+ // Try each document in turn until we successfully load the icon from one
+ guchar *px=NULL;
+ while ( !sources.empty() && !px ) {
+ gchar *doc_filename = sources.front();
+
+ // Did we already load this doc?
+ Glib::ustring key(doc_filename);
+ info = 0;
+ {
+ std::map<Glib::ustring, svg_doc_cache_t *>::iterator i = doc_cache.find(key);
+ if ( i != doc_cache.end() ) {
+ info = i->second;
+ }
+ }
+
+ /* Try to load from document. */
+ if (!info &&
+ Inkscape::IO::file_test( doc_filename, G_FILE_TEST_IS_REGULAR ) &&
+ (doc = sp_document_new( doc_filename, FALSE )) ) {
+
+ // prep the document
+ sp_document_ensure_up_to_date(doc);
+ /* Create new arena */
+ NRArena *arena = NRArena::create();
+ /* Create ArenaItem and set transform */
+ unsigned visionkey = sp_item_display_key_new(1);
+ /* fixme: Memory manage root if needed (Lauris) */
+ root = sp_item_invoke_show( SP_ITEM(SP_DOCUMENT_ROOT(doc)),
+ arena, visionkey, SP_ITEM_SHOW_DISPLAY );
+
+ // store into the cache
+ info = new svg_doc_cache_t;
+ g_assert(info);
+
+ info->doc=doc;
+ info->root=root;
+ doc_cache[key]=info;
+ }
+ if (info) {
+ doc=info->doc;
+ root=info->root;
+ }
+
+ // toss the filename
+ g_free(doc_filename);
+ sources.pop_front();
+
+ // move on to the next document if we couldn't get anything
+ if (!info && !doc) continue;
+
+ px = sp_icon_doc_icon( doc, root, name, psize );
+ }
+
+ return px;
+}
+
+// returns true if icon needed preloading, false if nothing was done
+static bool prerender_icon(gchar const *name, unsigned lsize, unsigned psize)
+{
+ Glib::ustring key=icon_cache_key(name, lsize, psize);
+ guchar *px=get_cached_pixels(key);
+ if (px) {
+ return false;
+ } else {
+ px = load_svg_pixels(name, lsize, psize);
+ if (px) {
+ px_cache[key] = px;
+ }
+ return true;
+ }
+}
+
+static guchar *
+sp_icon_image_load_svg(gchar const *name, unsigned lsize, unsigned psize)
+{
+ Glib::ustring key=icon_cache_key(name, lsize, psize);
+
+ // did we already load this icon at this scale/size?
+ guchar *px=get_cached_pixels(key);
+ if (!px) {
+ px = load_svg_pixels(name, lsize, psize);
+ if (px) {
+ px_cache[key] = px;
+ }
+ }
+ return px;
+}
+
+void sp_icon_overlay_pixels(guchar *px, int width, int height, int stride,
+ unsigned r, unsigned g, unsigned b)
+{
+ for ( int y = 0; y < height; y += 4 ) {
+ guchar *ptr = px + y * stride;
+ for ( int x = 0; x < width; x += 4 ) {
+ *(ptr++) = r;
+ *(ptr++) = g;
+ *(ptr++) = b;
+ *(ptr++) = 0xff;
+
+ ptr += 4 * 3;
+ }
+ }
+
+ if ( width > 1 && height > 1 ) {
+ // point at the last pixel
+ guchar *ptr = px + ((height-1) * stride) + ((width - 1) * 4);
+
+ if ( width > 2 ) {
+ px[4] = r;
+ px[5] = g;
+ px[6] = b;
+ px[7] = 0xff;
+
+ ptr[-12] = r;
+ ptr[-11] = g;
+ ptr[-10] = b;
+ ptr[-9] = 0xff;
+ }
+
+ ptr[-4] = r;
+ ptr[-3] = g;
+ ptr[-2] = b;
+ ptr[-1] = 0xff;
+
+ px[0 + stride] = r;
+ px[1 + stride] = g;
+ px[2 + stride] = b;
+ px[3 + stride] = 0xff;
+
+ ptr[0 - stride] = r;
+ ptr[1 - stride] = g;
+ ptr[2 - stride] = b;
+ ptr[3 - stride] = 0xff;
+
+ if ( height > 2 ) {
+ ptr[0 - stride * 3] = r;
+ ptr[1 - stride * 3] = g;
+ ptr[2 - stride * 3] = b;
+ ptr[3 - stride * 3] = 0xff;
+ }
+ }
+}
+
+class preRenderItem
+{
+public:
+ preRenderItem( GtkIconSize lsize, gchar const *name ) :
+ _lsize( lsize ),
+ _name( name )
+ {}
+ GtkIconSize _lsize;
+ Glib::ustring _name;
+};
+
+
+#include <queue>
+
+static std::queue<preRenderItem> pendingRenders;
+static bool callbackHooked = false;
+static Glib::Timer *prerender_timer=NULL;
+
+static void addPreRender( GtkIconSize lsize, gchar const *name )
+{
+
+ if ( !callbackHooked )
+ {
+ g_message("Beginning icon prerendering");
+ prerender_timer = new Glib::Timer();
+ callbackHooked = true;
+ g_idle_add_full( G_PRIORITY_LOW, &icon_prerender_task, NULL, NULL );
+ }
+
+ pendingRenders.push(preRenderItem(lsize, name));
+}
+
+// in seconds; 10msec is roughly the threshold for human-perceptible lag,
+// but up to 60-70msec is tolerable
+#define INTERACTIVE_LIMIT 0.07
+
+static inline int seconds_to_msec(double seconds) {
+ return (int)(seconds * 1000 + 0.5);
+}
+
+gboolean icon_prerender_task(gpointer data) {
+ Glib::Timer timer;
+
+ if (!pendingRenders.empty()) {
+ preRenderItem single=pendingRenders.front();
+ pendingRenders.pop();
+ int psize = sp_icon_get_phys_size(single._lsize);
+ prerender_icon(single._name.c_str(), single._lsize, psize);
+
+ double elapsed=timer.elapsed();
+ if ( elapsed > INTERACTIVE_LIMIT ) {
+ g_warning("Prerendering of icon \"%s\" at %dx%d pixels exceeded %dmsec (%dmsec)", single._name.c_str(), psize, psize, seconds_to_msec(INTERACTIVE_LIMIT), seconds_to_msec(elapsed));
+ }
+ }
+
+ if (!pendingRenders.empty()) {
+ return TRUE;
+ } else {
+ prerender_timer->stop();
+ g_message("Icon prerendering complete after %g seconds", prerender_timer->elapsed());
+ delete prerender_timer;
+ prerender_timer = NULL;
+ callbackHooked = false;
+ return FALSE;
+ }
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/icon.h b/src/widgets/icon.h
new file mode 100644
index 000000000..2ed165f7e
--- /dev/null
+++ b/src/widgets/icon.h
@@ -0,0 +1,51 @@
+#ifndef SEEN_SP_ICON_H
+#define SEEN_SP_ICON_H
+
+/*
+ * Generic icon widget
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib.h>
+
+
+
+#define SP_TYPE_ICON (sp_icon_get_type ())
+#define SP_ICON(o) (GTK_CHECK_CAST ((o), SP_TYPE_ICON, SPIcon))
+#define SP_IS_ICON(o) (GTK_CHECK_TYPE ((o), SP_TYPE_ICON))
+
+#include <gtk/gtkwidget.h>
+
+struct SPIcon {
+ GtkWidget widget;
+
+ GtkIconSize lsize;
+ int psize;
+ gchar *name;
+
+ GdkPixbuf *pb;
+ GdkPixbuf *pb_faded;
+};
+
+struct SPIconClass {
+ GtkWidgetClass parent_class;
+};
+
+GType sp_icon_get_type (void);
+
+GtkWidget *sp_icon_new( GtkIconSize size, const gchar *name );
+
+#include <glibmm/ustring.h>
+#include <gtkmm/widget.h>
+
+// Might return a wrapped SPIcon, or Gtk::Image
+Gtk::Widget *sp_icon_get_icon( const Glib::ustring &oid, GtkIconSize size = GTK_ICON_SIZE_BUTTON );
+
+
+#endif // SEEN_SP_ICON_H
diff --git a/src/widgets/layer-selector.cpp b/src/widgets/layer-selector.cpp
new file mode 100644
index 000000000..34433e9b7
--- /dev/null
+++ b/src/widgets/layer-selector.cpp
@@ -0,0 +1,588 @@
+/*
+ * Inkscape::Widgets::LayerSelector - layer selector widget
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glibmm/i18n.h>
+
+#include "desktop-handles.h"
+
+#include "widgets/layer-selector.h"
+#include "widgets/shrink-wrap-button.h"
+#include "widgets/icon.h"
+
+#include "util/reverse-list.h"
+#include "util/filter-list.h"
+
+#include "sp-item.h"
+#include "desktop.h"
+#include "document.h"
+#include "dialogs/layer-properties.h"
+#include "xml/node-event-vector.h"
+
+namespace Inkscape {
+namespace Widgets {
+
+namespace {
+
+class AlternateIcons : public Gtk::HBox {
+public:
+ AlternateIcons(GtkIconSize size, gchar const *a, gchar const *b)
+ : _a(NULL), _b(NULL)
+ {
+ if (a) {
+ _a = Gtk::manage(sp_icon_get_icon(a, size));
+ _a->set_no_show_all(true);
+ add(*_a);
+ }
+ if (b) {
+ _b = Gtk::manage(sp_icon_get_icon(b,size));
+ _b->set_no_show_all(true);
+ add(*_b);
+ }
+ setState(false);
+ }
+
+ bool state() const { return _state; }
+ void setState(bool state) {
+ _state = state;
+ if (_state) {
+ if (_a) {
+ _a->hide();
+ }
+ if (_b) {
+ _b->show();
+ }
+ } else {
+ if (_a) {
+ _a->show();
+ }
+ if (_b) {
+ _b->hide();
+ }
+ }
+ }
+
+private:
+ Gtk::Widget *_a;
+ Gtk::Widget *_b;
+ bool _state;
+};
+
+}
+
+/** LayerSelector constructor. Creates lock and hide buttons,
+ * initalizes the layer dropdown selector with a label renderer,
+ * and hooks up signal for setting the desktop layer when the
+ * selector is changed.
+ */
+LayerSelector::LayerSelector(SPDesktop *desktop)
+: _desktop(NULL), _layer(NULL)
+{
+ AlternateIcons *label;
+
+ label = Gtk::manage(new AlternateIcons(GTK_ICON_SIZE_MENU, "visible", "hidden"));
+ _visibility_toggle.add(*label);
+ _visibility_toggle.signal_toggled().connect(
+ sigc::compose(
+ sigc::mem_fun(*label, &AlternateIcons::setState),
+ sigc::mem_fun(_visibility_toggle, &Gtk::ToggleButton::get_active)
+ )
+ );
+ _visibility_toggled_connection = _visibility_toggle.signal_toggled().connect(
+ sigc::compose(
+ sigc::mem_fun(*this, &LayerSelector::_hideLayer),
+ sigc::mem_fun(_visibility_toggle, &Gtk::ToggleButton::get_active)
+ )
+ );
+
+ _visibility_toggle.set_relief(Gtk::RELIEF_NONE);
+ shrink_wrap_button(_visibility_toggle);
+ _tooltips.set_tip(_visibility_toggle, _("Toggle current layer visibility"));
+ pack_start(_visibility_toggle, Gtk::PACK_EXPAND_PADDING);
+
+ label = Gtk::manage(new AlternateIcons(GTK_ICON_SIZE_MENU, "lock_unlocked", "width_height_lock"));
+ _lock_toggle.add(*label);
+ _lock_toggle.signal_toggled().connect(
+ sigc::compose(
+ sigc::mem_fun(*label, &AlternateIcons::setState),
+ sigc::mem_fun(_lock_toggle, &Gtk::ToggleButton::get_active)
+ )
+ );
+ _lock_toggled_connection = _lock_toggle.signal_toggled().connect(
+ sigc::compose(
+ sigc::mem_fun(*this, &LayerSelector::_lockLayer),
+ sigc::mem_fun(_lock_toggle, &Gtk::ToggleButton::get_active)
+ )
+ );
+
+ _lock_toggle.set_relief(Gtk::RELIEF_NONE);
+ shrink_wrap_button(_lock_toggle);
+ _tooltips.set_tip(_lock_toggle, _("Lock or unlock current layer"));
+ pack_start(_lock_toggle, Gtk::PACK_EXPAND_PADDING);
+
+ _tooltips.set_tip(_selector, _("Current layer"));
+ pack_start(_selector, Gtk::PACK_EXPAND_WIDGET);
+
+ _layer_model = Gtk::ListStore::create(_model_columns);
+ _selector.set_model(_layer_model);
+ _selector.pack_start(_label_renderer);
+ _selector.set_cell_data_func(
+ _label_renderer,
+ sigc::mem_fun(*this, &LayerSelector::_prepareLabelRenderer)
+ );
+
+ _selection_changed_connection = _selector.signal_changed().connect(
+ sigc::mem_fun(*this, &LayerSelector::_setDesktopLayer)
+ );
+ setDesktop(desktop);
+}
+
+/** Destructor - disconnects signal handler
+ */
+LayerSelector::~LayerSelector() {
+ setDesktop(NULL);
+ _selection_changed_connection.disconnect();
+}
+
+namespace {
+
+/** Helper function - detaches desktop from selector
+ */
+bool detach(LayerSelector *selector) {
+ selector->setDesktop(NULL);
+ return FALSE;
+}
+
+}
+
+/** Sets the desktop for the widget. First disconnects signals
+ * for the current desktop, then stores the pointer to the
+ * given \a desktop, and attaches its signals to this one.
+ * Then it selects the current layer for the desktop.
+ */
+void LayerSelector::setDesktop(SPDesktop *desktop) {
+ if ( desktop == _desktop ) {
+ return;
+ }
+
+ if (_desktop) {
+// _desktop_shutdown_connection.disconnect();
+ _layer_changed_connection.disconnect();
+// g_signal_handlers_disconnect_by_func(_desktop, (gpointer)&detach, this);
+ }
+ _desktop = desktop;
+ if (_desktop) {
+ // TODO we need a different signal for this, really..s
+// _desktop_shutdown_connection = _desktop->connectShutdown(
+// sigc::bind (sigc::ptr_fun (detach), this));
+// g_signal_connect_after(_desktop, "shutdown", GCallback(detach), this);
+
+ _layer_changed_connection = _desktop->connectCurrentLayerChanged(
+ sigc::mem_fun(*this, &LayerSelector::_selectLayer)
+ );
+ _selectLayer(_desktop->currentLayer());
+ }
+}
+
+namespace {
+
+class is_layer {
+public:
+ is_layer(SPDesktop *desktop) : _desktop(desktop) {}
+ bool operator()(SPObject &object) const {
+ return _desktop->isLayer(&object);
+ }
+private:
+ SPDesktop *_desktop;
+};
+
+class column_matches_object {
+public:
+ column_matches_object(Gtk::TreeModelColumn<SPObject *> const &column,
+ SPObject &object)
+ : _column(column), _object(object) {}
+ bool operator()(Gtk::TreeModel::const_iterator const &iter) const {
+ SPObject *current=(*iter)[_column];
+ return current == &_object;
+ }
+private:
+ Gtk::TreeModelColumn<SPObject *> const &_column;
+ SPObject &_object;
+};
+
+}
+
+/** Selects the given layer in the dropdown selector.
+ */
+void LayerSelector::_selectLayer(SPObject *layer) {
+ using Inkscape::Util::List;
+ using Inkscape::Util::cons;
+ using Inkscape::Util::reverse_list;
+
+ _selection_changed_connection.block();
+
+ while (!_layer_model->children().empty()) {
+ Gtk::ListStore::iterator first_row(_layer_model->children().begin());
+ _destroyEntry(first_row);
+ _layer_model->erase(first_row);
+ }
+
+ SPObject *root=_desktop->currentRoot();
+
+ if (_layer) {
+ sp_object_unref(_layer, NULL);
+ _layer = NULL;
+ }
+
+ if (layer) {
+ List<SPObject &> hierarchy=reverse_list<SPObject::ParentIterator>(layer, root);
+ if ( layer == root ) {
+ _buildEntries(0, cons(*root, hierarchy));
+ } else if (hierarchy) {
+ _buildSiblingEntries(0, *root, hierarchy);
+ }
+
+ Gtk::TreeIter row(
+ std::find_if(
+ _layer_model->children().begin(),
+ _layer_model->children().end(),
+ column_matches_object(_model_columns.object, *layer)
+ )
+ );
+ if ( row != _layer_model->children().end() ) {
+ _selector.set_active(row);
+ }
+
+ _layer = layer;
+ sp_object_ref(_layer, NULL);
+ }
+
+ if ( !layer || layer == root ) {
+ _visibility_toggle.set_sensitive(false);
+ _visibility_toggle.set_active(false);
+ _lock_toggle.set_sensitive(false);
+ _lock_toggle.set_active(false);
+ } else {
+ _visibility_toggle.set_sensitive(true);
+ _visibility_toggle.set_active(( SP_IS_ITEM(layer) ? SP_ITEM(layer)->isHidden() : false ));
+ _lock_toggle.set_sensitive(true);
+ _lock_toggle.set_active(( SP_IS_ITEM(layer) ? SP_ITEM(layer)->isLocked() : false ));
+ }
+
+ _selection_changed_connection.unblock();
+}
+
+/** Sets the current desktop layer to the actively selected layer.
+ */
+void LayerSelector::_setDesktopLayer() {
+ Gtk::ListStore::iterator selected(_selector.get_active());
+ SPObject *layer=_selector.get_active()->get_value(_model_columns.object);
+ if ( _desktop && layer ) {
+ _layer_changed_connection.block();
+ _desktop->setCurrentLayer(layer);
+ _layer_changed_connection.unblock();
+ SP_DT_SELECTION(_desktop)->clear();
+ _selectLayer(_desktop->currentLayer());
+ }
+}
+
+/** Creates rows in the _layer_model data structure for each item
+ * in \a hierarchy, to a given \a depth.
+ */
+void LayerSelector::_buildEntries(unsigned depth,
+ Inkscape::Util::List<SPObject &> hierarchy)
+{
+ using Inkscape::Util::List;
+ using Inkscape::Util::rest;
+
+ _buildEntry(depth, *hierarchy);
+
+ List<SPObject &> remainder=rest(hierarchy);
+ if (remainder) {
+ _buildEntries(depth+1, remainder);
+ } else {
+ _buildSiblingEntries(depth+1, *hierarchy, remainder);
+ }
+}
+
+/** Creates entries in the _layer_model data structure for
+ * all siblings of the first child in \a parent.
+ */
+void LayerSelector::_buildSiblingEntries(
+ unsigned depth, SPObject &parent,
+ Inkscape::Util::List<SPObject &> hierarchy
+) {
+ using Inkscape::Util::List;
+ using Inkscape::Util::rest;
+ using Inkscape::Util::reverse_list_in_place;
+ using Inkscape::Util::filter_list;
+
+ Inkscape::Util::List<SPObject &> siblings(
+ reverse_list_in_place(
+ filter_list<SPObject::SiblingIterator>(
+ is_layer(_desktop), parent.firstChild(), NULL
+ )
+ )
+ );
+
+ SPObject *layer( hierarchy ? &*hierarchy : NULL );
+
+ while (siblings) {
+ _buildEntry(depth, *siblings);
+ if ( &*siblings == layer ) {
+ _buildSiblingEntries(depth+1, *layer, rest(hierarchy));
+ }
+ ++siblings;
+ }
+}
+
+namespace {
+
+struct Callbacks {
+ sigc::slot<void> update_row;
+ sigc::slot<void> update_list;
+};
+
+void attribute_changed(Inkscape::XML::Node *repr, gchar const *name,
+ gchar const *old_value, gchar const *new_value,
+ bool is_interactive, void *data)
+{
+ if ( !std::strcmp(name, "inkscape:groupmode") ) {
+ reinterpret_cast<Callbacks *>(data)->update_list();
+ } else {
+ reinterpret_cast<Callbacks *>(data)->update_row();
+ }
+}
+
+void node_added(Inkscape::XML::Node *parent, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, void *data) {
+ gchar const *mode=child->attribute("inkscape:groupmode");
+ if ( mode && !std::strcmp(mode, "layer") ) {
+ reinterpret_cast<Callbacks *>(data)->update_list();
+ }
+}
+
+void node_removed(Inkscape::XML::Node *parent, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, void *data) {
+ gchar const *mode=child->attribute("inkscape:groupmode");
+ if ( mode && !std::strcmp(mode, "layer") ) {
+ reinterpret_cast<Callbacks *>(data)->update_list();
+ }
+}
+
+void node_reordered(Inkscape::XML::Node *parent, Inkscape::XML::Node *child,
+ Inkscape::XML::Node *old_ref, Inkscape::XML::Node *new_ref,
+ void *data)
+{
+ gchar const *mode=child->attribute("inkscape:groupmode");
+ if ( mode && !std::strcmp(mode, "layer") ) {
+ reinterpret_cast<Callbacks *>(data)->update_list();
+ }
+}
+
+void update_row_for_object(SPObject *object,
+ Gtk::TreeModelColumn<SPObject *> const &column,
+ Glib::RefPtr<Gtk::ListStore> const &model)
+{
+ Gtk::TreeIter row(
+ std::find_if(
+ model->children().begin(),
+ model->children().end(),
+ column_matches_object(column, *object)
+ )
+ );
+ if ( row != model->children().end() ) {
+ model->row_changed(model->get_path(row), row);
+ }
+}
+
+void rebuild_all_rows(sigc::slot<void, SPObject *> rebuild, SPDesktop *desktop)
+{
+ rebuild(desktop->currentLayer());
+}
+
+}
+
+void LayerSelector::_protectUpdate(sigc::slot<void> slot) {
+ bool visibility_blocked=_visibility_toggled_connection.blocked();
+ bool lock_blocked=_lock_toggled_connection.blocked();
+ _visibility_toggled_connection.block(true);
+ _lock_toggled_connection.block(true);
+ slot();
+ _visibility_toggled_connection.block(visibility_blocked);
+ _lock_toggled_connection.block(lock_blocked);
+}
+
+/** Builds and appends a row in the layer model object.
+ */
+void LayerSelector::_buildEntry(unsigned depth, SPObject &object) {
+ Inkscape::XML::NodeEventVector *vector;
+
+ Callbacks *callbacks=new Callbacks();
+
+ callbacks->update_row = sigc::bind(
+ sigc::mem_fun(*this, &LayerSelector::_protectUpdate),
+ sigc::bind(
+ sigc::ptr_fun(&update_row_for_object),
+ &object, _model_columns.object, _layer_model
+ )
+ );
+
+ SPObject *layer=_desktop->currentLayer();
+ if ( &object == layer || &object == SP_OBJECT_PARENT(layer) ) {
+ callbacks->update_list = sigc::bind(
+ sigc::mem_fun(*this, &LayerSelector::_protectUpdate),
+ sigc::bind(
+ sigc::ptr_fun(&rebuild_all_rows),
+ sigc::mem_fun(*this, &LayerSelector::_selectLayer),
+ _desktop
+ )
+ );
+
+ Inkscape::XML::NodeEventVector events = {
+ &node_added,
+ &node_removed,
+ &attribute_changed,
+ NULL,
+ &node_reordered
+ };
+
+ vector = new Inkscape::XML::NodeEventVector(events);
+ } else {
+ Inkscape::XML::NodeEventVector events = {
+ NULL,
+ NULL,
+ &attribute_changed,
+ NULL,
+ NULL
+ };
+
+ vector = new Inkscape::XML::NodeEventVector(events);
+ }
+
+ Gtk::ListStore::iterator row(_layer_model->append());
+
+ row->set_value(_model_columns.depth, depth);
+
+ sp_object_ref(&object, NULL);
+ row->set_value(_model_columns.object, &object);
+
+ Inkscape::GC::anchor(SP_OBJECT_REPR(&object));
+ row->set_value(_model_columns.repr, SP_OBJECT_REPR(&object));
+
+ row->set_value(_model_columns.callbacks, reinterpret_cast<void *>(callbacks));
+
+ sp_repr_add_listener(SP_OBJECT_REPR(&object), vector, callbacks);
+}
+
+/** Removes a row from the _model_columns object, disconnecting listeners
+ * on the slot.
+ */
+void LayerSelector::_destroyEntry(Gtk::ListStore::iterator const &row) {
+ Callbacks *callbacks=reinterpret_cast<Callbacks *>(row->get_value(_model_columns.callbacks));
+ SPObject *object=row->get_value(_model_columns.object);
+ if (object) {
+ sp_object_unref(object, NULL);
+ }
+ Inkscape::XML::Node *repr=row->get_value(_model_columns.repr);
+ if (repr) {
+ sp_repr_remove_listener_by_data(repr, callbacks);
+ Inkscape::GC::release(repr);
+ }
+ delete callbacks;
+}
+
+/** Formats the label for a given layer row
+ */
+void LayerSelector::_prepareLabelRenderer(
+ Gtk::TreeModel::const_iterator const &row
+) {
+ unsigned depth=(*row)[_model_columns.depth];
+ SPObject *object=(*row)[_model_columns.object];
+ bool label_defaulted(false);
+
+ // TODO: when the currently selected row is removed,
+ // (or before one has been selected) something appears to
+ // "invent" an iterator with null data and try to render it;
+ // where does it come from, and how can we avoid it?
+ if ( object && SP_OBJECT_REPR(object) ) {
+ SPObject *layer=( _desktop ? _desktop->currentLayer() : NULL );
+ SPObject *root=( _desktop ? _desktop->currentRoot() : NULL );
+
+ bool isancestor = !( layer && SP_OBJECT_PARENT(object) == SP_OBJECT_PARENT(layer) || layer == root && SP_OBJECT_PARENT(object) == root);
+
+ bool iscurrent = ( object == layer && object != root );
+
+ gchar *format = g_strdup_printf (
+ "<span size=\"smaller\" %s><tt>%*s%s</tt>%s%s%s%%s%s%s%s</span>",
+ ( _desktop && _desktop->itemIsHidden (SP_ITEM(object)) ? "foreground=\"gray50\"" : "" ),
+ depth, "", ( iscurrent ? "&#8226;" : " " ),
+ ( iscurrent ? "<b>" : "" ),
+ ( SP_ITEM(object)->isLocked() ? "[" : "" ),
+ ( isancestor ? "<small>" : "" ),
+ ( isancestor ? "</small>" : "" ),
+ ( SP_ITEM(object)->isLocked() ? "]" : "" ),
+ ( iscurrent ? "</b>" : "" )
+ );
+
+ gchar const *label;
+ if ( object != root ) {
+ label = object->label();
+ if (!label) {
+ label = object->defaultLabel();
+ label_defaulted = true;
+ }
+ } else {
+ label = _("(root)");
+ }
+
+ gchar *text = g_markup_printf_escaped(format, label);
+ _label_renderer.property_markup() = text;
+ g_free(text);
+ g_free(format);
+ } else {
+ _label_renderer.property_markup() = "<small> </small>";
+ }
+
+ _label_renderer.property_ypad() = 1;
+ _label_renderer.property_style() = ( label_defaulted ?
+ Pango::STYLE_ITALIC :
+ Pango::STYLE_NORMAL );
+}
+
+void LayerSelector::_lockLayer(bool lock) {
+ if ( _layer && SP_IS_ITEM(_layer) ) {
+ SP_ITEM(_layer)->setLocked(lock);
+ sp_document_maybe_done(SP_DT_DOCUMENT(_desktop), "LayerSelector:lock");
+ }
+}
+
+void LayerSelector::_hideLayer(bool hide) {
+ if ( _layer && SP_IS_ITEM(_layer) ) {
+ SP_ITEM(_layer)->setHidden(hide);
+ sp_document_maybe_done(SP_DT_DOCUMENT(_desktop), "LayerSelector:hide");
+ }
+}
+
+}
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/layer-selector.h b/src/widgets/layer-selector.h
new file mode 100644
index 000000000..0b5300272
--- /dev/null
+++ b/src/widgets/layer-selector.h
@@ -0,0 +1,110 @@
+/*
+ * Inkscape::Widgets::LayerSelector - layer selector widget
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_WIDGETS_LAYER_SELECTOR
+#define SEEN_INKSCAPE_WIDGETS_LAYER_SELECTOR
+
+#include <gtkmm/box.h>
+#include <gtkmm/combobox.h>
+#include <gtkmm/togglebutton.h>
+#include <gtkmm/tooltips.h>
+#include <gtkmm/cellrenderertext.h>
+#include <gtkmm/treemodel.h>
+#include <gtkmm/liststore.h>
+#include <sigc++/slot.h>
+#include "util/list.h"
+
+class SPDesktop;
+class SPDocument;
+class SPObject;
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+}
+
+
+namespace Inkscape {
+namespace Widgets {
+
+class DocumentTreeModel;
+
+class LayerSelector : public Gtk::HBox {
+public:
+ LayerSelector(SPDesktop *desktop = NULL);
+ ~LayerSelector();
+
+ SPDesktop *desktop() { return _desktop; }
+ void setDesktop(SPDesktop *desktop);
+
+private:
+ class LayerModelColumns : public Gtk::TreeModel::ColumnRecord {
+ public:
+ Gtk::TreeModelColumn<unsigned> depth;
+ Gtk::TreeModelColumn<SPObject *> object;
+ Gtk::TreeModelColumn<Inkscape::XML::Node *> repr;
+ Gtk::TreeModelColumn<void *> callbacks;
+
+ LayerModelColumns() {
+ add(depth); add(object); add(repr); add(callbacks);
+ }
+ };
+
+ SPDesktop *_desktop;
+
+ Gtk::Tooltips _tooltips;
+ Gtk::ComboBox _selector;
+ Gtk::ToggleButton _visibility_toggle;
+ Gtk::ToggleButton _lock_toggle;
+
+ LayerModelColumns _model_columns;
+ Gtk::CellRendererText _label_renderer;
+ Glib::RefPtr<Gtk::ListStore> _layer_model;
+
+// sigc::connection _desktop_shutdown_connection;
+ sigc::connection _layer_changed_connection;
+ sigc::connection _selection_changed_connection;
+ sigc::connection _visibility_toggled_connection;
+ sigc::connection _lock_toggled_connection;
+
+ SPObject *_layer;
+
+ void _selectLayer(SPObject *layer);
+ void _setDesktopLayer();
+
+ void _buildEntry(unsigned depth, SPObject &object);
+ void _buildEntries(unsigned depth,
+ Inkscape::Util::List<SPObject &> hierarchy);
+ void _buildSiblingEntries(unsigned depth,
+ SPObject &parent,
+ Inkscape::Util::List<SPObject &> hierarchy);
+ void _protectUpdate(sigc::slot<void> slot);
+ void _destroyEntry(Gtk::ListStore::iterator const &row);
+ void _hideLayer(bool hide);
+ void _lockLayer(bool lock);
+
+ void _prepareLabelRenderer(Gtk::TreeModel::const_iterator const &row);
+};
+
+}
+}
+
+#endif
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/makefile.in b/src/widgets/makefile.in
new file mode 100644
index 000000000..cab398df1
--- /dev/null
+++ b/src/widgets/makefile.in
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+@SET_MAKE@
+
+# Explicit so that it's the default rule.
+all:
+ cd .. && $(MAKE) widgets/all
+
+clean %.a %.o:
+ cd .. && $(MAKE) widgets/$@
+
+.PHONY: all clean
+
+OBJEXT = @OBJEXT@
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)
diff --git a/src/widgets/paint-selector.cpp b/src/widgets/paint-selector.cpp
new file mode 100644
index 000000000..8b0935e8d
--- /dev/null
+++ b/src/widgets/paint-selector.cpp
@@ -0,0 +1,963 @@
+#define __SP_PAINT_SELECTOR_C__
+
+/** \file
+ * SPPaintSelector: Generic paint selector widget.
+ */
+
+/*
+ * Copyright (C) Lauris Kaplinski 2002
+ * bulia byak <buliabyak@users.sf.net>
+*/
+
+#define noSP_PS_VERBOSE
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+
+
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkradiobutton.h>
+#include <gtk/gtkframe.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkoptionmenu.h>
+#include <gtk/gtktooltips.h>
+#include <gtk/gtkmenuitem.h>
+
+#include "../sp-pattern.h"
+#include <glibmm/i18n.h>
+#include "../widgets/icon.h"
+#include "../inkscape-stock.h"
+#include "widgets/widget-sizes.h"
+#include "xml/repr.h"
+
+#include "sp-color-notebook.h"
+#include "sp-linear-gradient-fns.h"
+#include "sp-radial-gradient-fns.h"
+/* fixme: Move it from dialogs to here */
+#include "gradient-selector.h"
+#include <inkscape.h>
+#include <document-private.h>
+#include <desktop-style.h>
+#include <style.h>
+#include "svg/svg.h"
+#include "svg/css-ostringstream.h"
+
+#include "paint-selector.h"
+
+enum {
+ MODE_CHANGED,
+ GRABBED,
+ DRAGGED,
+ RELEASED,
+ CHANGED,
+ FILLRULE_CHANGED,
+ LAST_SIGNAL
+};
+
+static void sp_paint_selector_class_init (SPPaintSelectorClass *klass);
+static void sp_paint_selector_init (SPPaintSelector *slider);
+static void sp_paint_selector_destroy (GtkObject *object);
+
+static GtkWidget *sp_paint_selector_style_button_add (SPPaintSelector *psel, const gchar *px, SPPaintSelectorMode mode, GtkTooltips *tt, const gchar *tip);
+static void sp_paint_selector_style_button_toggled (GtkToggleButton *tb, SPPaintSelector *psel);
+static void sp_paint_selector_fillrule_toggled (GtkToggleButton *tb, SPPaintSelector *psel);
+
+static void sp_paint_selector_set_mode_empty (SPPaintSelector *psel);
+static void sp_paint_selector_set_mode_multiple (SPPaintSelector *psel);
+static void sp_paint_selector_set_mode_none (SPPaintSelector *psel);
+static void sp_paint_selector_set_mode_color (SPPaintSelector *psel, SPPaintSelectorMode mode);
+static void sp_paint_selector_set_mode_gradient (SPPaintSelector *psel, SPPaintSelectorMode mode);
+static void sp_paint_selector_set_mode_pattern (SPPaintSelector *psel, SPPaintSelectorMode mode);
+static void sp_paint_selector_set_mode_unset (SPPaintSelector *psel);
+
+
+static void sp_paint_selector_set_style_buttons (SPPaintSelector *psel, GtkWidget *active);
+
+static GtkVBoxClass *parent_class;
+static guint psel_signals[LAST_SIGNAL] = {0};
+
+GtkType
+sp_paint_selector_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GtkTypeInfo info = {
+ "SPPaintSelector",
+ sizeof (SPPaintSelector),
+ sizeof (SPPaintSelectorClass),
+ (GtkClassInitFunc) sp_paint_selector_class_init,
+ (GtkObjectInitFunc) sp_paint_selector_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_VBOX, &info);
+ }
+ return type;
+}
+
+static void
+sp_paint_selector_class_init (SPPaintSelectorClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ parent_class = (GtkVBoxClass*)gtk_type_class (GTK_TYPE_VBOX);
+
+ psel_signals[MODE_CHANGED] = gtk_signal_new ("mode_changed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPPaintSelectorClass, mode_changed),
+ gtk_marshal_NONE__UINT,
+ GTK_TYPE_NONE, 1, GTK_TYPE_UINT);
+ psel_signals[GRABBED] = gtk_signal_new ("grabbed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPPaintSelectorClass, grabbed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ psel_signals[DRAGGED] = gtk_signal_new ("dragged",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPPaintSelectorClass, dragged),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ psel_signals[RELEASED] = gtk_signal_new ("released",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPPaintSelectorClass, released),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ psel_signals[CHANGED] = gtk_signal_new ("changed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPPaintSelectorClass, changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ psel_signals[FILLRULE_CHANGED] = gtk_signal_new ("fillrule_changed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPPaintSelectorClass, fillrule_changed),
+ gtk_marshal_NONE__UINT,
+ GTK_TYPE_NONE, 1, GTK_TYPE_UINT);
+
+ object_class->destroy = sp_paint_selector_destroy;
+}
+
+#define XPAD 4
+#define YPAD 1
+
+static void
+sp_paint_selector_init (SPPaintSelector *psel)
+{
+ GtkTooltips *tt = gtk_tooltips_new();
+
+ psel->mode = (SPPaintSelectorMode)-1; // huh? do you mean 0xff? -- I think this means "not in the enum"
+
+ /* Paint style button box */
+ psel->style = gtk_hbox_new (FALSE, 0);
+ gtk_widget_show (psel->style);
+ gtk_container_set_border_width (GTK_CONTAINER (psel->style), 4);
+ gtk_box_pack_start (GTK_BOX (psel), psel->style, FALSE, FALSE, 0);
+
+ /* Buttons */
+ psel->none = sp_paint_selector_style_button_add (psel, INKSCAPE_STOCK_FILL_NONE,
+ SP_PAINT_SELECTOR_MODE_NONE, tt, _("No paint"));
+ psel->solid = sp_paint_selector_style_button_add (psel, INKSCAPE_STOCK_FILL_SOLID,
+ SP_PAINT_SELECTOR_MODE_COLOR_RGB, tt, _("Flat color"));
+ psel->gradient = sp_paint_selector_style_button_add (psel, INKSCAPE_STOCK_FILL_GRADIENT,
+ SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR, tt, _("Linear gradient"));
+ psel->radial = sp_paint_selector_style_button_add (psel, INKSCAPE_STOCK_FILL_RADIAL,
+ SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL, tt, _("Radial gradient")),
+ psel->pattern = sp_paint_selector_style_button_add (psel, INKSCAPE_STOCK_FILL_PATTERN,
+ SP_PAINT_SELECTOR_MODE_PATTERN, tt, _("Pattern"));
+ psel->unset = sp_paint_selector_style_button_add (psel, INKSCAPE_STOCK_FILL_UNSET,
+ SP_PAINT_SELECTOR_MODE_UNSET, tt, _("Unset paint (make it undefined so it can be inherited)"));
+
+ /* Fillrule */
+ {
+ psel->fillrulebox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (psel->style), psel->fillrulebox, FALSE, FALSE, 0);
+
+ GtkWidget *w;
+ psel->evenodd = gtk_radio_button_new (NULL);
+ gtk_button_set_relief (GTK_BUTTON (psel->evenodd), GTK_RELIEF_NONE);
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON(psel->evenodd), FALSE);
+ // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/painting.html#FillRuleProperty
+ gtk_tooltips_set_tip (tt, psel->evenodd, _("Any path self-intersections or subpaths create holes in the fill (fill-rule: evenodd)"), NULL);
+ gtk_object_set_data (GTK_OBJECT (psel->evenodd), "mode", GUINT_TO_POINTER (SP_PAINT_SELECTOR_FILLRULE_EVENODD));
+ w = sp_icon_new (GTK_ICON_SIZE_MENU, "fillrule_evenodd");
+ gtk_container_add (GTK_CONTAINER (psel->evenodd), w);
+ gtk_box_pack_start (GTK_BOX (psel->fillrulebox), psel->evenodd, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (psel->evenodd), "toggled", GTK_SIGNAL_FUNC (sp_paint_selector_fillrule_toggled), psel);
+
+ psel->nonzero = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (psel->evenodd)));
+ gtk_button_set_relief (GTK_BUTTON (psel->nonzero), GTK_RELIEF_NONE);
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON(psel->nonzero), FALSE);
+ // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/painting.html#FillRuleProperty
+ gtk_tooltips_set_tip (tt, psel->nonzero, _("Fill is solid unless a subpath is counterdirectional (fill-rule: nonzero)"), NULL);
+ gtk_object_set_data (GTK_OBJECT (psel->nonzero), "mode", GUINT_TO_POINTER (SP_PAINT_SELECTOR_FILLRULE_NONZERO));
+ w = sp_icon_new (GTK_ICON_SIZE_MENU, "fillrule_nonzero");
+ gtk_container_add (GTK_CONTAINER (psel->nonzero), w);
+ gtk_box_pack_start (GTK_BOX (psel->fillrulebox), psel->nonzero, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (psel->nonzero), "toggled", GTK_SIGNAL_FUNC (sp_paint_selector_fillrule_toggled), psel);
+ }
+
+ /* Frame */
+ psel->frame = gtk_frame_new ("");
+ gtk_widget_show (psel->frame);
+ gtk_container_set_border_width (GTK_CONTAINER (psel->frame), 0);
+ gtk_box_pack_start (GTK_BOX (psel), psel->frame, TRUE, TRUE, 0);
+
+ /* Last used color */
+ sp_color_set_rgb_float (&psel->color, 0.0, 0.0, 0.0);
+ psel->alpha = 1.0;
+}
+
+static void
+sp_paint_selector_destroy (GtkObject *object)
+{
+ SPPaintSelector *psel = SP_PAINT_SELECTOR (object);
+
+ // clean up our long-living pattern menu
+ g_object_set_data (G_OBJECT(psel),"patternmenu",NULL);
+
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+static GtkWidget *
+sp_paint_selector_style_button_add (SPPaintSelector *psel, const gchar *pixmap, SPPaintSelectorMode mode, GtkTooltips *tt, const gchar *tip)
+{
+ GtkWidget *b, *w;
+
+ b = gtk_toggle_button_new ();
+ gtk_tooltips_set_tip (tt, b, tip, NULL);
+ gtk_widget_show (b);
+
+ gtk_container_set_border_width (GTK_CONTAINER (b), 0);
+
+ gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
+
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
+ gtk_object_set_data (GTK_OBJECT (b), "mode", GUINT_TO_POINTER (mode));
+
+ w = sp_icon_new (GTK_ICON_SIZE_BUTTON, pixmap);
+ gtk_widget_show (w);
+ gtk_container_add (GTK_CONTAINER (b), w);
+
+ gtk_box_pack_start (GTK_BOX (psel->style), b, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (b), "toggled", GTK_SIGNAL_FUNC (sp_paint_selector_style_button_toggled), psel);
+
+ return b;
+}
+
+static void
+sp_paint_selector_style_button_toggled (GtkToggleButton *tb, SPPaintSelector *psel)
+{
+ if (!psel->update && gtk_toggle_button_get_active (tb)) {
+ sp_paint_selector_set_mode (psel, (SPPaintSelectorMode)GPOINTER_TO_UINT (gtk_object_get_data (GTK_OBJECT (tb), "mode")));
+ }
+}
+
+static void
+sp_paint_selector_fillrule_toggled (GtkToggleButton *tb, SPPaintSelector *psel)
+{
+ if (!psel->update && gtk_toggle_button_get_active (tb)) {
+ SPPaintSelectorFillRule fr = (SPPaintSelectorFillRule)GPOINTER_TO_UINT (gtk_object_get_data (GTK_OBJECT (tb), "mode"));
+ gtk_signal_emit (GTK_OBJECT (psel), psel_signals[FILLRULE_CHANGED], fr);
+ }
+}
+
+void
+sp_paint_selector_show_fillrule (SPPaintSelector *psel, bool is_fill)
+{
+ if (psel->fillrulebox) {
+ if (is_fill) {
+ gtk_widget_show_all (psel->fillrulebox);
+ } else {
+ gtk_widget_destroy (psel->fillrulebox);
+ psel->fillrulebox = NULL;
+ }
+ }
+}
+
+
+GtkWidget *
+sp_paint_selector_new (bool is_fill)
+{
+ SPPaintSelector *psel;
+
+ psel = (SPPaintSelector*)gtk_type_new (SP_TYPE_PAINT_SELECTOR);
+
+ sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE);
+
+ // This silliness is here because I don't know how to pass a parameter to the
+ // GtkObject "constructor" (sp_paint_selector_init). Remove it when paint_selector
+ // becomes a normal class.
+ sp_paint_selector_show_fillrule (psel, is_fill);
+
+ return GTK_WIDGET (psel);
+}
+
+void
+sp_paint_selector_set_mode (SPPaintSelector *psel, SPPaintSelectorMode mode)
+{
+ if (psel->mode != mode) {
+ psel->update = TRUE;
+#ifdef SP_PS_VERBOSE
+ g_print ("Mode change %d -> %d\n", psel->mode, mode);
+#endif
+ switch (mode) {
+ case SP_PAINT_SELECTOR_MODE_EMPTY:
+ sp_paint_selector_set_mode_empty (psel);
+ break;
+ case SP_PAINT_SELECTOR_MODE_MULTIPLE:
+ sp_paint_selector_set_mode_multiple (psel);
+ break;
+ case SP_PAINT_SELECTOR_MODE_NONE:
+ sp_paint_selector_set_mode_none (psel);
+ break;
+ case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
+ case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
+ sp_paint_selector_set_mode_color (psel, mode);
+ break;
+ case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR:
+ case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL:
+ sp_paint_selector_set_mode_gradient (psel, mode);
+ break;
+ case SP_PAINT_SELECTOR_MODE_PATTERN:
+ sp_paint_selector_set_mode_pattern (psel, mode);
+ break;
+ case SP_PAINT_SELECTOR_MODE_UNSET:
+ sp_paint_selector_set_mode_unset (psel);
+ break;
+ default:
+ g_warning ("file %s: line %d: Unknown paint mode %d", __FILE__, __LINE__, mode);
+ break;
+ }
+ psel->mode = mode;
+ gtk_signal_emit (GTK_OBJECT (psel), psel_signals[MODE_CHANGED], psel->mode);
+ psel->update = FALSE;
+ }
+}
+
+void
+sp_paint_selector_set_fillrule (SPPaintSelector *psel, SPPaintSelectorFillRule fillrule)
+{
+ if (psel->fillrulebox) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (psel->evenodd), (fillrule == SP_PAINT_SELECTOR_FILLRULE_EVENODD));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (psel->nonzero), (fillrule == SP_PAINT_SELECTOR_FILLRULE_NONZERO));
+ }
+}
+
+void
+sp_paint_selector_set_color_alpha (SPPaintSelector *psel, const SPColor *color, float alpha)
+{
+ g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) );
+ SPColorSelector *csel;
+ guint32 rgba;
+
+ if ( sp_color_get_colorspace_type (color) == SP_COLORSPACE_TYPE_CMYK )
+ {
+#ifdef SP_PS_VERBOSE
+ g_print ("PaintSelector set CMYKA\n");
+#endif
+ sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_COLOR_CMYK);
+ }
+ else
+ {
+#ifdef SP_PS_VERBOSE
+ g_print ("PaintSelector set RGBA\n");
+#endif
+ sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_COLOR_RGB);
+ }
+
+ csel = (SPColorSelector*)gtk_object_get_data (GTK_OBJECT (psel->selector), "color-selector");
+ rgba = sp_color_get_rgba32_falpha( &*color, alpha );
+ csel->base->setColorAlpha( *color, alpha );
+}
+
+void
+sp_paint_selector_set_gradient_linear (SPPaintSelector *psel, SPGradient *vector)
+{
+ SPGradientSelector *gsel;
+#ifdef SP_PS_VERBOSE
+ g_print ("PaintSelector set GRADIENT LINEAR\n");
+#endif
+ sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR);
+
+ gsel = (SPGradientSelector*)gtk_object_get_data (GTK_OBJECT (psel->selector), "gradient-selector");
+
+ sp_gradient_selector_set_mode (gsel, SP_GRADIENT_SELECTOR_MODE_LINEAR);
+ sp_gradient_selector_set_vector (gsel, (vector) ? SP_OBJECT_DOCUMENT (vector) : NULL, vector);
+}
+
+void
+sp_paint_selector_set_gradient_radial (SPPaintSelector *psel, SPGradient *vector)
+{
+ SPGradientSelector *gsel;
+#ifdef SP_PS_VERBOSE
+ g_print ("PaintSelector set GRADIENT RADIAL\n");
+#endif
+ sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL);
+
+ gsel = (SPGradientSelector*)gtk_object_get_data (GTK_OBJECT (psel->selector), "gradient-selector");
+
+ sp_gradient_selector_set_mode (gsel, SP_GRADIENT_SELECTOR_MODE_RADIAL);
+ sp_gradient_selector_set_vector (gsel, (vector) ? SP_OBJECT_DOCUMENT (vector) : NULL, vector);
+}
+
+void
+sp_paint_selector_set_gradient_properties (SPPaintSelector *psel, SPGradientUnits units, SPGradientSpread spread)
+{
+ SPGradientSelector *gsel;
+ g_return_if_fail (SP_IS_PAINT_SELECTOR (psel));
+ g_return_if_fail ((psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) ||
+ (psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL));
+ gsel = (SPGradientSelector*)gtk_object_get_data (GTK_OBJECT (psel->selector), "gradient-selector");
+ sp_gradient_selector_set_units (gsel, units);
+ sp_gradient_selector_set_spread (gsel, spread);
+}
+
+void
+sp_paint_selector_get_gradient_properties (SPPaintSelector *psel, SPGradientUnits *units, SPGradientSpread *spread)
+{
+ SPGradientSelector *gsel;
+ g_return_if_fail (SP_IS_PAINT_SELECTOR (psel));
+ g_return_if_fail ((psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) ||
+ (psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL));
+ gsel = (SPGradientSelector*)gtk_object_get_data (GTK_OBJECT (psel->selector), "gradient-selector");
+ if (units) *units = sp_gradient_selector_get_units (gsel);
+ if (spread) *spread = sp_gradient_selector_get_spread (gsel);
+}
+
+/**
+ * \post (alpha == NULL) || (*alpha in [0.0, 1.0]).
+ */
+void
+sp_paint_selector_get_color_alpha (SPPaintSelector *psel, SPColor *color, gfloat *alpha)
+{
+ SPColorSelector *csel;
+
+ csel = (SPColorSelector*)gtk_object_get_data (GTK_OBJECT (psel->selector), "color-selector");
+
+ csel->base->getColorAlpha( *color, alpha );
+
+ g_assert( !alpha
+ || ( ( 0.0 <= *alpha )
+ && ( *alpha <= 1.0 ) ) );
+}
+
+SPGradient *
+sp_paint_selector_get_gradient_vector (SPPaintSelector *psel)
+{
+ SPGradientSelector *gsel;
+
+ g_return_val_if_fail ((psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) ||
+ (psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL), NULL);
+
+ gsel = (SPGradientSelector*)gtk_object_get_data (GTK_OBJECT (psel->selector), "gradient-selector");
+
+ return sp_gradient_selector_get_vector (gsel);
+}
+
+void
+sp_gradient_selector_attrs_to_gradient (SPGradient *gr, SPPaintSelector *psel)
+{
+ SPGradientUnits units;
+ SPGradientSpread spread;
+ sp_paint_selector_get_gradient_properties (psel, &units, &spread);
+ sp_gradient_set_units (gr, units);
+ sp_gradient_set_spread (gr, spread);
+ SP_OBJECT(gr)->updateRepr();
+}
+
+static void
+sp_paint_selector_clear_frame(SPPaintSelector *psel)
+{
+ g_return_if_fail ( psel != NULL);
+
+ if (psel->selector) {
+
+ /* before we destroy the frame contents, we must detach
+ * the patternmenu so that Gtk doesn't gtk_widget_destroy
+ * all the children of the menu. (We also have a g_object_ref
+ * count set on it too so that the gtk_container_remove doesn't
+ * end up destroying it.
+ */
+ GtkWidget *patterns = (GtkWidget *)g_object_get_data (G_OBJECT(psel), "patternmenu");
+ if (patterns != NULL) {
+ GtkWidget * parent = gtk_widget_get_parent ( GTK_WIDGET (patterns));
+ if ( parent != NULL ) {
+ g_assert ( GTK_IS_CONTAINER (parent) );
+ gtk_container_remove ( GTK_CONTAINER (parent), patterns );
+ }
+ }
+
+ gtk_widget_destroy (psel->selector);
+ psel->selector = NULL;
+ }
+}
+
+static void
+sp_paint_selector_set_mode_empty (SPPaintSelector *psel)
+{
+ sp_paint_selector_set_style_buttons (psel, NULL);
+ gtk_widget_set_sensitive (psel->style, FALSE);
+
+ sp_paint_selector_clear_frame(psel);
+
+ gtk_frame_set_label (GTK_FRAME (psel->frame), _("No objects"));
+}
+
+static void
+sp_paint_selector_set_mode_multiple (SPPaintSelector *psel)
+{
+ sp_paint_selector_set_style_buttons (psel, NULL);
+ gtk_widget_set_sensitive (psel->style, TRUE);
+
+ sp_paint_selector_clear_frame(psel);
+
+ gtk_frame_set_label (GTK_FRAME (psel->frame), _("Multiple styles"));
+}
+
+static void
+sp_paint_selector_set_mode_unset (SPPaintSelector *psel)
+{
+ sp_paint_selector_set_style_buttons (psel, psel->unset);
+ gtk_widget_set_sensitive (psel->style, TRUE);
+
+ sp_paint_selector_clear_frame(psel);
+
+ gtk_frame_set_label (GTK_FRAME (psel->frame), _("Paint is undefined"));
+}
+
+static void
+sp_paint_selector_set_mode_none (SPPaintSelector *psel)
+{
+ sp_paint_selector_set_style_buttons (psel, psel->none);
+ gtk_widget_set_sensitive (psel->style, TRUE);
+
+ sp_paint_selector_clear_frame(psel);
+
+ gtk_frame_set_label (GTK_FRAME (psel->frame), _("No paint"));
+}
+
+/* Color paint */
+
+static void
+sp_paint_selector_color_grabbed (SPColorSelector *csel, SPPaintSelector *psel)
+{
+ gtk_signal_emit (GTK_OBJECT (psel), psel_signals[GRABBED]);
+}
+
+static void
+sp_paint_selector_color_dragged (SPColorSelector *csel, SPPaintSelector *psel)
+{
+ gtk_signal_emit (GTK_OBJECT (psel), psel_signals[DRAGGED]);
+}
+
+static void
+sp_paint_selector_color_released (SPColorSelector *csel, SPPaintSelector *psel)
+{
+ gtk_signal_emit (GTK_OBJECT (psel), psel_signals[RELEASED]);
+}
+
+static void
+sp_paint_selector_color_changed (SPColorSelector *csel, SPPaintSelector *psel)
+{
+ csel->base->getColorAlpha( psel->color, &psel->alpha );
+
+ gtk_signal_emit (GTK_OBJECT (psel), psel_signals[CHANGED]);
+}
+
+static void
+sp_paint_selector_set_mode_color (SPPaintSelector *psel, SPPaintSelectorMode mode)
+{
+ GtkWidget *csel;
+
+ sp_paint_selector_set_style_buttons (psel, psel->solid);
+ gtk_widget_set_sensitive (psel->style, TRUE);
+
+ if ((psel->mode == SP_PAINT_SELECTOR_MODE_COLOR_RGB) || (psel->mode == SP_PAINT_SELECTOR_MODE_COLOR_CMYK)) {
+ /* Already have color selector */
+ csel = (GtkWidget*)gtk_object_get_data (GTK_OBJECT (psel->selector), "color-selector");
+ } else {
+
+ sp_paint_selector_clear_frame(psel);
+ /* Create new color selector */
+ /* Create vbox */
+ GtkWidget *vb = gtk_vbox_new (FALSE, 4);
+ gtk_widget_show (vb);
+
+ /* Color selector */
+ csel = sp_color_selector_new (SP_TYPE_COLOR_NOTEBOOK, SP_COLORSPACE_TYPE_NONE);
+ gtk_widget_show (csel);
+ gtk_object_set_data (GTK_OBJECT (vb), "color-selector", csel);
+ gtk_box_pack_start (GTK_BOX (vb), csel, TRUE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT (csel), "grabbed", GTK_SIGNAL_FUNC (sp_paint_selector_color_grabbed), psel);
+ gtk_signal_connect (GTK_OBJECT (csel), "dragged", GTK_SIGNAL_FUNC (sp_paint_selector_color_dragged), psel);
+ gtk_signal_connect (GTK_OBJECT (csel), "released", GTK_SIGNAL_FUNC (sp_paint_selector_color_released), psel);
+ gtk_signal_connect (GTK_OBJECT (csel), "changed", GTK_SIGNAL_FUNC (sp_paint_selector_color_changed), psel);
+ /* Pack everything to frame */
+ gtk_container_add (GTK_CONTAINER (psel->frame), vb);
+ psel->selector = vb;
+
+ /* Set color */
+ SP_COLOR_SELECTOR( csel )->base->setColorAlpha( psel->color, psel->alpha );
+
+ }
+
+ gtk_frame_set_label (GTK_FRAME (psel->frame), _("Flat color"));
+#ifdef SP_PS_VERBOSE
+ g_print ("Color req\n");
+#endif
+}
+
+/* Gradient */
+
+static void
+sp_paint_selector_gradient_grabbed (SPColorSelector *csel, SPPaintSelector *psel)
+{
+ gtk_signal_emit (GTK_OBJECT (psel), psel_signals[GRABBED]);
+}
+
+static void
+sp_paint_selector_gradient_dragged (SPColorSelector *csel, SPPaintSelector *psel)
+{
+ gtk_signal_emit (GTK_OBJECT (psel), psel_signals[DRAGGED]);
+}
+
+static void
+sp_paint_selector_gradient_released (SPColorSelector *csel, SPPaintSelector *psel)
+{
+ gtk_signal_emit (GTK_OBJECT (psel), psel_signals[RELEASED]);
+}
+
+static void
+sp_paint_selector_gradient_changed (SPColorSelector *csel, SPPaintSelector *psel)
+{
+ gtk_signal_emit (GTK_OBJECT (psel), psel_signals[CHANGED]);
+}
+
+static void
+sp_paint_selector_set_mode_gradient (SPPaintSelector *psel, SPPaintSelectorMode mode)
+{
+ GtkWidget *gsel;
+
+ /* fixme: We do not need function-wide gsel at all */
+
+ if (mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) {
+ sp_paint_selector_set_style_buttons (psel, psel->gradient);
+ } else {
+ sp_paint_selector_set_style_buttons (psel, psel->radial);
+ }
+ gtk_widget_set_sensitive (psel->style, TRUE);
+
+ if ((psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) || (psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL)) {
+ /* Already have gradient selector */
+ gsel = (GtkWidget*)gtk_object_get_data (GTK_OBJECT (psel->selector), "gradient-selector");
+ } else {
+ sp_paint_selector_clear_frame(psel);
+ /* Create new gradient selector */
+ gsel = sp_gradient_selector_new ();
+ gtk_widget_show (gsel);
+ gtk_signal_connect (GTK_OBJECT (gsel), "grabbed", GTK_SIGNAL_FUNC (sp_paint_selector_gradient_grabbed), psel);
+ gtk_signal_connect (GTK_OBJECT (gsel), "dragged", GTK_SIGNAL_FUNC (sp_paint_selector_gradient_dragged), psel);
+ gtk_signal_connect (GTK_OBJECT (gsel), "released", GTK_SIGNAL_FUNC (sp_paint_selector_gradient_released), psel);
+ gtk_signal_connect (GTK_OBJECT (gsel), "changed", GTK_SIGNAL_FUNC (sp_paint_selector_gradient_changed), psel);
+ /* Pack everything to frame */
+ gtk_container_add (GTK_CONTAINER (psel->frame), gsel);
+ psel->selector = gsel;
+ gtk_object_set_data (GTK_OBJECT (psel->selector), "gradient-selector", gsel);
+ }
+
+ /* Actually we have to set optiomenu history here */
+ if (mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) {
+ sp_gradient_selector_set_mode (SP_GRADIENT_SELECTOR (gsel), SP_GRADIENT_SELECTOR_MODE_LINEAR);
+ gtk_frame_set_label (GTK_FRAME (psel->frame), _("Linear gradient"));
+ } else {
+ sp_gradient_selector_set_mode (SP_GRADIENT_SELECTOR (gsel), SP_GRADIENT_SELECTOR_MODE_RADIAL);
+ gtk_frame_set_label (GTK_FRAME (psel->frame), _("Radial gradient"));
+ }
+#ifdef SP_PS_VERBOSE
+ g_print ("Gradient req\n");
+#endif
+}
+
+static void
+sp_paint_selector_set_style_buttons (SPPaintSelector *psel, GtkWidget *active)
+{
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (psel->none), (active == psel->none));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (psel->solid), (active == psel->solid));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (psel->gradient), (active == psel->gradient));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (psel->radial), (active == psel->radial));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (psel->pattern), (active == psel->pattern));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (psel->unset), (active == psel->unset));
+}
+
+static void
+sp_psel_pattern_destroy (GtkWidget *widget, SPPaintSelector *psel)
+{
+ // drop our reference to the pattern menu widget
+ g_object_unref ( G_OBJECT (widget) );
+}
+
+static void
+sp_psel_pattern_change (GtkWidget *widget, SPPaintSelector *psel)
+{
+ gtk_signal_emit (GTK_OBJECT (psel), psel_signals[CHANGED]);
+}
+
+static GtkWidget*
+ink_pattern_menu (GtkWidget *mnu)
+{
+ /* Create new menu widget */
+ GtkWidget *m = gtk_menu_new ();
+ gtk_widget_show (m);
+
+ /* Pick up all patterns */
+ SPDocument *doc = SP_ACTIVE_DOCUMENT;
+ GSList *pl = NULL;
+ const GSList *patterns = sp_document_get_resource_list (doc, "pattern");
+ for (GSList *l = (GSList *) patterns; l != NULL; l = l->next) {
+ if (SP_PATTERN (l->data) == pattern_getroot (SP_PATTERN (l->data))) { // only if this is a root pattern
+ pl = g_slist_prepend (pl, l->data);
+ }
+ }
+
+ pl = g_slist_reverse (pl);
+
+ if (!doc) {
+ GtkWidget *i;
+ i = gtk_menu_item_new_with_label (_("No document selected"));
+ gtk_widget_show (i);
+ gtk_menu_append (GTK_MENU (m), i);
+ gtk_widget_set_sensitive (mnu, FALSE);
+ } else if (!pl) {
+ GtkWidget *i;
+ i = gtk_menu_item_new_with_label (_("No patterns in document"));
+ gtk_widget_show (i);
+ gtk_menu_append (GTK_MENU (m), i);
+ gtk_widget_set_sensitive (mnu, FALSE);
+ } else {
+ for (; pl != NULL; pl = pl->next){
+ if (SP_IS_PATTERN(pl->data)){
+ SPPattern *pat = SP_PATTERN (pl->data);
+ GtkWidget *i = gtk_menu_item_new ();
+ gtk_widget_show (i);
+ g_object_set_data (G_OBJECT (i), "pattern", pat);
+ // g_signal_connect (G_OBJECT (i), "activate", G_CALLBACK (sp_gvs_gradient_activate), gvs);
+ GtkWidget *hb = gtk_hbox_new (FALSE, 4);
+ gtk_widget_show (hb);
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) pl->data);
+ GtkWidget *l = gtk_label_new (repr->attribute("id"));
+ gtk_widget_show (l);
+ gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0);
+ gtk_widget_show (hb);
+ gtk_container_add (GTK_CONTAINER (i), hb);
+ gtk_menu_append (GTK_MENU (m), i);
+ }
+ }
+
+ gtk_widget_set_sensitive (mnu, TRUE);
+ }
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (mnu), m);
+
+ /* Set history */
+ //gtk_option_menu_set_history (GTK_OPTION_MENU (mnu), 0);
+
+ g_slist_free (pl);
+ return mnu;
+}
+
+
+/*update pattern list*/
+void
+sp_update_pattern_list ( SPPaintSelector *psel, SPPattern *pattern)
+{
+ if (psel->update) return;
+ GtkWidget *mnu = (GtkWidget *)g_object_get_data (G_OBJECT(psel), "patternmenu");
+ g_assert ( mnu != NULL );
+
+ /* Clear existing menu if any */
+ gtk_option_menu_remove_menu (GTK_OPTION_MENU (mnu));
+
+ ink_pattern_menu (mnu);
+
+ /* Set history */
+
+ if (pattern && !gtk_object_get_data(GTK_OBJECT(mnu), "update")) {
+
+ gtk_object_set_data(GTK_OBJECT(mnu), "update", GINT_TO_POINTER(TRUE));
+
+ gchar *patname = (gchar *) SP_OBJECT_REPR(pattern)->attribute("id");
+
+ GtkMenu *m = GTK_MENU(gtk_option_menu_get_menu (GTK_OPTION_MENU(mnu)));
+ GList *kids = GTK_MENU_SHELL(m)->children;
+
+ int patpos = 0;
+ int i = 0;
+
+ for (; kids != NULL; kids = kids->next) {
+ gchar *men_pat = (gchar *) SP_OBJECT_REPR(g_object_get_data(G_OBJECT(kids->data), "pattern"))->attribute("id");
+ if ( strcmp(men_pat, patname) == 0 ) {
+ patpos = i;
+ }
+ i++;
+ }
+
+ gtk_option_menu_set_history(GTK_OPTION_MENU (mnu), patpos);
+ gtk_object_set_data(GTK_OBJECT (mnu), "update", GINT_TO_POINTER (FALSE));
+ }
+ //gtk_option_menu_set_history (GTK_OPTION_MENU (mnu), 0);
+}
+
+static void
+sp_paint_selector_set_mode_pattern (SPPaintSelector *psel, SPPaintSelectorMode mode)
+{
+ if (mode == SP_PAINT_SELECTOR_MODE_PATTERN)
+ sp_paint_selector_set_style_buttons (psel, psel->pattern);
+
+ gtk_widget_set_sensitive (psel->style, TRUE);
+
+ GtkWidget *tbl=NULL;
+
+ if (psel->mode == SP_PAINT_SELECTOR_MODE_PATTERN){
+ /* Already have pattern menu */
+ tbl = (GtkWidget*)gtk_object_get_data (GTK_OBJECT (psel->selector), "pattern-selector");
+ } else {
+ sp_paint_selector_clear_frame(psel);
+
+ /* Create vbox */
+ tbl = gtk_vbox_new (FALSE, 4);
+ gtk_widget_show (tbl);
+
+ {
+ GtkWidget *hb = gtk_hbox_new (FALSE, 1);
+
+ GtkWidget *mnu = gtk_option_menu_new ();
+ ink_pattern_menu (mnu);
+ gtk_signal_connect (GTK_OBJECT (mnu), "changed", GTK_SIGNAL_FUNC (sp_psel_pattern_change), psel);
+ gtk_signal_connect (GTK_OBJECT (mnu), "destroy", GTK_SIGNAL_FUNC (sp_psel_pattern_destroy), psel);
+ gtk_object_set_data (GTK_OBJECT (psel), "patternmenu", mnu);
+ g_object_ref( G_OBJECT (mnu));
+
+ gtk_container_add (GTK_CONTAINER (hb), mnu);
+ gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ {
+ GtkWidget *hb = gtk_hbox_new (FALSE, 0);
+ GtkWidget *l = gtk_label_new (NULL);
+ gtk_label_set_markup(GTK_LABEL(l), _("Use <b>Edit &gt; Object(s) to Pattern</b> to create a new pattern from selection."));
+ gtk_label_set_line_wrap (GTK_LABEL(l), true);
+ gtk_widget_set_size_request (l, 180, -1);
+ gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, AUX_BETWEEN_BUTTON_GROUPS);
+ gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ gtk_widget_show_all (tbl);
+
+ gtk_container_add (GTK_CONTAINER (psel->frame), tbl);
+ psel->selector = tbl;
+ gtk_object_set_data (GTK_OBJECT (psel->selector), "pattern-selector", tbl);
+
+ gtk_frame_set_label (GTK_FRAME (psel->frame), _("Pattern fill"));
+ }
+#ifdef SP_PS_VERBOSE
+ g_print ("Pattern req\n");
+#endif
+}
+
+SPPattern *
+sp_paint_selector_get_pattern (SPPaintSelector *psel)
+{
+ SPPattern *pat;
+
+ g_return_val_if_fail ((psel->mode == SP_PAINT_SELECTOR_MODE_PATTERN) , NULL);
+
+ GtkWidget *patmnu = (GtkWidget *) g_object_get_data (G_OBJECT(psel), "patternmenu");
+ /* no pattern menu if we were just selected */
+ if ( patmnu == NULL ) return NULL;
+
+ GtkMenu *m = GTK_MENU(gtk_option_menu_get_menu (GTK_OPTION_MENU(patmnu)));
+
+ pat = pattern_getroot (SP_PATTERN(g_object_get_data (G_OBJECT(gtk_menu_get_active (m)), "pattern")));
+
+ return pat;
+}
+
+void
+sp_paint_selector_set_flat_color (SPPaintSelector *psel, SPDesktop *desktop, const gchar *color_property, const gchar *opacity_property)
+{
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+
+ SPColor color;
+ gfloat alpha;
+ sp_paint_selector_get_color_alpha (psel, &color, &alpha);
+ guint32 rgba = sp_color_get_rgba32_falpha (&color, alpha);
+
+ gchar b[64];
+ sp_svg_write_color (b, 64, rgba);
+
+ sp_repr_css_set_property (css, color_property, b);
+ Inkscape::CSSOStringStream osalpha;
+ osalpha << alpha;
+ sp_repr_css_set_property (css, opacity_property, osalpha.str().c_str());
+
+ sp_desktop_set_style (desktop, css);
+
+ sp_repr_css_attr_unref (css);
+}
+
+SPPaintSelectorMode
+sp_style_determine_paint_selector_mode (SPStyle *style, bool isfill)
+{
+ unsigned set = isfill? style->fill.set : style->stroke.set;
+ if (!set)
+ return SP_PAINT_SELECTOR_MODE_UNSET;
+
+ unsigned type = isfill? style->fill.type : style->stroke.type;
+ switch (type) {
+
+ case SP_PAINT_TYPE_NONE:
+ {
+ return SP_PAINT_SELECTOR_MODE_NONE;
+ }
+
+ case SP_PAINT_TYPE_COLOR:
+ {
+ return SP_PAINT_SELECTOR_MODE_COLOR_RGB; // so far only rgb can be read from svg
+ }
+
+ case SP_PAINT_TYPE_PAINTSERVER:
+ {
+ SPPaintServer *server = isfill? SP_STYLE_FILL_SERVER (style) : SP_STYLE_STROKE_SERVER (style);
+
+ if (SP_IS_LINEARGRADIENT (server)) {
+ return SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR;
+ } else if (SP_IS_RADIALGRADIENT (server)) {
+ return SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL;
+ } else if (SP_IS_PATTERN (server)) {
+ return SP_PAINT_SELECTOR_MODE_PATTERN;
+ }
+
+ g_warning ( "file %s: line %d: Unknown paintserver",
+ __FILE__, __LINE__ );
+ return SP_PAINT_SELECTOR_MODE_NONE;
+ }
+
+ default:
+ g_warning ( "file %s: line %d: Unknown paint type %d",
+ __FILE__, __LINE__, type );
+ break;
+ }
+
+ return SP_PAINT_SELECTOR_MODE_NONE;
+}
diff --git a/src/widgets/paint-selector.h b/src/widgets/paint-selector.h
new file mode 100644
index 000000000..de8bbf46a
--- /dev/null
+++ b/src/widgets/paint-selector.h
@@ -0,0 +1,110 @@
+#ifndef __SP_PAINT_SELECTOR_H__
+#define __SP_PAINT_SELECTOR_H__
+
+/** \file
+ * Generic paint selector widget
+ *
+ * Copyright (C) Lauris 2002
+ *
+ */
+
+#include <glib.h>
+#include "sp-gradient-spread.h"
+#include "sp-gradient-units.h"
+class SPGradient;
+
+#define SP_TYPE_PAINT_SELECTOR (sp_paint_selector_get_type ())
+#define SP_PAINT_SELECTOR(o) (GTK_CHECK_CAST ((o), SP_TYPE_PAINT_SELECTOR, SPPaintSelector))
+#define SP_PAINT_SELECTOR_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_PAINT_SELECTOR, SPPaintSelectorClass))
+#define SP_IS_PAINT_SELECTOR(o) (GTK_CHECK_TYPE ((o), SP_TYPE_PAINT_SELECTOR))
+#define SP_IS_PAINT_SELECTOR_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_PAINT_SELECTOR))
+
+#include <gtk/gtkvbox.h>
+
+#include "../forward.h"
+#include <color.h>
+#include <libnr/nr-forward.h>
+
+typedef enum {
+ SP_PAINT_SELECTOR_MODE_EMPTY,
+ SP_PAINT_SELECTOR_MODE_MULTIPLE,
+ SP_PAINT_SELECTOR_MODE_NONE,
+ SP_PAINT_SELECTOR_MODE_COLOR_RGB,
+ SP_PAINT_SELECTOR_MODE_COLOR_CMYK,
+ SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR,
+ SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL,
+ SP_PAINT_SELECTOR_MODE_PATTERN,
+ SP_PAINT_SELECTOR_MODE_UNSET
+} SPPaintSelectorMode;
+
+typedef enum {
+ SP_PAINT_SELECTOR_FILLRULE_NONZERO,
+ SP_PAINT_SELECTOR_FILLRULE_EVENODD
+} SPPaintSelectorFillRule;
+
+/// Generic paint selector widget
+struct SPPaintSelector {
+ GtkVBox vbox;
+
+ guint update : 1;
+
+ SPPaintSelectorMode mode;
+
+ GtkWidget *style;
+ GtkWidget *none, *solid, *gradient, *radial, *pattern, *unset;
+
+ GtkWidget *fillrulebox;
+ GtkWidget *evenodd, *nonzero;
+
+ GtkWidget *frame, *selector;
+
+ SPColor color;
+ float alpha;
+};
+
+/// The SPPaintSelector vtable
+struct SPPaintSelectorClass {
+ GtkVBoxClass parent_class;
+
+ void (* mode_changed) (SPPaintSelector *psel, SPPaintSelectorMode mode);
+
+ void (* grabbed) (SPPaintSelector *psel);
+ void (* dragged) (SPPaintSelector *psel);
+ void (* released) (SPPaintSelector *psel);
+ void (* changed) (SPPaintSelector *psel);
+ void (* fillrule_changed) (SPPaintSelector *psel, SPPaintSelectorFillRule fillrule);
+};
+
+GtkType sp_paint_selector_get_type (void);
+
+GtkWidget *sp_paint_selector_new (bool is_fill);
+
+void sp_paint_selector_set_mode (SPPaintSelector *psel, SPPaintSelectorMode mode);
+void sp_paint_selector_set_fillrule (SPPaintSelector *psel, SPPaintSelectorFillRule fillrule);
+
+void sp_paint_selector_set_color_alpha (SPPaintSelector *psel, const SPColor *color, float alpha);
+
+void sp_paint_selector_set_gradient_linear (SPPaintSelector *psel, SPGradient *vector);
+
+void sp_paint_selector_set_gradient_radial (SPPaintSelector *psel, SPGradient *vector);
+
+void sp_paint_selector_set_gradient_properties (SPPaintSelector *psel, SPGradientUnits units, SPGradientSpread spread);
+void sp_paint_selector_get_gradient_properties (SPPaintSelector *psel, SPGradientUnits *units, SPGradientSpread *spread);
+
+void sp_gradient_selector_attrs_to_gradient (SPGradient *gr, SPPaintSelector *psel);
+
+void sp_paint_selector_get_color_alpha (SPPaintSelector *psel, SPColor *color, gfloat *alpha);
+
+SPGradient *sp_paint_selector_get_gradient_vector (SPPaintSelector *psel);
+
+void sp_paint_selector_system_color_set (SPPaintSelector *psel, const SPColor *color, float opacity);
+
+SPPattern * sp_paint_selector_get_pattern (SPPaintSelector *psel);
+
+void sp_update_pattern_list ( SPPaintSelector *psel, SPPattern *pat);
+
+void sp_paint_selector_set_flat_color (SPPaintSelector *psel, SPDesktop *desktop, const gchar *color_property, const gchar *opacity_property);
+
+SPPaintSelectorMode sp_style_determine_paint_selector_mode (SPStyle *style, bool isfill);
+
+#endif
diff --git a/src/widgets/ruler.cpp b/src/widgets/ruler.cpp
new file mode 100644
index 000000000..bd3ec28eb
--- /dev/null
+++ b/src/widgets/ruler.cpp
@@ -0,0 +1,663 @@
+#define __SP_RULER_C__
+
+/*
+ * Customized ruler class for inkscape
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Frank Felfe <innerspace@iname.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 1999-2005 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <cmath>
+#include <string.h>
+#include "widget-sizes.h"
+#include "ruler.h"
+#include "unit-constants.h"
+
+#define MINIMUM_INCR 5
+#define MAXIMUM_SUBDIVIDE 5
+#define MAXIMUM_SCALES 10
+
+#define ROUND(x) int (std::floor ((x) + 0.5000000001))
+
+
+static void sp_hruler_class_init (SPHRulerClass *klass);
+static void sp_hruler_init (SPHRuler *hruler);
+static gint sp_hruler_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event);
+static void sp_hruler_draw_ticks (GtkRuler *ruler);
+static void sp_hruler_draw_pos (GtkRuler *ruler);
+
+
+GtkType
+sp_hruler_get_type (void)
+{
+ static GtkType hruler_type = 0;
+
+ if (!hruler_type)
+ {
+ static const GtkTypeInfo hruler_info =
+ {
+ "SPHRuler",
+ sizeof (SPHRuler),
+ sizeof (SPHRulerClass),
+ (GtkClassInitFunc) sp_hruler_class_init,
+ (GtkObjectInitFunc) sp_hruler_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ hruler_type = gtk_type_unique (gtk_ruler_get_type (), &hruler_info);
+ }
+
+ return hruler_type;
+}
+
+static void
+sp_hruler_class_init (SPHRulerClass *klass)
+{
+ GtkWidgetClass *widget_class;
+ GtkRulerClass *ruler_class;
+
+ widget_class = (GtkWidgetClass*) klass;
+ ruler_class = (GtkRulerClass*) klass;
+
+ widget_class->motion_notify_event = sp_hruler_motion_notify;
+
+ ruler_class->draw_ticks = sp_hruler_draw_ticks;
+ ruler_class->draw_pos = sp_hruler_draw_pos;
+}
+
+static void
+sp_hruler_init (SPHRuler *hruler)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (hruler);
+ widget->requisition.width = widget->style->xthickness * 2 + 1;
+ widget->requisition.height = widget->style->ythickness * 2 + RULER_HEIGHT;
+}
+
+
+GtkWidget*
+sp_hruler_new (void)
+{
+ return GTK_WIDGET (gtk_type_new (sp_hruler_get_type ()));
+}
+
+static gint
+sp_hruler_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkRuler *ruler;
+ gint x;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (SP_IS_HRULER (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ ruler = GTK_RULER (widget);
+
+ if (event->is_hint)
+ gdk_window_get_pointer (widget->window, &x, NULL, NULL);
+ else
+ x = (int)event->x;
+
+ ruler->position = ruler->lower + ((ruler->upper - ruler->lower) * x) / widget->allocation.width;
+
+ /* Make sure the ruler has been allocated already */
+ if (ruler->backing_store != NULL)
+ gtk_ruler_draw_pos (ruler);
+
+ return FALSE;
+}
+
+static void
+sp_hruler_draw_ticks (GtkRuler *ruler)
+{
+ GtkWidget *widget;
+ GdkGC *gc, *bg_gc;
+ PangoFontDescription *pango_desc;
+ PangoContext *pango_context;
+ PangoLayout *pango_layout;
+ gint i, tick_index;
+ gint width, height;
+ gint xthickness;
+ gint ythickness;
+ gint length, ideal_length;
+ double lower, upper; /* Upper and lower limits, in ruler units */
+ double increment; /* Number of pixels per unit */
+ gint scale; /* Number of units per major unit */
+ double subd_incr;
+ double start, end, cur;
+ gchar unit_str[32];
+ gint digit_height;
+ gint text_width;
+ gint pos;
+
+ g_return_if_fail (ruler != NULL);
+ g_return_if_fail (SP_IS_HRULER (ruler));
+
+ if (!GTK_WIDGET_DRAWABLE (ruler))
+ return;
+
+ widget = GTK_WIDGET (ruler);
+
+ gc = widget->style->fg_gc[GTK_STATE_NORMAL];
+ bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
+
+ pango_desc = widget->style->font_desc;
+
+ // Create the pango layout
+ pango_context = gtk_widget_get_pango_context (widget);
+
+ pango_layout = pango_layout_new (pango_context);
+
+ PangoFontDescription *fs = pango_font_description_new ();
+ pango_font_description_set_size (fs, RULER_FONT_SIZE);
+ pango_layout_set_font_description (pango_layout, fs);
+ pango_font_description_free (fs);
+
+ digit_height = (int) floor (RULER_FONT_SIZE * RULER_FONT_VERTICAL_SPACING / PANGO_SCALE + 0.5);
+
+ xthickness = widget->style->xthickness;
+ ythickness = widget->style->ythickness;
+
+ width = widget->allocation.width;
+ height = widget->allocation.height;// - ythickness * 2;
+
+ gtk_paint_box (widget->style, ruler->backing_store,
+ GTK_STATE_NORMAL, GTK_SHADOW_NONE,
+ NULL, widget, "hruler",
+ 0, 0,
+ widget->allocation.width, widget->allocation.height);
+
+ upper = ruler->upper / ruler->metric->pixels_per_unit;
+ lower = ruler->lower / ruler->metric->pixels_per_unit;
+
+ if ((upper - lower) == 0)
+ return;
+ increment = (double) width / (upper - lower);
+
+ /* determine the scale
+ * We calculate the text size as for the vruler instead of using
+ * text_width = gdk_string_width(font, unit_str), so that the result
+ * for the scale looks consistent with an accompanying vruler
+ */
+ scale = (int)(ceil (ruler->max_size / ruler->metric->pixels_per_unit));
+ sprintf (unit_str, "%d", scale);
+ text_width = strlen (unit_str) * digit_height + 1;
+
+ for (scale = 0; scale < MAXIMUM_SCALES; scale++)
+ if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_width)
+ break;
+
+ if (scale == MAXIMUM_SCALES)
+ scale = MAXIMUM_SCALES - 1;
+
+ /* drawing starts here */
+ length = 0;
+ for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--)
+ {
+ subd_incr = ruler->metric->ruler_scale[scale] /
+ ruler->metric->subdivide[i];
+ if (subd_incr * fabs(increment) <= MINIMUM_INCR)
+ continue;
+
+ /* Calculate the length of the tickmarks. Make sure that
+ * this length increases for each set of ticks
+ */
+ ideal_length = height / (i + 1) - 1;
+ if (ideal_length > ++length)
+ length = ideal_length;
+
+ if (lower < upper)
+ {
+ start = floor (lower / subd_incr) * subd_incr;
+ end = ceil (upper / subd_incr) * subd_incr;
+ }
+ else
+ {
+ start = floor (upper / subd_incr) * subd_incr;
+ end = ceil (lower / subd_incr) * subd_incr;
+ }
+
+ tick_index = 0;
+ cur = start;
+
+ while (cur <= end)
+ {
+ pos = ROUND ((cur - lower) * increment);
+
+ gdk_draw_line (ruler->backing_store, gc,
+ pos, height + ythickness,
+ pos, height - length + ythickness);
+
+ /* draw label */
+ double label_spacing_px = (increment*(double)ruler->metric->ruler_scale[scale])/ruler->metric->subdivide[i];
+ if (i == 0 &&
+ (label_spacing_px > 6*digit_height || tick_index%2 == 0 || cur == 0) &&
+ (label_spacing_px > 3*digit_height || tick_index%4 == 0 || cur == 0))
+ {
+ if (fabs((int)cur) >= 2000 && (((int) cur)/1000)*1000 == ((int) cur))
+ sprintf (unit_str, "%dk", ((int) cur)/1000);
+ else
+ sprintf (unit_str, "%d", (int) cur);
+
+ pango_layout_set_text (pango_layout, unit_str, -1);
+
+ gdk_draw_layout (ruler->backing_store, gc,
+ pos + 2, 0, pango_layout);
+ }
+
+ /* Calculate cur from start rather than incrementing by subd_incr
+ * in each iteration. This is to avoid propagation of floating point
+ * errors in subd_incr.
+ */
+ ++tick_index;
+ cur = start + (((double)tick_index) * (double)ruler->metric->ruler_scale[scale])/ ruler->metric->subdivide[i];
+ }
+ }
+}
+
+static void
+sp_hruler_draw_pos (GtkRuler *ruler)
+{
+ GtkWidget *widget;
+ GdkGC *gc;
+ int i;
+ gint x, y;
+ gint width, height;
+ gint bs_width, bs_height;
+ gint xthickness;
+ gint ythickness;
+ gfloat increment;
+
+ g_return_if_fail (ruler != NULL);
+ g_return_if_fail (SP_IS_HRULER (ruler));
+
+ if (GTK_WIDGET_DRAWABLE (ruler))
+ {
+ widget = GTK_WIDGET (ruler);
+
+ gc = widget->style->fg_gc[GTK_STATE_NORMAL];
+ xthickness = widget->style->xthickness;
+ ythickness = widget->style->ythickness;
+ width = widget->allocation.width;
+ height = widget->allocation.height - ythickness * 2;
+
+ bs_width = height / 2;
+ bs_width |= 1; /* make sure it's odd */
+ bs_height = bs_width / 2 + 1;
+
+ if ((bs_width > 0) && (bs_height > 0))
+ {
+ /* If a backing store exists, restore the ruler */
+ if (ruler->backing_store && ruler->non_gr_exp_gc)
+ gdk_draw_pixmap (ruler->widget.window,
+ ruler->non_gr_exp_gc,
+ ruler->backing_store,
+ ruler->xsrc, ruler->ysrc,
+ ruler->xsrc, ruler->ysrc,
+ bs_width, bs_height);
+
+ increment = (gfloat) width / (ruler->upper - ruler->lower);
+
+ x = ROUND ((ruler->position - ruler->lower) * increment) + (xthickness - bs_width) / 2 - 1;
+ y = (height + bs_height) / 2 + ythickness;
+
+ for (i = 0; i < bs_height; i++)
+ gdk_draw_line (widget->window, gc,
+ x + i, y + i,
+ x + bs_width - 1 - i, y + i);
+
+
+ ruler->xsrc = x;
+ ruler->ysrc = y;
+ }
+ }
+}
+
+
+
+
+
+// vruler
+
+static void sp_vruler_class_init (SPVRulerClass *klass);
+static void sp_vruler_init (SPVRuler *vruler);
+static gint sp_vruler_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event);
+static void sp_vruler_draw_ticks (GtkRuler *ruler);
+static void sp_vruler_draw_pos (GtkRuler *ruler);
+
+
+GtkType
+sp_vruler_get_type (void)
+{
+ static GtkType vruler_type = 0;
+
+ if (!vruler_type)
+ {
+ static const GtkTypeInfo vruler_info =
+ {
+ "SPVRuler",
+ sizeof (SPVRuler),
+ sizeof (SPVRulerClass),
+ (GtkClassInitFunc) sp_vruler_class_init,
+ (GtkObjectInitFunc) sp_vruler_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ vruler_type = gtk_type_unique (gtk_ruler_get_type (), &vruler_info);
+ }
+
+ return vruler_type;
+}
+
+static void
+sp_vruler_class_init (SPVRulerClass *klass)
+{
+ GtkWidgetClass *widget_class;
+ GtkRulerClass *ruler_class;
+
+ widget_class = (GtkWidgetClass*) klass;
+ ruler_class = (GtkRulerClass*) klass;
+
+ widget_class->motion_notify_event = sp_vruler_motion_notify;
+
+ ruler_class->draw_ticks = sp_vruler_draw_ticks;
+ ruler_class->draw_pos = sp_vruler_draw_pos;
+}
+
+static void
+sp_vruler_init (SPVRuler *vruler)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (vruler);
+ widget->requisition.width = widget->style->xthickness * 2 + RULER_WIDTH;
+ widget->requisition.height = widget->style->ythickness * 2 + 1;
+}
+
+GtkWidget*
+sp_vruler_new (void)
+{
+ return GTK_WIDGET (gtk_type_new (sp_vruler_get_type ()));
+}
+
+
+static gint
+sp_vruler_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkRuler *ruler;
+ gint y;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (SP_IS_VRULER (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ ruler = GTK_RULER (widget);
+
+ if (event->is_hint)
+ gdk_window_get_pointer (widget->window, NULL, &y, NULL);
+ else
+ y = (int)event->y;
+
+ ruler->position = ruler->lower + ((ruler->upper - ruler->lower) * y) / widget->allocation.height;
+
+ /* Make sure the ruler has been allocated already */
+ if (ruler->backing_store != NULL)
+ gtk_ruler_draw_pos (ruler);
+
+ return FALSE;
+}
+
+static void
+sp_vruler_draw_ticks (GtkRuler *ruler)
+{
+ GtkWidget *widget;
+ GdkGC *gc, *bg_gc;
+ PangoFontDescription *pango_desc;
+ PangoContext *pango_context;
+ PangoLayout *pango_layout;
+ gint i, j, tick_index;
+ gint width, height;
+ gint xthickness;
+ gint ythickness;
+ gint length, ideal_length;
+ double lower, upper; /* Upper and lower limits, in ruler units */
+ double increment; /* Number of pixels per unit */
+ gint scale; /* Number of units per major unit */
+ double subd_incr;
+ double start, end, cur;
+ gchar unit_str[32];
+ gchar digit_str[2] = { '\0', '\0' };
+ gint digit_height;
+ gint text_height;
+ gint pos;
+
+ g_return_if_fail (ruler != NULL);
+ g_return_if_fail (SP_IS_VRULER (ruler));
+
+ if (!GTK_WIDGET_DRAWABLE (ruler))
+ return;
+
+ widget = GTK_WIDGET (ruler);
+
+ gc = widget->style->fg_gc[GTK_STATE_NORMAL];
+ bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
+
+ pango_desc = widget->style->font_desc;
+
+ // Create the pango layout
+ pango_context = gtk_widget_get_pango_context (widget);
+
+ pango_layout = pango_layout_new (pango_context);
+
+ PangoFontDescription *fs = pango_font_description_new ();
+ pango_font_description_set_size (fs, RULER_FONT_SIZE);
+ pango_layout_set_font_description (pango_layout, fs);
+ pango_font_description_free (fs);
+
+ digit_height = (int) floor (RULER_FONT_SIZE * RULER_FONT_VERTICAL_SPACING / PANGO_SCALE + 0.5);
+
+ xthickness = widget->style->xthickness;
+ ythickness = widget->style->ythickness;
+
+ width = widget->allocation.height;
+ height = widget->allocation.width;// - ythickness * 2;
+
+ gtk_paint_box (widget->style, ruler->backing_store,
+ GTK_STATE_NORMAL, GTK_SHADOW_NONE,
+ NULL, widget, "vruler",
+ 0, 0,
+ widget->allocation.width, widget->allocation.height);
+
+ upper = ruler->upper / ruler->metric->pixels_per_unit;
+ lower = ruler->lower / ruler->metric->pixels_per_unit;
+
+ if ((upper - lower) == 0)
+ return;
+ increment = (double) width / (upper - lower);
+
+ /* determine the scale
+ * use the maximum extents of the ruler to determine the largest
+ * possible number to be displayed. Calculate the height in pixels
+ * of this displayed text. Use this height to find a scale which
+ * leaves sufficient room for drawing the ruler.
+ */
+ scale = (int)ceil (ruler->max_size / ruler->metric->pixels_per_unit);
+ sprintf (unit_str, "%d", scale);
+ text_height = strlen (unit_str) * digit_height + 1;
+
+ for (scale = 0; scale < MAXIMUM_SCALES; scale++)
+ if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_height)
+ break;
+
+ if (scale == MAXIMUM_SCALES)
+ scale = MAXIMUM_SCALES - 1;
+
+ /* drawing starts here */
+ length = 0;
+ for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--) {
+ subd_incr = (double) ruler->metric->ruler_scale[scale] /
+ (double) ruler->metric->subdivide[i];
+ if (subd_incr * fabs(increment) <= MINIMUM_INCR)
+ continue;
+
+ /* Calculate the length of the tickmarks. Make sure that
+ * this length increases for each set of ticks
+ */
+ ideal_length = height / (i + 1) - 1;
+ if (ideal_length > ++length)
+ length = ideal_length;
+
+ if (lower < upper)
+ {
+ start = floor (lower / subd_incr) * subd_incr;
+ end = ceil (upper / subd_incr) * subd_incr;
+ }
+ else
+ {
+ start = floor (upper / subd_incr) * subd_incr;
+ end = ceil (lower / subd_incr) * subd_incr;
+ }
+
+ tick_index = 0;
+ cur = start;
+
+ while (cur < end) {
+ pos = ROUND ((cur - lower) * increment);
+
+ gdk_draw_line (ruler->backing_store, gc,
+ height + xthickness - length, pos,
+ height + xthickness, pos);
+
+ /* draw label */
+ double label_spacing_px = fabs((increment*(double)ruler->metric->ruler_scale[scale])/ruler->metric->subdivide[i]);
+ if (i == 0 &&
+ (label_spacing_px > 6*digit_height || tick_index%2 == 0 || cur == 0) &&
+ (label_spacing_px > 3*digit_height || tick_index%4 == 0 || cur == 0))
+ {
+ if (fabs((int)cur) >= 2000 && (((int) cur)/1000)*1000 == ((int) cur))
+ sprintf (unit_str, "%dk", ((int) cur)/1000);
+ else
+ sprintf (unit_str, "%d", (int) cur);
+ for (j = 0; j < (int) strlen (unit_str); j++)
+ {
+ digit_str[0] = unit_str[j];
+
+ pango_layout_set_text (pango_layout, digit_str, 1);
+
+ gdk_draw_layout (ruler->backing_store, gc,
+ xthickness + 1,
+ pos + digit_height * (j) + 1,
+ pango_layout);
+ }
+ }
+
+ /* Calculate cur from start rather than incrementing by subd_incr
+ * in each iteration. This is to avoid propagation of floating point
+ * errors in subd_incr.
+ */
+ ++tick_index;
+ cur = start + (((double)tick_index) * (double)ruler->metric->ruler_scale[scale])/ ruler->metric->subdivide[i];
+ }
+ }
+}
+
+static void
+sp_vruler_draw_pos (GtkRuler *ruler)
+{
+ GtkWidget *widget;
+ GdkGC *gc;
+ int i;
+ gint x, y;
+ gint width, height;
+ gint bs_width, bs_height;
+ gint xthickness;
+ gint ythickness;
+ gfloat increment;
+
+ g_return_if_fail (ruler != NULL);
+ g_return_if_fail (SP_IS_VRULER (ruler));
+
+ if (GTK_WIDGET_DRAWABLE (ruler))
+ {
+ widget = GTK_WIDGET (ruler);
+
+ gc = widget->style->fg_gc[GTK_STATE_NORMAL];
+ xthickness = widget->style->xthickness;
+ ythickness = widget->style->ythickness;
+ width = widget->allocation.width - xthickness * 2;
+ height = widget->allocation.height;
+
+ bs_height = width / 2;
+ bs_height |= 1; /* make sure it's odd */
+ bs_width = bs_height / 2 + 1;
+
+ if ((bs_width > 0) && (bs_height > 0))
+ {
+ /* If a backing store exists, restore the ruler */
+ if (ruler->backing_store && ruler->non_gr_exp_gc)
+ gdk_draw_pixmap (ruler->widget.window,
+ ruler->non_gr_exp_gc,
+ ruler->backing_store,
+ ruler->xsrc, ruler->ysrc,
+ ruler->xsrc, ruler->ysrc,
+ bs_width, bs_height);
+
+ increment = (gfloat) height / (ruler->upper - ruler->lower);
+
+ x = (width + bs_width) / 2 + xthickness;
+ y = ROUND ((ruler->position - ruler->lower) * increment) + (ythickness - bs_height) / 2 - 1;
+
+ for (i = 0; i < bs_width; i++)
+ gdk_draw_line (widget->window, gc,
+ x + i, y + i,
+ x + i, y + bs_height - 1 - i);
+
+ ruler->xsrc = x;
+ ruler->ysrc = y;
+ }
+ }
+}
+
+/// Ruler metrics.
+static GtkRulerMetric const sp_ruler_metrics[] = {
+ // NOTE: the order of records in this struct must correspond to the SPMetric enum.
+ {"NONE", "", 1, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
+ {"millimeters", "mm", PX_PER_MM, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
+ {"centimeters", "cm", PX_PER_CM, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
+ {"inches", "in", PX_PER_IN, { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }, { 1, 2, 4, 8, 16 }},
+ {"points", "pt", PX_PER_PT, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
+ {"pixels", "px", PX_PER_PX, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
+ {"meters", "m", PX_PER_M, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
+};
+
+void
+sp_ruler_set_metric (GtkRuler *ruler,
+ SPMetric metric)
+{
+ g_return_if_fail (ruler != NULL);
+ g_return_if_fail (GTK_IS_RULER (ruler));
+ g_return_if_fail((unsigned) metric < G_N_ELEMENTS(sp_ruler_metrics));
+
+ if (metric == 0)
+ return;
+
+ ruler->metric = const_cast<GtkRulerMetric *>(&sp_ruler_metrics[metric]);
+
+ if (GTK_WIDGET_DRAWABLE (ruler))
+ gtk_widget_queue_draw (GTK_WIDGET (ruler));
+}
diff --git a/src/widgets/ruler.h b/src/widgets/ruler.h
new file mode 100644
index 000000000..1370c19c8
--- /dev/null
+++ b/src/widgets/ruler.h
@@ -0,0 +1,84 @@
+#ifndef __SP_RULER_H__
+#define __SP_RULER_H__
+
+/*
+ * Customized ruler class for inkscape
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Frank Felfe <innerspace@iname.com>
+ *
+ * Copyright (C) 1999-2002 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <gtk/gtkruler.h>
+#include "sp-metric.h"
+
+#include <glib.h>
+
+
+
+void sp_ruler_set_metric (GtkRuler * ruler, SPMetric metric);
+
+
+#define SP_HRULER(obj) GTK_CHECK_CAST (obj, sp_hruler_get_type (), SPHRuler)
+#define SP_HRULER_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, sp_hruler_get_type (), SPHRulerClass)
+#define SP_IS_HRULER(obj) GTK_CHECK_TYPE (obj, sp_hruler_get_type ())
+
+
+struct SPHRuler
+{
+ GtkRuler ruler;
+};
+
+struct SPHRulerClass
+{
+ GtkRulerClass parent_class;
+};
+
+
+GtkType sp_hruler_get_type (void);
+GtkWidget* sp_hruler_new (void);
+
+
+
+// vruler
+
+
+
+#define SP_VRULER(obj) GTK_CHECK_CAST (obj, sp_vruler_get_type (), SPVRuler)
+#define SP_VRULER_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, sp_vruler_get_type (), SPVRulerClass)
+#define SP_IS_VRULER(obj) GTK_CHECK_TYPE (obj, sp_vruler_get_type ())
+
+
+struct SPVRuler
+{
+ GtkRuler ruler;
+};
+
+struct SPVRulerClass
+{
+ GtkRulerClass parent_class;
+};
+
+
+GtkType sp_vruler_get_type (void);
+GtkWidget* sp_vruler_new (void);
+
+
+
+
+#endif /* __SP_RULER_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/select-toolbar.cpp b/src/widgets/select-toolbar.cpp
new file mode 100644
index 000000000..4230c7adb
--- /dev/null
+++ b/src/widgets/select-toolbar.cpp
@@ -0,0 +1,540 @@
+/*
+ * Selector aux toolbar
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 2003-2005 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "widgets/button.h"
+#include "widgets/spw-utilities.h"
+#include "widgets/widget-sizes.h"
+#include "widgets/spinbutton-events.h"
+
+#include "prefs-utils.h"
+#include "selection-chemistry.h"
+#include "document.h"
+#include "inkscape.h"
+#include "desktop-style.h"
+#include "desktop.h"
+#include "desktop-handles.h"
+#include "sp-namedview.h"
+#include "toolbox.h"
+#include <glibmm/i18n.h>
+#include "widgets/sp-widget.h"
+#include "helper/unit-menu.h"
+#include "helper/units.h"
+#include "widgets/sp-widget.h"
+#include "inkscape.h"
+#include "verbs.h"
+#include "prefs-utils.h"
+#include "selection.h"
+#include "selection-chemistry.h"
+#include "sp-item-transform.h"
+#include "message-stack.h"
+
+
+static void
+sp_selection_layout_widget_update(SPWidget *spw, Inkscape::Selection *sel)
+{
+ if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
+ return;
+ }
+
+ gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE));
+
+ GtkWidget *f = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(spw), "frame");
+
+ using NR::X;
+ using NR::Y;
+ if ( sel && !sel->isEmpty() ) {
+ NR::Rect const bbox(sel->bounds());
+ NR::Point const dimensions(bbox.dimensions());
+ if ((dimensions[X] > 1e-6) || (dimensions[Y] > 1e-6)) {
+ GtkWidget *us = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(spw), "units");
+ SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
+
+ if (unit.base == SP_UNIT_DIMENSIONLESS) {
+ char const * const keys[] = {"X", "Y", "width", "height"};
+ double const val = 1. / unit.unittobase;
+ for (unsigned i = 0; i < G_N_ELEMENTS(keys); ++i) {
+ GtkAdjustment *a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), keys[i]);
+ gtk_adjustment_set_value(a, val);
+ }
+ } else {
+ struct { char const *key; double val; } const keyval[] = {
+ { "X", bbox.min()[X] },
+ { "Y", bbox.min()[Y] },
+ { "width", dimensions[X] },
+ { "height", dimensions[Y] }
+ };
+ for (unsigned i = 0; i < G_N_ELEMENTS(keyval); ++i) {
+ GtkAdjustment *a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), keyval[i].key);
+ gtk_adjustment_set_value(a, sp_pixels_get_units(keyval[i].val, unit));
+ }
+ }
+
+ gtk_widget_set_sensitive(f, TRUE);
+ } else {
+ gtk_widget_set_sensitive(f, FALSE);
+ }
+ } else {
+ gtk_widget_set_sensitive(f, FALSE);
+ }
+
+ gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE));
+}
+
+
+static void
+sp_selection_layout_widget_modify_selection(SPWidget *spw, Inkscape::Selection *selection, guint flags, gpointer data)
+{
+ SPDesktop *desktop = (SPDesktop *) data;
+ if ((SP_DT_SELECTION(desktop) == selection) // only respond to changes in our desktop
+ && (flags & (SP_OBJECT_MODIFIED_FLAG |
+ SP_OBJECT_PARENT_MODIFIED_FLAG |
+ SP_OBJECT_CHILD_MODIFIED_FLAG )))
+ {
+ sp_selection_layout_widget_update(spw, selection);
+ }
+}
+
+static void
+sp_selection_layout_widget_change_selection(SPWidget *spw, Inkscape::Selection *selection, gpointer data)
+{
+ SPDesktop *desktop = (SPDesktop *) data;
+ if (SP_DT_SELECTION(desktop) == selection) // only respond to changes in our desktop
+ sp_selection_layout_widget_update(spw, selection);
+}
+
+static void
+sp_object_layout_any_value_changed(GtkAdjustment *adj, SPWidget *spw)
+{
+ if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
+ return;
+ }
+
+ GtkWidget *us = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(spw), "units");
+ SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
+ if (sp_unit_selector_update_test(SP_UNIT_SELECTOR(us))) {
+ /*
+ * When only units are being changed, don't treat changes
+ * to adjuster values as object changes.
+ */
+ return;
+ }
+ gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE));
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+ SPDocument *document = SP_DT_DOCUMENT(desktop);
+
+ sp_document_ensure_up_to_date (document);
+ NR::Rect bbox = selection->bounds();
+
+ if (!((bbox.max()[NR::X] - bbox.min()[NR::X] > 1e-6) || (bbox.max()[NR::Y] - bbox.min()[NR::Y] > 1e-6))) {
+ return;
+ }
+
+ gdouble x0, y0, x1, y1, xrel, yrel;
+ GtkAdjustment *a_w;
+ GtkAdjustment *a_h;
+
+ if (unit.base == SP_UNIT_ABSOLUTE || unit.base == SP_UNIT_DEVICE) {
+ GtkAdjustment *a;
+ a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "X");
+ x0 = sp_units_get_pixels (a->value, unit);
+ a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "Y");
+ y0 = sp_units_get_pixels (a->value, unit);
+ a_w = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "width");
+ x1 = x0 + sp_units_get_pixels (a_w->value, unit);
+ xrel = sp_units_get_pixels (a_w->value, unit) / bbox.extent(NR::X);
+ a_h = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "height");
+ y1 = y0 + sp_units_get_pixels (a_h->value, unit);
+ yrel = sp_units_get_pixels (a_h->value, unit) / bbox.extent(NR::Y);
+ } else {
+ GtkAdjustment *a;
+ a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "X");
+ double const x0_propn = a->value * unit.unittobase;
+ x0 = bbox.min()[NR::X] * x0_propn;
+ a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "Y");
+ double const y0_propn = a->value * unit.unittobase;
+ y0 = y0_propn * bbox.min()[NR::Y];
+ a_w = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "width");
+ xrel = a_w->value * unit.unittobase;
+ x1 = x0 + xrel * bbox.extent(NR::X);
+ a_h = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "height");
+ yrel = a_h->value * unit.unittobase;
+ y1 = y0 + yrel * bbox.extent(NR::Y);
+ }
+
+ // Keep proportions if lock is on
+ GtkWidget *lock = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "lock"));
+ if (SP_BUTTON_IS_DOWN(lock)) {
+ if (adj == a_h) {
+ x1 = x0 + yrel * bbox.extent(NR::X);
+ } else if (adj == a_w) {
+ y1 = y0 + xrel * bbox.extent(NR::Y);
+ }
+ }
+
+ // scales and moves, in px
+ double mh = fabs(x0 - bbox.min()[NR::X]);
+ double sh = fabs(x1 - bbox.max()[NR::X]);
+ double mv = fabs(y0 - bbox.min()[NR::Y]);
+ double sv = fabs(y1 - bbox.max()[NR::Y]);
+
+ // unless the unit is %, convert the scales and moves to the unit
+ if (unit.base == SP_UNIT_ABSOLUTE || unit.base == SP_UNIT_DEVICE) {
+ mh = sp_pixels_get_units (mh, unit);
+ sh = sp_pixels_get_units (sh, unit);
+ mv = sp_pixels_get_units (mv, unit);
+ sv = sp_pixels_get_units (sv, unit);
+ }
+
+ // do the action only if one of the scales/moves is greater than half the last significant
+ // digit in the spinbox (currently spinboxes have 3 fractional digits, so that makes 0.0005). If
+ // the value was changed by the user, the difference will be at least that much; otherwise it's
+ // just rounding difference between the spinbox value and actual value, so no action is
+ // performed
+ char const * const actionkey = ( mh > 5e-4 ? "selector:toolbar:move:horizontal" :
+ sh > 5e-4 ? "selector:toolbar:scale:horizontal" :
+ mv > 5e-4 ? "selector:toolbar:move:vertical" :
+ sv > 5e-4 ? "selector:toolbar:scale:vertical" : NULL );
+
+ if (actionkey != NULL) {
+ gdouble strokewidth = stroke_average_width (selection->itemList());
+ int transform_stroke = prefs_get_int_attribute ("options.transform", "stroke", 1);
+
+ NR::Matrix scaler = get_scale_transform_with_stroke (bbox, strokewidth, transform_stroke, x0, y0, x1, y1);
+
+ sp_selection_apply_affine(selection, scaler);
+ sp_document_maybe_done (document, actionkey);
+
+ // defocus spinbuttons by moving focus to the canvas, unless "stay" is on
+ spinbutton_defocus(GTK_OBJECT(spw));
+ }
+
+ gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE));
+}
+
+GtkWidget *
+sp_select_toolbox_spinbutton(gchar *label, gchar *data, float lower_limit, GtkWidget *us, GtkWidget *spw, gchar *tooltip, gboolean altx)
+{
+ GtkTooltips *tt = gtk_tooltips_new();
+
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *l = gtk_label_new(Q_(label));
+ gtk_tooltips_set_tip(tt, l, tooltip, NULL);
+ gtk_widget_show(l);
+ gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
+ gtk_container_add(GTK_CONTAINER(hb), l);
+
+ GtkObject *a = gtk_adjustment_new(0.0, lower_limit, 1e6, SPIN_STEP, SPIN_PAGE_STEP, SPIN_PAGE_STEP);
+ sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
+ gtk_object_set_data(GTK_OBJECT(spw), data, a);
+
+ GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), SPIN_STEP, 3);
+ gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
+ gtk_widget_set_size_request(sb, AUX_SPINBUTTON_WIDTH, AUX_SPINBUTTON_HEIGHT);
+ gtk_widget_show(sb);
+ gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), spw);
+ gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), spw);
+
+ gtk_container_add(GTK_CONTAINER(hb), sb);
+ gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(sp_object_layout_any_value_changed), spw);
+
+ if (altx) { // this spinbutton will be activated by alt-x
+ gtk_object_set_data(GTK_OBJECT(sb), "altx", sb);
+ }
+
+ return hb;
+}
+
+static gboolean aux_set_unit(SPUnitSelector *,
+ SPUnit const *old,
+ SPUnit const *new_units,
+ GObject *dlg)
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+
+ if (!desktop) {
+ return FALSE;
+ }
+
+ Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+
+ if (selection->isEmpty())
+ return FALSE;
+
+ if ((old->base == SP_UNIT_ABSOLUTE || old->base == SP_UNIT_DEVICE)
+ && (new_units->base == SP_UNIT_DIMENSIONLESS))
+ {
+
+ /* Absolute to percentage */
+ g_object_set_data(dlg, "update", GUINT_TO_POINTER(TRUE));
+
+ GtkAdjustment *ax = GTK_ADJUSTMENT(g_object_get_data(dlg, "X"));
+ GtkAdjustment *ay = GTK_ADJUSTMENT(g_object_get_data(dlg, "Y"));
+ GtkAdjustment *aw = GTK_ADJUSTMENT(g_object_get_data(dlg, "width"));
+ GtkAdjustment *ah = GTK_ADJUSTMENT(g_object_get_data(dlg, "height"));
+
+ double const x = sp_units_get_pixels (ax->value, *old);
+ double const y = sp_units_get_pixels (ay->value, *old);
+ double const w = sp_units_get_pixels (aw->value, *old);
+ double const h = sp_units_get_pixels (ah->value, *old);
+
+ NR::Rect bbox = selection->bounds();
+
+ gtk_adjustment_set_value(ax, fabs(bbox.min()[NR::X]) > 1e-6? 100.0 * x / bbox.min()[NR::X] : 100.0);
+ gtk_adjustment_set_value(ay, fabs(bbox.min()[NR::Y]) > 1e-6? 100.0 * y / bbox.min()[NR::Y] : 100.0);
+ gtk_adjustment_set_value(aw, fabs(bbox.extent(NR::X)) > 1e-6? 100.0 * w / bbox.extent(NR::X) : 100.0);
+ gtk_adjustment_set_value(ah, fabs(bbox.extent(NR::Y)) > 1e-6? 100.0 * h / bbox.extent(NR::Y) : 100.0);
+
+ g_object_set_data(dlg, "update", GUINT_TO_POINTER(FALSE));
+ return TRUE;
+ } else if ((old->base == SP_UNIT_DIMENSIONLESS)
+ && (new_units->base == SP_UNIT_ABSOLUTE || new_units->base == SP_UNIT_DEVICE)) {
+
+ /* Percentage to absolute */
+ g_object_set_data(dlg, "update", GUINT_TO_POINTER(TRUE));
+
+ GtkAdjustment *ax = GTK_ADJUSTMENT(g_object_get_data(dlg, "X"));
+ GtkAdjustment *ay = GTK_ADJUSTMENT(g_object_get_data(dlg, "Y"));
+ GtkAdjustment *aw = GTK_ADJUSTMENT(g_object_get_data(dlg, "width"));
+ GtkAdjustment *ah = GTK_ADJUSTMENT(g_object_get_data(dlg, "height"));
+
+ NR::Rect bbox = selection->bounds();
+
+ gtk_adjustment_set_value(ax, sp_pixels_get_units(0.01 * ax->value * bbox.min()[NR::X], *new_units));
+ gtk_adjustment_set_value(ay, sp_pixels_get_units(0.01 * ay->value * bbox.min()[NR::Y], *new_units));
+ gtk_adjustment_set_value(aw, sp_pixels_get_units(0.01 * aw->value * bbox.extent(NR::X), *new_units));
+ gtk_adjustment_set_value(ah, sp_pixels_get_units(0.01 * ah->value * bbox.extent(NR::Y), *new_units));
+
+ g_object_set_data(dlg, "update", GUINT_TO_POINTER(FALSE));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// toggle button callbacks and updaters
+
+static void toggle_stroke (GtkWidget *button, gpointer data) {
+ prefs_set_int_attribute ("options.transform", "stroke", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 1 : 0);
+ SPDesktop *desktop = (SPDesktop *)data;
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
+ desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now <b>stroke width</b> is <b>scaled</b> when objects are scaled."));
+ } else {
+ desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now <b>stroke width</b> is <b>not scaled</b> when objects are scaled."));
+ }
+}
+
+static void toggle_corners (GtkWidget *button, gpointer data) {
+ prefs_set_int_attribute ("options.transform", "rectcorners", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 1 : 0);
+ SPDesktop *desktop = (SPDesktop *)data;
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
+ desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now <b>rounded rectangle corners</b> are <b>scaled</b> when rectangles are scaled."));
+ } else {
+ desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now <b>rounded rectangle corners</b> are <b>not scaled</b> when rectangles are scaled."));
+ }
+}
+
+static void toggle_gradient (GtkWidget *button, gpointer data) {
+ prefs_set_int_attribute ("options.transform", "gradient", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 1 : 0);
+ SPDesktop *desktop = (SPDesktop *)data;
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
+ desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now <b>gradients</b> are <b>transformed</b> along with their objects when those are transformed (moved, scaled, rotated, or skewed)."));
+ } else {
+ desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now <b>gradients</b> remain <b>fixed</b> when objects are transformed (moved, scaled, rotated, or skewed)."));
+ }
+}
+
+static void toggle_pattern (GtkWidget *button, gpointer data) {
+ prefs_set_int_attribute ("options.transform", "pattern", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 1 : 0);
+ SPDesktop *desktop = (SPDesktop *)data;
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
+ desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now <b>patterns</b> are <b>transformed</b> along with their objects when those are transformed (moved, scaled, rotated, or skewed)."));
+ } else {
+ desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now <b>patterns</b> remain <b>fixed</b> when objects are transformed (moved, scaled, rotated, or skewed)."));
+ }
+}
+
+GtkWidget *
+sp_select_toolbox_new(SPDesktop *desktop)
+{
+ Inkscape::UI::View::View *view = desktop;
+
+ GtkTooltips *tt = gtk_tooltips_new();
+ GtkWidget *tb = gtk_hbox_new(FALSE, 0);
+
+ sp_toolbox_button_normal_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_OBJECT_ROTATE_90_CCW), view, tt);
+ sp_toolbox_button_normal_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_OBJECT_ROTATE_90_CW), view, tt);
+ sp_toolbox_button_normal_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_OBJECT_FLIP_HORIZONTAL), view, tt);
+ sp_toolbox_button_normal_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_OBJECT_FLIP_VERTICAL), view, tt);
+
+ aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_normal_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_SELECTION_TO_BACK), view, tt);
+ sp_toolbox_button_normal_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_SELECTION_LOWER), view, tt);
+ sp_toolbox_button_normal_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_SELECTION_RAISE), view, tt);
+ sp_toolbox_button_normal_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_SELECTION_TO_FRONT), view, tt);
+
+ // Create the parent widget for x y w h tracker.
+ GtkWidget *spw = sp_widget_new_global(INKSCAPE);
+
+ // Remember the desktop's canvas widget, to be used for defocusing.
+ gtk_object_set_data(GTK_OBJECT(spw), "dtw", SP_DT_CANVAS(desktop));
+
+ // The vb frame holds all other widgets and is used to set sensitivity depending on selection state.
+ GtkWidget *vb = gtk_hbox_new(FALSE, 0);
+ gtk_widget_show(vb);
+ gtk_container_add(GTK_CONTAINER(spw), vb);
+ gtk_object_set_data(GTK_OBJECT(spw), "frame", vb);
+
+ // Create the units menu.
+ GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
+ sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
+ sp_unit_selector_add_unit(SP_UNIT_SELECTOR(us), &sp_unit_get_by_id(SP_UNIT_PERCENT), 0);
+ sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), SP_DT_NAMEDVIEW(desktop)->doc_units);
+ g_signal_connect(G_OBJECT(us), "set_unit", G_CALLBACK(aux_set_unit), spw);
+
+ // four spinbuttons
+
+ gtk_container_add(GTK_CONTAINER(vb),
+ //TRANSLATORS: only translate "string" in "context|string".
+ // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS
+ sp_select_toolbox_spinbutton(_("select_toolbar|X"), "X", -1e6, us, spw, _("Horizontal coordinate of selection"), TRUE));
+ aux_toolbox_space(vb, AUX_BETWEEN_SPINBUTTONS);
+ gtk_container_add(GTK_CONTAINER(vb),
+ //TRANSLATORS: only translate "string" in "context|string".
+ // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS
+ sp_select_toolbox_spinbutton(_("select_toolbar|Y"), "Y", -1e6, us, spw, _("Vertical coordinate of selection"), FALSE));
+ aux_toolbox_space(vb, AUX_BETWEEN_BUTTON_GROUPS);
+
+ gtk_container_add(GTK_CONTAINER(vb),
+ //TRANSLATORS: only translate "string" in "context|string".
+ // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS
+ sp_select_toolbox_spinbutton(_("select_toolbar|W"), "width", 1e-3, us, spw, _("Width of selection"), FALSE));
+
+ // lock toggle
+ GtkWidget *lockbox = gtk_vbox_new(TRUE, 0);
+ GtkWidget *lock = sp_button_new_from_data( GTK_ICON_SIZE_MENU,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "width_height_lock",
+ _("Change both width and height by the same proportion"),
+ tt);
+ gtk_box_pack_start(GTK_BOX(lockbox), lock, TRUE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vb), lockbox, FALSE, FALSE, 0);
+ gtk_object_set_data(GTK_OBJECT(spw), "lock", lock);
+
+ gtk_container_add(GTK_CONTAINER(vb),
+ //TRANSLATORS: only translate "string" in "context|string".
+ // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS
+ sp_select_toolbox_spinbutton(_("select_toolbar|H"), "height", 1e-3, us, spw, _("Height of selection"), FALSE));
+
+ aux_toolbox_space(vb, 2);
+
+ // Add the units menu.
+ gtk_widget_show(us);
+ gtk_container_add(GTK_CONTAINER(vb), us);
+ gtk_object_set_data(GTK_OBJECT(spw), "units", us);
+
+ // Set font size.
+ sp_set_font_size_smaller (vb);
+
+ // Force update when selection changes.
+ gtk_signal_connect(GTK_OBJECT(spw), "modify_selection", GTK_SIGNAL_FUNC(sp_selection_layout_widget_modify_selection), desktop);
+ gtk_signal_connect(GTK_OBJECT(spw), "change_selection", GTK_SIGNAL_FUNC(sp_selection_layout_widget_change_selection), desktop);
+
+ // Update now.
+ sp_selection_layout_widget_update(SP_WIDGET(spw), SP_ACTIVE_DESKTOP ? SP_DT_SELECTION(SP_ACTIVE_DESKTOP) : NULL);
+
+ // Insert spw into the toolbar.
+ gtk_box_pack_start(GTK_BOX(tb), spw, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
+
+ // "Transform with object" buttons
+
+ GtkWidget *cvbox = gtk_vbox_new (FALSE, 0);
+ GtkWidget *cbox = gtk_hbox_new (FALSE, 0);
+
+ {
+ GtkWidget *button = sp_button_new_from_data( GTK_ICON_SIZE_SMALL_TOOLBAR,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "transform_stroke",
+ _("When scaling objects, scale the stroke width by the same proportion"),
+ tt);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), prefs_get_int_attribute ("options.transform", "stroke", 1));
+ g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (toggle_stroke), desktop);
+ gtk_box_pack_start(GTK_BOX(cbox), button, FALSE, FALSE, 0);
+ }
+
+ {
+ GtkWidget *button = sp_button_new_from_data( GTK_ICON_SIZE_SMALL_TOOLBAR,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "transform_corners",
+ _("When scaling rectangles, scale the radii of rounded corners"),
+ tt);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), prefs_get_int_attribute ("options.transform", "rectcorners", 1));
+ g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (toggle_corners), desktop);
+ gtk_box_pack_start(GTK_BOX(cbox), button, FALSE, FALSE, 0);
+ }
+
+ {
+ GtkWidget *button = sp_button_new_from_data( GTK_ICON_SIZE_SMALL_TOOLBAR,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "transform_gradient",
+ _("Transform gradients (in fill or stroke) along with the objects"),
+ tt);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), prefs_get_int_attribute ("options.transform", "gradient", 1));
+ g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (toggle_gradient), desktop);
+ gtk_box_pack_start(GTK_BOX(cbox), button, FALSE, FALSE, 0);
+ }
+
+ {
+ GtkWidget *button = sp_button_new_from_data( GTK_ICON_SIZE_SMALL_TOOLBAR,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "transform_pattern",
+ _("Transform patterns (in fill or stroke) along with the objects"),
+ tt);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), prefs_get_int_attribute ("options.transform", "pattern", 1));
+ g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (toggle_pattern), desktop);
+ gtk_box_pack_start(GTK_BOX(cbox), button, FALSE, FALSE, 0);
+ }
+
+ gtk_box_pack_start(GTK_BOX(cvbox), cbox, TRUE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(tb), cvbox, FALSE, FALSE, 0);
+
+ gtk_widget_show_all(tb);
+
+ return tb;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/widgets/select-toolbar.h b/src/widgets/select-toolbar.h
new file mode 100644
index 000000000..87576210a
--- /dev/null
+++ b/src/widgets/select-toolbar.h
@@ -0,0 +1,34 @@
+#ifndef SEEN_SELECT_TOOLBAR_H
+#define SEEN_SELECT_TOOLBAR_H
+
+/*
+ * Selector aux toolbar
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <bulia@dr.com>
+ *
+ * Copyright (C) 2003 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <gtk/gtkstyle.h>
+
+#include <forward.h>
+
+GtkWidget *sp_select_toolbox_new(SPDesktop *desktop);
+
+
+#endif /* !SEEN_SELECT_TOOLBAR_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/shrink-wrap-button.cpp b/src/widgets/shrink-wrap-button.cpp
new file mode 100644
index 000000000..c1775005c
--- /dev/null
+++ b/src/widgets/shrink-wrap-button.cpp
@@ -0,0 +1,55 @@
+/*
+ * Inkscape::Widgets::shrink_wrap_button - shrink a button to minimum size
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <gtkmm/button.h>
+#include <gtk/gtkbin.h>
+
+namespace Inkscape {
+namespace Widgets {
+
+namespace {
+
+void minimum_size(GtkWidget *widget, GtkRequisition *requisition, void *) {
+ GtkWidget *child(gtk_bin_get_child(GTK_BIN(widget)));
+
+ if (child) {
+ gtk_widget_size_request(child, requisition);
+ } else {
+ requisition->width = 0;
+ requisition->height = 0;
+ }
+
+ requisition->width += 2 + 2 * std::max(2, widget->style->xthickness);
+ requisition->height += 2 + 2 * std::max(2, widget->style->ythickness);
+}
+
+}
+
+void shrink_wrap_button(Gtk::Button &button) {
+ button.set_border_width(0);
+ button.unset_flags(Gtk::CAN_FOCUS | Gtk::CAN_DEFAULT);
+ g_signal_connect_after(G_OBJECT(button.gobj()), "size_request",
+ G_CALLBACK(minimum_size), NULL);
+}
+
+}
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/shrink-wrap-button.h b/src/widgets/shrink-wrap-button.h
new file mode 100644
index 000000000..ce615960f
--- /dev/null
+++ b/src/widgets/shrink-wrap-button.h
@@ -0,0 +1,35 @@
+/*
+ * Inkscape::Widgets::shrink_wrap_button - shrink a button to minimum size
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_WIDGETS_SHRINK_WRAP_BUTTON_H
+#define SEEN_INKSCAPE_WIDGETS_SHRINK_WRAP_BUTTON_H
+
+namespace Gtk { class Button; }
+
+namespace Inkscape {
+namespace Widgets {
+
+void shrink_wrap_button(Gtk::Button &button);
+
+}
+}
+
+#endif
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/sp-color-gtkselector.cpp b/src/widgets/sp-color-gtkselector.cpp
new file mode 100644
index 000000000..3c0ec17c6
--- /dev/null
+++ b/src/widgets/sp-color-gtkselector.cpp
@@ -0,0 +1,170 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <glibmm/i18n.h>
+#include "sp-color-gtkselector.h"
+
+
+static void sp_color_gtkselector_class_init (SPColorGtkselectorClass *klass);
+static void sp_color_gtkselector_init (SPColorGtkselector *csel);
+static void sp_color_gtkselector_destroy (GtkObject *object);
+
+static void sp_color_gtkselector_show_all (GtkWidget *widget);
+static void sp_color_gtkselector_hide_all (GtkWidget *widget);
+
+
+static SPColorSelectorClass *parent_class;
+
+#define XPAD 4
+#define YPAD 1
+
+GType
+sp_color_gtkselector_get_type (void)
+{
+ static GType type = 0;
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (SPColorGtkselectorClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) sp_color_gtkselector_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (SPColorGtkselector),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) sp_color_gtkselector_init,
+ NULL,
+ };
+
+ type = g_type_register_static (SP_TYPE_COLOR_SELECTOR,
+ "SPColorGtkselector",
+ &info,
+ static_cast< GTypeFlags > (0) );
+ }
+ return type;
+}
+
+static void
+sp_color_gtkselector_class_init (SPColorGtkselectorClass *klass)
+{
+ static const gchar* nameset[] = {N_("System"), 0};
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ SPColorSelectorClass *selector_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+ selector_class = SP_COLOR_SELECTOR_CLASS (klass);
+
+ parent_class = SP_COLOR_SELECTOR_CLASS (g_type_class_peek_parent (klass));
+
+ selector_class->name = nameset;
+ selector_class->submode_count = 1;
+
+ object_class->destroy = sp_color_gtkselector_destroy;
+
+ widget_class->show_all = sp_color_gtkselector_show_all;
+ widget_class->hide_all = sp_color_gtkselector_hide_all;
+}
+
+void sp_color_gtkselector_init (SPColorGtkselector *csel)
+{
+ SP_COLOR_SELECTOR(csel)->base = new ColorGtkselector( SP_COLOR_SELECTOR(csel) );
+
+ if ( SP_COLOR_SELECTOR(csel)->base )
+ {
+ SP_COLOR_SELECTOR(csel)->base->init();
+ }
+}
+
+void ColorGtkselector::init()
+{
+ GtkWidget *gtksel;
+
+ gtksel = gtk_color_selection_new();
+ gtk_widget_show (gtksel);
+ _gtkThing = GTK_COLOR_SELECTION (gtksel);
+ gtk_box_pack_start (GTK_BOX (_csel), gtksel, TRUE, TRUE, 0);
+
+ _sigId = g_signal_connect( GTK_OBJECT(gtksel), "color-changed", GTK_SIGNAL_FUNC( _gtkChanged ), _csel);
+}
+
+static void
+sp_color_gtkselector_destroy (GtkObject *object)
+{
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+static void
+sp_color_gtkselector_show_all (GtkWidget *widget)
+{
+ gtk_widget_show (widget);
+}
+
+static void
+sp_color_gtkselector_hide_all (GtkWidget *widget)
+{
+ gtk_widget_hide (widget);
+}
+
+GtkWidget *
+sp_color_gtkselector_new (GType, SPColorSpaceType colorspace)
+{
+ SPColorGtkselector *csel;
+
+ csel = (SPColorGtkselector*)gtk_type_new (SP_TYPE_COLOR_GTKSELECTOR);
+
+ return GTK_WIDGET (csel);
+}
+
+ColorGtkselector::ColorGtkselector( SPColorSelector* csel )
+ : ColorSelector( csel ),
+ _gtkThing(0)
+{
+}
+
+ColorGtkselector::~ColorGtkselector()
+{
+}
+
+void ColorGtkselector::_colorChanged( const SPColor& color, gfloat alpha )
+{
+ GdkColor gcolor;
+ float rgb[3];
+ g_return_if_fail (_csel != NULL);
+ g_return_if_fail (SP_IS_COLOR_GTKSELECTOR (_csel));
+ g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) );
+
+ sp_color_copy (&_color, &color);
+ _alpha = alpha;
+
+ sp_color_get_rgb_floatv( &color, rgb );
+ gcolor.pixel = 0;
+ gcolor.red = static_cast< guint16 > (rgb[0] * 65535);
+ gcolor.green = static_cast< guint16 > (rgb[1] * 65535);
+ gcolor.blue = static_cast< guint16 > (rgb[2] * 65535);
+
+// g_message( "***** _colorChanged %04x %04x %04x", gcolor.red, gcolor.green, gcolor.blue );
+ g_signal_handler_block( _gtkThing, _sigId );
+ gtk_color_selection_set_current_alpha (_gtkThing, (guint16)(65535 * alpha));
+ gtk_color_selection_set_current_color (_gtkThing, &gcolor);
+ g_signal_handler_unblock(_gtkThing, _sigId );
+}
+
+void ColorGtkselector::_gtkChanged( GtkColorSelection *colorselection, SPColorGtkselector *gtksel )
+{
+ ColorGtkselector* gtkInst = (ColorGtkselector*)(SP_COLOR_SELECTOR(gtksel)->base);
+ SPColor ourColor;
+ GdkColor color;
+ guint16 alpha;
+
+ gtk_color_selection_get_current_color (colorselection, &color);
+ alpha = gtk_color_selection_get_current_alpha (colorselection);
+
+ sp_color_set_rgb_float (&ourColor, (color.red / 65535.0), (color.green / 65535.0), (color.blue / 65535.0));
+
+// g_message( "***** _gtkChanged %04x %04x %04x", color.red, color.green, color.blue );
+
+ gtkInst->_updateInternals( ourColor, static_cast< gfloat > (alpha) / 65535.0, gtk_color_selection_is_adjusting(colorselection) );
+}
diff --git a/src/widgets/sp-color-gtkselector.h b/src/widgets/sp-color-gtkselector.h
new file mode 100644
index 000000000..e3b780f25
--- /dev/null
+++ b/src/widgets/sp-color-gtkselector.h
@@ -0,0 +1,55 @@
+#ifndef __SP_COLOR_GTKSELECTOR_H__
+#define __SP_COLOR_GTKSELECTOR_H__
+
+#include <gtk/gtkcolorsel.h>
+#include "../color.h"
+#include "sp-color-selector.h"
+
+#include <glib.h>
+
+
+
+struct SPColorGtkselector;
+
+
+
+class ColorGtkselector: public ColorSelector
+{
+public:
+ ColorGtkselector( SPColorSelector* csel );
+ virtual ~ColorGtkselector();
+
+ virtual void init();
+
+protected:
+ static void _gtkChanged( GtkColorSelection *colorselection, SPColorGtkselector *gtksel );
+
+ virtual void _colorChanged( const SPColor& color, gfloat alpha );
+
+ GtkColorSelection* _gtkThing;
+ gulong _sigId;
+};
+
+
+
+#define SP_TYPE_COLOR_GTKSELECTOR (sp_color_gtkselector_get_type ())
+#define SP_COLOR_GTKSELECTOR(o) (GTK_CHECK_CAST ((o), SP_TYPE_COLOR_GTKSELECTOR, SPColorGtkselector))
+#define SP_COLOR_GTKSELECTOR_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_COLOR_GTKSELECTOR, SPColorGtkselectorClass))
+#define SP_IS_COLOR_GTKSELECTOR(o) (GTK_CHECK_TYPE ((o), SP_TYPE_COLOR_GTKSELECTOR))
+#define SP_IS_COLOR_GTKSELECTOR_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_COLOR_GTKSELECTOR))
+
+struct SPColorGtkselector {
+ SPColorSelector base;
+};
+
+struct SPColorGtkselectorClass {
+ SPColorSelectorClass parent_class;
+};
+
+GType sp_color_gtkselector_get_type (void);
+
+GtkWidget *sp_color_gtkselector_new (GType selector_type, SPColorSpaceType colorspace);
+
+
+
+#endif
diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp
new file mode 100644
index 000000000..9d92077da
--- /dev/null
+++ b/src/widgets/sp-color-notebook.cpp
@@ -0,0 +1,625 @@
+#define __SP_COLOR_NOTEBOOK_C__
+
+/*
+ * A block of 3 color sliders plus spinbuttons
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ *
+ * This code is in public domain
+ */
+
+#undef SPCS_PREVIEW
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <glibmm/i18n.h>
+#include "../dialogs/dialog-events.h"
+#include "../prefs-utils.h"
+#include "sp-color-notebook.h"
+#include "spw-utilities.h"
+
+#include "sp-color-scales.h"
+#include "sp-color-wheel-selector.h"
+
+struct SPColorNotebookTracker {
+ const gchar* name;
+ const gchar* className;
+ GType type;
+ guint submode;
+ gboolean enabledFull;
+ gboolean enabledBrief;
+ SPColorNotebook *backPointer;
+};
+
+static void sp_color_notebook_class_init (SPColorNotebookClass *klass);
+static void sp_color_notebook_init (SPColorNotebook *slider);
+static void sp_color_notebook_destroy (GtkObject *object);
+
+static void sp_color_notebook_show_all (GtkWidget *widget);
+static void sp_color_notebook_hide_all (GtkWidget *widget);
+
+static SPColorSelectorClass *parent_class;
+
+#define XPAD 4
+#define YPAD 1
+
+GtkType
+sp_color_notebook_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GtkTypeInfo info = {
+ "SPColorNotebook",
+ sizeof (SPColorNotebook),
+ sizeof (SPColorNotebookClass),
+ (GtkClassInitFunc) sp_color_notebook_class_init,
+ (GtkObjectInitFunc) sp_color_notebook_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (SP_TYPE_COLOR_SELECTOR, &info);
+ }
+ return type;
+}
+
+static void
+sp_color_notebook_class_init (SPColorNotebookClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ SPColorSelectorClass *selector_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+ selector_class = SP_COLOR_SELECTOR_CLASS (klass);
+
+ parent_class = SP_COLOR_SELECTOR_CLASS (g_type_class_peek_parent (klass));
+
+ object_class->destroy = sp_color_notebook_destroy;
+
+ widget_class->show_all = sp_color_notebook_show_all;
+ widget_class->hide_all = sp_color_notebook_hide_all;
+}
+
+static void
+sp_color_notebook_switch_page(GtkNotebook *notebook,
+ GtkNotebookPage *page,
+ guint page_num,
+ SPColorNotebook *colorbook)
+{
+ if ( colorbook )
+ {
+ ColorNotebook* nb = (ColorNotebook*)(SP_COLOR_SELECTOR(colorbook)->base);
+ nb->switchPage( notebook, page, page_num );
+
+ // remember the page we seitched to
+ prefs_set_int_attribute ("colorselector", "page", page_num);
+
+ }
+}
+
+void ColorNotebook::switchPage(GtkNotebook*,
+ GtkNotebookPage*,
+ guint page_num)
+{
+ SPColorSelector* csel;
+ GtkWidget* widget;
+
+ if ( gtk_notebook_get_current_page (GTK_NOTEBOOK (_book)) >= 0 )
+ {
+ csel = getCurrentSelector();
+ csel->base->getColorAlpha(_color, &_alpha);
+ }
+ widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK (_book), page_num);
+ if ( widget && SP_IS_COLOR_SELECTOR (widget) )
+ {
+ csel = SP_COLOR_SELECTOR (widget);
+ csel->base->setColorAlpha( _color, _alpha );
+
+ // Temporary workaround to undo a spurious GRABBED
+ _released();
+ }
+}
+
+static gint sp_color_notebook_menu_handler( GtkWidget *widget, GdkEvent *event )
+{
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+ SPColorSelector* csel = SP_COLOR_SELECTOR(widget);
+ ((ColorNotebook*)(csel->base))->menuHandler( event );
+
+ /* Tell calling code that we have handled this event; the buck
+ * stops here. */
+ return TRUE;
+ }
+
+ /* Tell calling code that we have not handled this event; pass it on. */
+ return FALSE;
+}
+
+gint ColorNotebook::menuHandler( GdkEvent* event )
+{
+ GdkEventButton *bevent = (GdkEventButton *) event;
+ gtk_menu_popup (GTK_MENU( _popup ), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ return TRUE;
+}
+
+static void sp_color_notebook_menuitem_response (GtkMenuItem *menuitem, gpointer user_data)
+{
+ gboolean active = FALSE;
+
+ active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem));
+ SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (user_data);
+ if ( entry )
+ {
+ if ( active )
+ {
+ ((ColorNotebook*)(SP_COLOR_SELECTOR(entry->backPointer)->base))->addPage(entry->type, entry->submode);
+ }
+ else
+ {
+ ((ColorNotebook*)(SP_COLOR_SELECTOR(entry->backPointer)->base))->removePage(entry->type, entry->submode);
+ }
+ }
+}
+
+static void
+sp_color_notebook_init (SPColorNotebook *colorbook)
+{
+ SP_COLOR_SELECTOR(colorbook)->base = new ColorNotebook( SP_COLOR_SELECTOR(colorbook) );
+
+ if ( SP_COLOR_SELECTOR(colorbook)->base )
+ {
+ SP_COLOR_SELECTOR(colorbook)->base->init();
+ }
+}
+
+void ColorNotebook::init()
+{
+ GtkWidget* table = 0;
+ guint row = 0;
+ guint i = 0;
+ guint j = 0;
+ GType *selector_types = 0;
+ guint selector_type_count = 0;
+
+ GtkTooltips *tt = gtk_tooltips_new ();
+
+ /* tempory hardcoding to get types loaded */
+ SP_TYPE_COLOR_SCALES;
+ SP_TYPE_COLOR_WHEEL_SELECTOR;
+
+ /* REJON: Comment out the next line to not use the normal GTK Color
+ wheel. */
+
+// SP_TYPE_COLOR_GTKSELECTOR;
+
+ _updating = FALSE;
+ _updatingrgba = FALSE;
+ _btn = 0;
+ _popup = 0;
+ _trackerList = g_ptr_array_new ();
+
+ _book = gtk_notebook_new ();
+ gtk_widget_show (_book);
+
+ selector_types = g_type_children (SP_TYPE_COLOR_SELECTOR, &selector_type_count);
+
+ for ( i = 0; i < selector_type_count; i++ )
+ {
+ if (!g_type_is_a (selector_types[i], SP_TYPE_COLOR_NOTEBOOK))
+ {
+ guint howmany = 1;
+ gpointer klass = gtk_type_class (selector_types[i]);
+ if ( klass && SP_IS_COLOR_SELECTOR_CLASS (klass) )
+ {
+ SPColorSelectorClass *ck = SP_COLOR_SELECTOR_CLASS (klass);
+ howmany = MAX (1, ck->submode_count);
+ for ( j = 0; j < howmany; j++ )
+ {
+ SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (malloc(sizeof(SPColorNotebookTracker)));
+ if ( entry )
+ {
+ memset( entry, 0, sizeof(SPColorNotebookTracker) );
+ entry->name = ck->name[j];
+ entry->type = selector_types[i];
+ entry->submode = j;
+ entry->enabledFull = TRUE;
+ entry->enabledBrief = TRUE;
+ entry->backPointer = SP_COLOR_NOTEBOOK(_csel);
+
+ g_ptr_array_add (_trackerList, entry);
+ }
+ }
+ }
+ }
+ }
+
+ for ( i = 0; i < _trackerList->len; i++ )
+ {
+ SPColorNotebookTracker *entry =
+ reinterpret_cast< SPColorNotebookTracker* > (g_ptr_array_index (_trackerList, i));
+ if ( entry )
+ {
+ addPage(entry->type, entry->submode);
+ }
+ }
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_widget_show (table);
+
+ gtk_box_pack_start (GTK_BOX (_csel), table, TRUE, TRUE, 0);
+
+ gtk_table_attach (GTK_TABLE (table), _book, 0, 2, row, row + 1,
+ static_cast<GtkAttachOptions>(GTK_EXPAND|GTK_FILL),
+ static_cast<GtkAttachOptions>(GTK_EXPAND|GTK_FILL),
+ XPAD, YPAD);
+
+ // restore the last active page
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (_book), prefs_get_int_attribute ("colorselector", "page", 0));
+
+ {
+ gboolean found = FALSE;
+
+ _popup = gtk_menu_new();
+ GtkMenu *menu = GTK_MENU (_popup);
+
+ for ( i = 0; i < _trackerList->len; i++ )
+ {
+ SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (g_ptr_array_index (_trackerList, i));
+ if ( entry )
+ {
+ GtkWidget *item = gtk_check_menu_item_new_with_label (_(entry->name));
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), entry->enabledFull);
+ gtk_widget_show (item);
+ gtk_menu_append (menu, item);
+
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (sp_color_notebook_menuitem_response),
+ reinterpret_cast< gpointer > (entry) );
+ found = TRUE;
+ }
+ }
+
+ GtkWidget *arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+ gtk_widget_show (arrow);
+
+ _btn = gtk_button_new ();
+ gtk_widget_show (_btn);
+ gtk_container_add (GTK_CONTAINER (_btn), arrow);
+
+ GtkWidget *align = gtk_alignment_new (1.0, 0.0, 0.0, 0.0);
+ gtk_widget_show (align);
+ gtk_container_add (GTK_CONTAINER (align), _btn);
+
+ // uncomment to reenable the "show/hide modes" menu,
+ // but first fix it so it remembers its settings in prefs and does not take that much space (entire vertical column!)
+ //gtk_table_attach (GTK_TABLE (table), align, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, XPAD, YPAD);
+
+ gtk_signal_connect_object(GTK_OBJECT(_btn), "event", GTK_SIGNAL_FUNC (sp_color_notebook_menu_handler), GTK_OBJECT(_csel));
+ if ( !found )
+ {
+ gtk_widget_set_sensitive (_btn, FALSE);
+ }
+ }
+
+ row++;
+
+ /* Create RGBA entry and color preview */
+ GtkWidget *rgbabox = gtk_hbox_new (FALSE, 0);
+
+ _rgbal = gtk_label_new_with_mnemonic (_("RGBA_:"));
+ gtk_misc_set_alignment (GTK_MISC (_rgbal), 1.0, 0.5);
+ gtk_box_pack_start(GTK_BOX(rgbabox), _rgbal, TRUE, TRUE, 2);
+
+ _rgbae = gtk_entry_new ();
+ sp_dialog_defocus_on_enter (_rgbae);
+ gtk_entry_set_max_length (GTK_ENTRY (_rgbae), 8);
+ gtk_entry_set_width_chars (GTK_ENTRY (_rgbae), 8);
+ gtk_tooltips_set_tip (tt, _rgbae, _("Hexadecimal RGBA value of the color"), NULL);
+ gtk_box_pack_start(GTK_BOX(rgbabox), _rgbae, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL(_rgbal), _rgbae);
+
+ sp_set_font_size_smaller (rgbabox);
+ gtk_widget_show_all (rgbabox);
+ gtk_table_attach (GTK_TABLE (table), rgbabox, 1, 2, row, row + 1, GTK_FILL, GTK_SHRINK, XPAD, YPAD);
+
+#ifdef SPCS_PREVIEW
+ _p = sp_color_preview_new (0xffffffff);
+ gtk_widget_show (_p);
+ gtk_table_attach (GTK_TABLE (table), _p, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, XPAD, YPAD);
+#endif
+
+ _switchId = g_signal_connect(GTK_OBJECT (_book), "switch-page",
+ GTK_SIGNAL_FUNC (sp_color_notebook_switch_page), SP_COLOR_NOTEBOOK(_csel));
+
+ _entryId = gtk_signal_connect (GTK_OBJECT (_rgbae), "changed", GTK_SIGNAL_FUNC (ColorNotebook::_rgbaEntryChangedHook), _csel);
+}
+
+static void
+sp_color_notebook_destroy (GtkObject *object)
+{
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+ColorNotebook::~ColorNotebook()
+{
+ if ( _trackerList )
+ {
+ g_ptr_array_free (_trackerList, TRUE);
+ _trackerList = 0;
+ }
+
+ if ( _switchId )
+ {
+ if ( _book )
+ {
+ g_signal_handler_disconnect (_book, _switchId);
+ _switchId = 0;
+ }
+ }
+}
+
+static void
+sp_color_notebook_show_all (GtkWidget *widget)
+{
+ gtk_widget_show (widget);
+}
+
+static void
+sp_color_notebook_hide_all (GtkWidget *widget)
+{
+ gtk_widget_hide (widget);
+}
+
+GtkWidget *
+sp_color_notebook_new (void)
+{
+ SPColorNotebook *colorbook;
+
+ colorbook = (SPColorNotebook*)gtk_type_new (SP_TYPE_COLOR_NOTEBOOK);
+
+ return GTK_WIDGET (colorbook);
+}
+
+ColorNotebook::ColorNotebook( SPColorSelector* csel )
+ : ColorSelector( csel )
+{
+}
+
+SPColorSelector* ColorNotebook::getCurrentSelector()
+{
+ SPColorSelector* csel = NULL;
+ gint current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (_book));
+
+ if ( current_page >= 0 )
+ {
+ GtkWidget* widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK (_book), current_page);
+ if ( SP_IS_COLOR_SELECTOR (widget) )
+ {
+ csel = SP_COLOR_SELECTOR (widget);
+ }
+ }
+
+ return csel;
+}
+
+void ColorNotebook::_colorChanged(const SPColor& color, gfloat alpha)
+{
+ SPColorSelector* cselPage = 0;
+
+ g_return_if_fail (_csel != NULL);
+ g_return_if_fail (SP_IS_COLOR_NOTEBOOK (_csel));
+ g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) );
+
+ cselPage = getCurrentSelector();
+ if ( cselPage )
+ {
+ cselPage->base->setColorAlpha( color, alpha );
+ }
+
+ _updateRgbaEntry( color, alpha );
+}
+
+void ColorNotebook::_rgbaEntryChangedHook(GtkEntry *entry, SPColorNotebook *colorbook)
+{
+ ((ColorNotebook*)(SP_COLOR_SELECTOR(colorbook)->base))->_rgbaEntryChanged( entry );
+}
+
+void ColorNotebook::_rgbaEntryChanged(GtkEntry* entry)
+{
+ const gchar *t;
+ gchar *e;
+ SPColor color;
+ guint rgba;
+
+ if (_updating) return;
+ if (_updatingrgba) return;
+
+ t = gtk_entry_get_text (entry);
+
+ if (t) {
+ rgba = strtoul (t, &e, 16);
+ if ( e != t ) {
+ ptrdiff_t len=e-t;
+ if ( len < 8 ) {
+ rgba = rgba << ( 4 * ( 8 - len ) );
+ }
+ _updatingrgba = TRUE;
+ sp_color_set_rgb_rgba32 (&color, rgba);
+ setColorAlpha( color, SP_RGBA32_A_F(rgba), true);
+ _updatingrgba = FALSE;
+ }
+ }
+}
+
+void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha )
+{
+ g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) );
+
+ if ( !_updatingrgba )
+ {
+ gchar s[32];
+ guint32 rgba;
+
+ /* Update RGBA entry */
+ rgba = sp_color_get_rgba32_falpha (&color, alpha);
+
+ g_snprintf (s, 32, "%08x", rgba);
+ const gchar* oldText = gtk_entry_get_text( GTK_ENTRY( _rgbae ) );
+ if ( strcmp( oldText, s ) != 0 )
+ {
+ g_signal_handler_block( _rgbae, _entryId );
+ gtk_entry_set_text( GTK_ENTRY(_rgbae), s );
+ g_signal_handler_unblock( _rgbae, _entryId );
+ }
+ }
+}
+
+void ColorNotebook::_entryGrabbed (SPColorSelector *, SPColorNotebook *colorbook)
+{
+ ColorNotebook* nb = (ColorNotebook*)(SP_COLOR_SELECTOR(colorbook)->base);
+ nb->_grabbed();
+}
+
+void ColorNotebook::_entryDragged (SPColorSelector *csel, SPColorNotebook *colorbook)
+{
+ gboolean oldState;
+ ColorNotebook* nb = (ColorNotebook*)(SP_COLOR_SELECTOR(colorbook)->base);
+
+ oldState = nb->_dragging;
+
+ nb->_dragging = TRUE;
+ nb->_entryModified( csel, colorbook );
+
+ nb->_dragging = oldState;
+}
+
+void ColorNotebook::_entryReleased (SPColorSelector *, SPColorNotebook *colorbook)
+{
+ ColorNotebook* nb = (ColorNotebook*)(SP_COLOR_SELECTOR(colorbook)->base);
+ nb->_released();
+}
+
+void ColorNotebook::_entryChanged (SPColorSelector *csel, SPColorNotebook *colorbook)
+{
+ gboolean oldState;
+ ColorNotebook* nb = (ColorNotebook*)(SP_COLOR_SELECTOR(colorbook)->base);
+
+ oldState = nb->_dragging;
+
+ nb->_dragging = FALSE;
+ nb->_entryModified( csel, colorbook );
+
+ nb->_dragging = oldState;
+}
+
+void ColorNotebook::_entryModified (SPColorSelector *csel, SPColorNotebook *colorbook)
+{
+ g_return_if_fail (colorbook != NULL);
+ g_return_if_fail (SP_IS_COLOR_NOTEBOOK (colorbook));
+ g_return_if_fail (csel != NULL);
+ g_return_if_fail (SP_IS_COLOR_SELECTOR (csel));
+
+ ColorNotebook* nb = (ColorNotebook*)(SP_COLOR_SELECTOR(colorbook)->base);
+ SPColor color;
+ gfloat alpha = 1.0;
+
+ csel->base->getColorAlpha( color, &alpha );
+ nb->_updateRgbaEntry( color, alpha );
+ nb->_updateInternals( color, alpha, nb->_dragging );
+}
+
+GtkWidget* ColorNotebook::addPage(GType page_type, guint submode)
+{
+ GtkWidget *page;
+
+ page = sp_color_selector_new ( page_type, SP_COLORSPACE_TYPE_NONE);
+ if ( page )
+ {
+ GtkWidget* tab_label = 0;
+ SPColorSelector* csel;
+
+ csel = SP_COLOR_SELECTOR (page);
+ if ( submode > 0 )
+ {
+ csel->base->setSubmode( submode );
+ }
+ gtk_widget_show (page);
+ int index = csel->base ? csel->base->getSubmode() : 0;
+ const gchar* str = _(SP_COLOR_SELECTOR_GET_CLASS (csel)->name[index]);
+// g_message( "Hitting up for tab for '%s'", str );
+ tab_label = gtk_label_new(_(str));
+ gtk_notebook_append_page( GTK_NOTEBOOK (_book), page, tab_label );
+ gtk_signal_connect (GTK_OBJECT (page), "grabbed", GTK_SIGNAL_FUNC (_entryGrabbed), _csel);
+ gtk_signal_connect (GTK_OBJECT (page), "dragged", GTK_SIGNAL_FUNC (_entryDragged), _csel);
+ gtk_signal_connect (GTK_OBJECT (page), "released", GTK_SIGNAL_FUNC (_entryReleased), _csel);
+ gtk_signal_connect (GTK_OBJECT (page), "changed", GTK_SIGNAL_FUNC (_entryChanged), _csel);
+ }
+
+ return page;
+}
+
+GtkWidget* ColorNotebook::getPage(GType page_type, guint submode)
+{
+ gint count = 0;
+ gint i = 0;
+ GtkWidget* page = 0;
+
+// count = gtk_notebook_get_n_pages (_book);
+ count = 200;
+ for ( i = 0; i < count && !page; i++ )
+ {
+ page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (_book), i);
+ if ( page )
+ {
+ SPColorSelector* csel;
+ guint pagemode;
+ csel = SP_COLOR_SELECTOR (page);
+ pagemode = csel->base->getSubmode();
+ if ( G_TYPE_FROM_INSTANCE (page) == page_type
+ && pagemode == submode )
+ {
+ // found it.
+ break;
+ }
+ else
+ {
+ page = 0;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ return page;
+}
+
+void ColorNotebook::removePage( GType page_type, guint submode )
+{
+ GtkWidget *page = 0;
+
+ page = getPage(page_type, submode);
+ if ( page )
+ {
+ gint where = gtk_notebook_page_num (GTK_NOTEBOOK (_book), page);
+ if ( where >= 0 )
+ {
+ if ( gtk_notebook_get_current_page (GTK_NOTEBOOK (_book)) == where )
+ {
+// getColorAlpha(_color, &_alpha);
+ }
+ gtk_notebook_remove_page (GTK_NOTEBOOK (_book), where);
+ }
+ }
+}
diff --git a/src/widgets/sp-color-notebook.h b/src/widgets/sp-color-notebook.h
new file mode 100644
index 000000000..ad79437da
--- /dev/null
+++ b/src/widgets/sp-color-notebook.h
@@ -0,0 +1,105 @@
+#ifndef __SP_COLOR_NOTEBOOK_H__
+#define __SP_COLOR_NOTEBOOK_H__
+
+/*
+ * A block of 3 color sliders plus spinbuttons
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ *
+ * This code is in public domain
+ */
+
+#include <gtk/gtkvbox.h>
+#include <gtk/gtknotebook.h>
+#include <gtk/gtkentry.h>
+#include "../color.h"
+#include "sp-color-selector.h"
+
+#include <glib.h>
+
+
+
+struct SPColorNotebook;
+
+class ColorNotebook: public ColorSelector
+{
+public:
+ ColorNotebook( SPColorSelector* csel );
+ virtual ~ColorNotebook();
+
+ virtual void init();
+
+ SPColorSelector* getCurrentSelector();
+ void switchPage( GtkNotebook *notebook, GtkNotebookPage *page, guint page_num );
+
+ GtkWidget* addPage( GType page_type, guint submode );
+ void removePage( GType page_type, guint submode );
+ GtkWidget* getPage( GType page_type, guint submode );
+
+ gint menuHandler( GdkEvent* event );
+
+protected:
+ static void _rgbaEntryChangedHook( GtkEntry* entry, SPColorNotebook *colorbook );
+ static void _entryGrabbed( SPColorSelector *csel, SPColorNotebook *colorbook );
+ static void _entryDragged( SPColorSelector *csel, SPColorNotebook *colorbook );
+ static void _entryReleased( SPColorSelector *csel, SPColorNotebook *colorbook );
+ static void _entryChanged( SPColorSelector *csel, SPColorNotebook *colorbook );
+ static void _entryModified( SPColorSelector *csel, SPColorNotebook *colorbook );
+
+ virtual void _colorChanged( const SPColor& color, gfloat alpha );
+
+ void _rgbaEntryChanged( GtkEntry* entry );
+ void _updateRgbaEntry( const SPColor& color, gfloat alpha );
+
+ gboolean _updating : 1;
+ gboolean _updatingrgba : 1;
+ gboolean _dragging : 1;
+ gulong _switchId;
+ gulong _entryId;
+ GtkWidget *_book;
+ GtkWidget *_rgbal, *_rgbae; /* RGBA entry */
+ GtkWidget *_p; /* Color preview */
+ GtkWidget *_btn;
+ GtkWidget *_popup;
+ GPtrArray *_trackerList;
+
+private:
+ // By default, disallow copy constructor and assignment operator
+ ColorNotebook( const ColorNotebook& obj );
+ ColorNotebook& operator=( const ColorNotebook& obj );
+};
+
+
+
+#define SP_TYPE_COLOR_NOTEBOOK (sp_color_notebook_get_type ())
+#define SP_COLOR_NOTEBOOK(o) (GTK_CHECK_CAST ((o), SP_TYPE_COLOR_NOTEBOOK, SPColorNotebook))
+#define SP_COLOR_NOTEBOOK_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_COLOR_NOTEBOOK, SPColorNotebookClass))
+#define SP_IS_COLOR_NOTEBOOK(o) (GTK_CHECK_TYPE ((o), SP_TYPE_COLOR_NOTEBOOK))
+#define SP_IS_COLOR_NOTEBOOK_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_COLOR_NOTEBOOK))
+
+struct SPColorNotebook {
+ SPColorSelector parent; /* Parent */
+};
+
+struct SPColorNotebookClass {
+ SPColorSelectorClass parent_class;
+
+ void (* grabbed) (SPColorNotebook *rgbsel);
+ void (* dragged) (SPColorNotebook *rgbsel);
+ void (* released) (SPColorNotebook *rgbsel);
+ void (* changed) (SPColorNotebook *rgbsel);
+};
+
+GtkType sp_color_notebook_get_type (void);
+
+GtkWidget *sp_color_notebook_new (void);
+
+/* void sp_color_notebook_set_mode (SPColorNotebook *csel, SPColorNotebookMode mode); */
+/* SPColorNotebookMode sp_color_notebook_get_mode (SPColorNotebook *csel); */
+
+
+
+#endif
diff --git a/src/widgets/sp-color-preview.cpp b/src/widgets/sp-color-preview.cpp
new file mode 100644
index 000000000..0139abc5a
--- /dev/null
+++ b/src/widgets/sp-color-preview.cpp
@@ -0,0 +1,196 @@
+#define __SP_COLOR_PREVIEW_C__
+
+/*
+ * A simple color preview widget
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "../display/nr-plain-stuff-gdk.h"
+#include "sp-color-preview.h"
+
+#define SPCP_DEFAULT_WIDTH 32
+#define SPCP_DEFAULT_HEIGHT 11
+
+static void sp_color_preview_class_init (SPColorPreviewClass *klass);
+static void sp_color_preview_init (SPColorPreview *image);
+static void sp_color_preview_destroy (GtkObject *object);
+
+static void sp_color_preview_size_request (GtkWidget *widget, GtkRequisition *requisition);
+static void sp_color_preview_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
+static gint sp_color_preview_expose (GtkWidget *widget, GdkEventExpose *event);
+
+static void sp_color_preview_paint (SPColorPreview *cp, GdkRectangle *area);
+
+static GtkWidgetClass *parent_class;
+
+GtkType
+sp_color_preview_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GtkTypeInfo info = {
+ "SPColorPreview",
+ sizeof (SPColorPreview),
+ sizeof (SPColorPreviewClass),
+ (GtkClassInitFunc) sp_color_preview_class_init,
+ (GtkObjectInitFunc) sp_color_preview_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_WIDGET, &info);
+ }
+ return type;
+}
+
+static void
+sp_color_preview_class_init (SPColorPreviewClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ parent_class = (GtkWidgetClass*)gtk_type_class (GTK_TYPE_WIDGET);
+
+ object_class->destroy = sp_color_preview_destroy;
+
+ widget_class->size_request = sp_color_preview_size_request;
+ widget_class->size_allocate = sp_color_preview_size_allocate;
+ widget_class->expose_event = sp_color_preview_expose;
+}
+
+static void
+sp_color_preview_init (SPColorPreview *image)
+{
+ GTK_WIDGET_SET_FLAGS (image, GTK_NO_WINDOW);
+
+ image->rgba = 0xffffffff;
+}
+
+static void
+sp_color_preview_destroy (GtkObject *object)
+{
+ SPColorPreview *image;
+
+ image = SP_COLOR_PREVIEW (object);
+
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+static void
+sp_color_preview_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+ SPColorPreview *slider;
+
+ slider = SP_COLOR_PREVIEW (widget);
+
+ requisition->width = SPCP_DEFAULT_WIDTH;
+ requisition->height = SPCP_DEFAULT_HEIGHT;
+}
+
+static void
+sp_color_preview_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ SPColorPreview *image;
+
+ image = SP_COLOR_PREVIEW (widget);
+
+ widget->allocation = *allocation;
+
+ if (GTK_WIDGET_DRAWABLE (image)) {
+ gtk_widget_queue_draw (GTK_WIDGET (image));
+ }
+}
+
+static gint
+sp_color_preview_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+ SPColorPreview *cp;
+
+ cp = SP_COLOR_PREVIEW (widget);
+
+ if (GTK_WIDGET_DRAWABLE (widget)) {
+ sp_color_preview_paint (cp, &event->area);
+ }
+
+ return TRUE;
+}
+
+GtkWidget *
+sp_color_preview_new (guint32 rgba)
+{
+ SPColorPreview *image;
+
+ image = (SPColorPreview*)gtk_type_new (SP_TYPE_COLOR_PREVIEW);
+
+ sp_color_preview_set_rgba32 (image, rgba);
+
+ return (GtkWidget *) image;
+}
+
+void
+sp_color_preview_set_rgba32 (SPColorPreview *cp, guint32 rgba)
+{
+ cp->rgba = rgba;
+
+ if (GTK_WIDGET_DRAWABLE (cp)) {
+ gtk_widget_queue_draw (GTK_WIDGET (cp));
+ }
+}
+
+static void
+sp_color_preview_paint (SPColorPreview *cp, GdkRectangle *area)
+{
+ GtkWidget *widget;
+ GdkRectangle warea, carea;
+ GdkRectangle wpaint, cpaint;
+ gint w2;
+
+ widget = GTK_WIDGET (cp);
+
+ warea.x = widget->allocation.x;
+ warea.y = widget->allocation.y;
+ warea.width = widget->allocation.width;
+ warea.height = widget->allocation.height;
+
+ if (!gdk_rectangle_intersect (area, &warea, &wpaint)) return;
+
+ /* Transparent area */
+
+ w2 = warea.width / 2;
+
+ carea.x = warea.x;
+ carea.y = warea.y;
+ carea.width = w2;
+ carea.height = warea.height;
+
+ if (gdk_rectangle_intersect (area, &carea, &cpaint)) {
+ nr_gdk_draw_rgba32_solid (widget->window, widget->style->black_gc,
+ cpaint.x, cpaint.y,
+ cpaint.width, cpaint.height,
+ cp->rgba);
+ }
+
+ /* Solid area */
+
+ carea.x = warea.x + w2;
+ carea.y = warea.y;
+ carea.width = warea.width - w2;
+ carea.height = warea.height;
+
+ if (gdk_rectangle_intersect (area, &carea, &cpaint)) {
+ nr_gdk_draw_rgba32_solid (widget->window, widget->style->black_gc,
+ cpaint.x, cpaint.y,
+ cpaint.width, cpaint.height,
+ cp->rgba | 0xff);
+ }
+}
+
diff --git a/src/widgets/sp-color-preview.h b/src/widgets/sp-color-preview.h
new file mode 100644
index 000000000..d9ba154e1
--- /dev/null
+++ b/src/widgets/sp-color-preview.h
@@ -0,0 +1,47 @@
+#ifndef __SP_COLOR_PREVIEW_H__
+#define __SP_COLOR_PREVIEW_H__
+
+/*
+ * A simple color preview widget
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <gtk/gtkwidget.h>
+
+#include <glib.h>
+
+
+
+#define SP_TYPE_COLOR_PREVIEW (sp_color_preview_get_type ())
+#define SP_COLOR_PREVIEW(o) (GTK_CHECK_CAST ((o), SP_TYPE_COLOR_PREVIEW, SPColorPreview))
+#define SP_COLOR_PREVIEW_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_COLOR_PREVIEW, SPColorPreviewClass))
+#define SP_IS_COLOR_PREVIEW(o) (GTK_CHECK_TYPE ((o), SP_TYPE_COLOR_PREVIEW))
+#define SP_IS_COLOR_PREVIEW_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_COLOR_PREVIEW))
+
+struct SPColorPreview {
+ GtkWidget widget;
+
+ guint32 rgba;
+};
+
+struct SPColorPreviewClass {
+ GtkWidgetClass parent_class;
+};
+
+GtkType sp_color_preview_get_type (void);
+
+GtkWidget *sp_color_preview_new (guint32 rgba);
+
+void sp_color_preview_set_rgba32 (SPColorPreview *cp, guint32 color);
+
+
+
+
+#endif
diff --git a/src/widgets/sp-color-scales.cpp b/src/widgets/sp-color-scales.cpp
new file mode 100644
index 000000000..8acd96bbb
--- /dev/null
+++ b/src/widgets/sp-color-scales.cpp
@@ -0,0 +1,741 @@
+/*
+ * bulia byak <buliabyak@users.sf.net>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <math.h>
+#include <gtk/gtk.h>
+#include <glibmm/i18n.h>
+#include "../dialogs/dialog-events.h"
+#include "sp-color-scales.h"
+
+#define CSC_CHANNEL_R (1 << 0)
+#define CSC_CHANNEL_G (1 << 1)
+#define CSC_CHANNEL_B (1 << 2)
+#define CSC_CHANNEL_A (1 << 3)
+#define CSC_CHANNEL_H (1 << 0)
+#define CSC_CHANNEL_S (1 << 1)
+#define CSC_CHANNEL_V (1 << 2)
+#define CSC_CHANNEL_C (1 << 0)
+#define CSC_CHANNEL_M (1 << 1)
+#define CSC_CHANNEL_Y (1 << 2)
+#define CSC_CHANNEL_K (1 << 3)
+#define CSC_CHANNEL_CMYKA (1 << 4)
+
+#define CSC_CHANNELS_ALL 0
+
+
+G_BEGIN_DECLS
+
+static void sp_color_scales_class_init (SPColorScalesClass *klass);
+static void sp_color_scales_init (SPColorScales *cs);
+static void sp_color_scales_destroy (GtkObject *object);
+
+static void sp_color_scales_show_all (GtkWidget *widget);
+static void sp_color_scales_hide_all (GtkWidget *widget);
+
+static const gchar *sp_color_scales_hue_map (void);
+
+G_END_DECLS
+
+static SPColorSelectorClass *parent_class;
+
+#define XPAD 4
+#define YPAD 1
+
+GType
+sp_color_scales_get_type (void)
+{
+ static GType type = 0;
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (SPColorScalesClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) sp_color_scales_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (SPColorScales),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) sp_color_scales_init,
+ NULL
+ };
+
+ type = g_type_register_static (SP_TYPE_COLOR_SELECTOR,
+ "SPColorScales",
+ &info,
+ static_cast< GTypeFlags > (0) );
+ }
+ return type;
+}
+
+static void
+sp_color_scales_class_init (SPColorScalesClass *klass)
+{
+ static const gchar* nameset[] = {N_("RGB"), N_("HSL"), N_("CMYK"), 0};
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ SPColorSelectorClass *selector_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+ selector_class = SP_COLOR_SELECTOR_CLASS (klass);
+
+ parent_class = SP_COLOR_SELECTOR_CLASS (g_type_class_peek_parent (klass));
+
+ selector_class->name = nameset;
+ selector_class->submode_count = 3;
+
+ object_class->destroy = sp_color_scales_destroy;
+
+ widget_class->show_all = sp_color_scales_show_all;
+ widget_class->hide_all = sp_color_scales_hide_all;
+}
+
+ColorScales::ColorScales( SPColorSelector* csel )
+ : ColorSelector( csel ),
+ _mode( SP_COLOR_SCALES_MODE_NONE ),
+ _rangeLimit( 255.0 ),
+ _updating( FALSE ),
+ _dragging( FALSE )
+{
+ gint i = 0;
+
+ for (i = 0; i < 5; i++) {
+ _l[i] = 0;
+ _a[i] = 0;
+ _s[i] = 0;
+ _b[i] = 0;
+ }
+}
+
+ColorScales::~ColorScales()
+{
+ gint i = 0;
+
+ for (i = 0; i < 5; i++) {
+ _l[i] = 0;
+ _a[i] = 0;
+ _s[i] = 0;
+ _b[i] = 0;
+ }
+}
+
+void sp_color_scales_init (SPColorScales *cs)
+{
+ SP_COLOR_SELECTOR(cs)->base = new ColorScales( SP_COLOR_SELECTOR(cs) );
+
+ if ( SP_COLOR_SELECTOR(cs)->base )
+ {
+ SP_COLOR_SELECTOR(cs)->base->init();
+ }
+}
+
+void ColorScales::init()
+{
+ GtkWidget *t;
+ gint i;
+
+ _updating = FALSE;
+ _dragging = FALSE;
+
+ _tt = gtk_tooltips_new();
+
+ t = gtk_table_new (5, 3, FALSE);
+ gtk_widget_show (t);
+ gtk_box_pack_start (GTK_BOX (_csel), t, TRUE, TRUE, 0);
+
+ /* Create components */
+ for (i = 0; i < static_cast< gint > (G_N_ELEMENTS(_a)) ; i++) {
+ /* Label */
+ _l[i] = gtk_label_new("");
+ gtk_misc_set_alignment (GTK_MISC (_l[i]), 1.0, 0.5);
+ gtk_widget_show (_l[i]);
+ gtk_table_attach (GTK_TABLE (t), _l[i], 0, 1, i, i + 1, GTK_FILL, GTK_FILL, XPAD, YPAD);
+ /* Adjustment */
+ _a[i] = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, _rangeLimit, 1.0, 10.0, 10.0);
+ /* Slider */
+ _s[i] = sp_color_slider_new (_a[i]);
+ gtk_widget_show (_s[i]);
+ gtk_table_attach (GTK_TABLE (t), _s[i], 1, 2, i, i + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)GTK_FILL, XPAD, YPAD);
+
+ /* Spinbutton */
+ _b[i] = gtk_spin_button_new (GTK_ADJUSTMENT (_a[i]), 1.0, 0);
+ sp_dialog_defocus_on_enter (_b[i]);
+ gtk_label_set_mnemonic_widget (GTK_LABEL(_l[i]), _b[i]);
+ gtk_widget_show (_b[i]);
+ gtk_table_attach (GTK_TABLE (t), _b[i], 2, 3, i, i + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, XPAD, YPAD);
+
+ /* Attach channel value to adjustment */
+ gtk_object_set_data (GTK_OBJECT (_a[i]), "channel", GINT_TO_POINTER (i));
+ /* Signals */
+ gtk_signal_connect (GTK_OBJECT (_a[i]), "value_changed",
+ GTK_SIGNAL_FUNC (_adjustmentAnyChanged), _csel);
+ gtk_signal_connect (GTK_OBJECT (_s[i]), "grabbed",
+ GTK_SIGNAL_FUNC (_sliderAnyGrabbed), _csel);
+ gtk_signal_connect (GTK_OBJECT (_s[i]), "released",
+ GTK_SIGNAL_FUNC (_sliderAnyReleased), _csel);
+ gtk_signal_connect (GTK_OBJECT (_s[i]), "changed",
+ GTK_SIGNAL_FUNC (_sliderAnyChanged), _csel);
+ }
+
+ /* Initial mode is none, so it works */
+ setMode(SP_COLOR_SCALES_MODE_RGB);
+}
+
+static void
+sp_color_scales_destroy (GtkObject *object)
+{
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+static void
+sp_color_scales_show_all (GtkWidget *widget)
+{
+ gtk_widget_show (widget);
+}
+
+static void
+sp_color_scales_hide_all (GtkWidget *widget)
+{
+ gtk_widget_hide (widget);
+}
+
+GtkWidget *
+sp_color_scales_new (void)
+{
+ SPColorScales *csel;
+
+ csel = (SPColorScales*)gtk_type_new (SP_TYPE_COLOR_SCALES);
+
+ return GTK_WIDGET (csel);
+}
+
+void ColorScales::_recalcColor( gboolean changing )
+{
+ if ( changing )
+ {
+ SPColor color;
+ gfloat alpha = 1.0;
+ gfloat c[5];
+
+ switch (_mode) {
+ case SP_COLOR_SCALES_MODE_RGB:
+ case SP_COLOR_SCALES_MODE_HSV:
+ _getRgbaFloatv(c);
+ sp_color_set_rgb_float (&color, c[0], c[1], c[2]);
+ alpha = c[3];
+ break;
+ case SP_COLOR_SCALES_MODE_CMYK:
+ _getCmykaFloatv( c );
+ sp_color_set_cmyk_float (&color, c[0], c[1], c[2], c[3]);
+ alpha = c[4];
+ break;
+ default:
+ g_warning ("file %s: line %d: Illegal color selector mode %d", __FILE__, __LINE__, _mode);
+ break;
+ }
+ _updateInternals( color, alpha, _dragging );
+ }
+ else
+ {
+ _updateInternals( _color, _alpha, _dragging );
+ }
+}
+
+/* Helpers for setting color value */
+gfloat ColorScales::getScaled( const GtkAdjustment *a )
+{
+ gfloat val = a->value / a->upper;
+ return val;
+}
+
+void ColorScales::setScaled( GtkAdjustment *a, gfloat v )
+{
+ gfloat val = v * a->upper;
+ gtk_adjustment_set_value( a, val );
+}
+
+void ColorScales::_setRangeLimit( gdouble upper )
+{
+ _rangeLimit = upper;
+ for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(_a)); i++ ) {
+ _a[i]->upper = upper;
+ gtk_adjustment_changed( _a[i] );
+ }
+}
+
+void ColorScales::_colorChanged( const SPColor& color, gfloat alpha )
+{
+ gfloat tmp[3];
+ gfloat c[5] = {0.0, 0.0, 0.0, 0.0};
+
+ switch (_mode) {
+ case SP_COLOR_SCALES_MODE_RGB:
+ sp_color_get_rgb_floatv (&color, c);
+ c[3] = alpha;
+ c[4] = 0.0;
+ break;
+ case SP_COLOR_SCALES_MODE_HSV:
+ sp_color_get_rgb_floatv (&color, tmp);
+ c[0] = getScaled( _a[0] );
+ sp_color_rgb_to_hsl_floatv (c, tmp[0], tmp[1], tmp[2]);
+ c[3] = alpha;
+ c[4] = 0.0;
+ break;
+ case SP_COLOR_SCALES_MODE_CMYK:
+ sp_color_get_cmyk_floatv (&color, c);
+ c[4] = alpha;
+ break;
+ default:
+ g_warning ("file %s: line %d: Illegal color selector mode %d", __FILE__, __LINE__, _mode);
+ break;
+ }
+
+ _updating = TRUE;
+ setScaled( _a[0], c[0] );
+ setScaled( _a[1], c[1] );
+ setScaled( _a[2], c[2] );
+ setScaled( _a[3], c[3] );
+ setScaled( _a[4], c[4] );
+ _updateSliders( CSC_CHANNELS_ALL );
+ _updating = FALSE;
+}
+
+void ColorScales::_getRgbaFloatv( gfloat *rgba )
+{
+ g_return_if_fail (rgba != NULL);
+
+ switch (_mode) {
+ case SP_COLOR_SCALES_MODE_RGB:
+ rgba[0] = getScaled(_a[0]);
+ rgba[1] = getScaled(_a[1]);
+ rgba[2] = getScaled(_a[2]);
+ rgba[3] = getScaled(_a[3]);
+ break;
+ case SP_COLOR_SCALES_MODE_HSV:
+ sp_color_hsl_to_rgb_floatv (rgba, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]));
+ rgba[3] = getScaled(_a[3]);
+ break;
+ case SP_COLOR_SCALES_MODE_CMYK:
+ sp_color_cmyk_to_rgb_floatv (rgba, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3]));
+ rgba[3] = getScaled(_a[4]);
+ break;
+ default:
+ g_warning ("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__);
+ break;
+ }
+}
+
+void ColorScales::_getCmykaFloatv( gfloat *cmyka )
+{
+ gfloat rgb[3];
+
+ g_return_if_fail (cmyka != NULL);
+
+ switch (_mode) {
+ case SP_COLOR_SCALES_MODE_RGB:
+ sp_color_rgb_to_cmyk_floatv (cmyka, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]));
+ cmyka[4] = getScaled(_a[3]);
+ break;
+ case SP_COLOR_SCALES_MODE_HSV:
+ sp_color_hsl_to_rgb_floatv (rgb, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]));
+ sp_color_rgb_to_cmyk_floatv (cmyka, rgb[0], rgb[1], rgb[2]);
+ cmyka[4] = getScaled(_a[3]);
+ break;
+ case SP_COLOR_SCALES_MODE_CMYK:
+ cmyka[0] = getScaled(_a[0]);
+ cmyka[1] = getScaled(_a[1]);
+ cmyka[2] = getScaled(_a[2]);
+ cmyka[3] = getScaled(_a[3]);
+ cmyka[4] = getScaled(_a[4]);
+ break;
+ default:
+ g_warning ("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__);
+ break;
+ }
+}
+
+guint32 ColorScales::_getRgba32()
+{
+ gfloat c[4];
+ guint32 rgba;
+
+ _getRgbaFloatv(c);
+
+ rgba = SP_RGBA32_F_COMPOSE (c[0], c[1], c[2], c[3]);
+
+ return rgba;
+}
+
+void ColorScales::setMode(SPColorScalesMode mode)
+{
+ gfloat rgba[4];
+ gfloat c[4];
+
+ if (_mode == mode) return;
+
+ if ((_mode == SP_COLOR_SCALES_MODE_RGB) ||
+ (_mode == SP_COLOR_SCALES_MODE_HSV) ||
+ (_mode == SP_COLOR_SCALES_MODE_CMYK)) {
+ _getRgbaFloatv(rgba);
+ } else {
+ rgba[0] = rgba[1] = rgba[2] = rgba[3] = 1.0;
+ }
+
+ _mode = mode;
+
+ switch (mode) {
+ case SP_COLOR_SCALES_MODE_RGB:
+ _setRangeLimit(255.0);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[0]), _("_R"));
+ gtk_tooltips_set_tip (_tt, _s[0], _("Red"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[0], _("Red"), NULL);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[1]), _("_G"));
+ gtk_tooltips_set_tip (_tt, _s[1], _("Green"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[1], _("Green"), NULL);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[2]), _("_B"));
+ gtk_tooltips_set_tip (_tt, _s[2], _("Blue"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[2], _("Blue"), NULL);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[3]), _("_A"));
+ gtk_tooltips_set_tip (_tt, _s[3], _("Alpha (opacity)"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[3], _("Alpha (opacity)"), NULL);
+ sp_color_slider_set_map (SP_COLOR_SLIDER (_s[0]), NULL);
+ gtk_widget_hide (_l[4]);
+ gtk_widget_hide (_s[4]);
+ gtk_widget_hide (_b[4]);
+ _updating = TRUE;
+ setScaled( _a[0], rgba[0] );
+ setScaled( _a[1], rgba[1] );
+ setScaled( _a[2], rgba[2] );
+ setScaled( _a[3], rgba[3] );
+ _updating = FALSE;
+ _updateSliders( CSC_CHANNELS_ALL );
+ break;
+ case SP_COLOR_SCALES_MODE_HSV:
+ _setRangeLimit(255.0);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[0]), _("_H"));
+ gtk_tooltips_set_tip (_tt, _s[0], _("Hue"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[0], _("Hue"), NULL);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[1]), _("_S"));
+ gtk_tooltips_set_tip (_tt, _s[1], _("Saturation"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[1], _("Saturation"), NULL);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[2]), _("_L"));
+ gtk_tooltips_set_tip (_tt, _s[2], _("Lightness"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[2], _("Lightness"), NULL);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[3]), _("_A"));
+ gtk_tooltips_set_tip (_tt, _s[3], _("Alpha (opacity)"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[3], _("Alpha (opacity)"), NULL);
+ sp_color_slider_set_map (SP_COLOR_SLIDER (_s[0]), (guchar*)sp_color_scales_hue_map ());
+ gtk_widget_hide (_l[4]);
+ gtk_widget_hide (_s[4]);
+ gtk_widget_hide (_b[4]);
+ _updating = TRUE;
+ c[0] = 0.0;
+ sp_color_rgb_to_hsl_floatv (c, rgba[0], rgba[1], rgba[2]);
+ setScaled( _a[0], c[0] );
+ setScaled( _a[1], c[1] );
+ setScaled( _a[2], c[2] );
+ setScaled( _a[3], rgba[3] );
+ _updating = FALSE;
+ _updateSliders( CSC_CHANNELS_ALL );
+ break;
+ case SP_COLOR_SCALES_MODE_CMYK:
+ _setRangeLimit(100.0);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[0]), _("_C"));
+ gtk_tooltips_set_tip (_tt, _s[0], _("Cyan"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[0], _("Cyan"), NULL);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[1]), _("_M"));
+ gtk_tooltips_set_tip (_tt, _s[1], _("Magenta"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[1], _("Magenta"), NULL);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[2]), _("_Y"));
+ gtk_tooltips_set_tip (_tt, _s[2], _("Yellow"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[2], _("Yellow"), NULL);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[3]), _("_K"));
+ gtk_tooltips_set_tip (_tt, _s[3], _("Black"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[3], _("Black"), NULL);
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[4]), _("_A"));
+ gtk_tooltips_set_tip (_tt, _s[4], _("Alpha (opacity)"), NULL);
+ gtk_tooltips_set_tip (_tt, _b[4], _("Alpha (opacity)"), NULL);
+ sp_color_slider_set_map (SP_COLOR_SLIDER (_s[0]), NULL);
+ gtk_widget_show (_l[4]);
+ gtk_widget_show (_s[4]);
+ gtk_widget_show (_b[4]);
+ _updating = TRUE;
+ sp_color_rgb_to_cmyk_floatv (c, rgba[0], rgba[1], rgba[2]);
+ setScaled( _a[0], c[0] );
+ setScaled( _a[1], c[1] );
+ setScaled( _a[2], c[2] );
+ setScaled( _a[3], c[3] );
+ setScaled( _a[4], rgba[3] );
+ _updating = FALSE;
+ _updateSliders( CSC_CHANNELS_ALL );
+ break;
+ default:
+ g_warning ("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__);
+ break;
+ }
+}
+
+SPColorScalesMode ColorScales::getMode() const
+{
+ return _mode;
+}
+
+void ColorScales::setSubmode( guint submode )
+{
+ g_return_if_fail (_csel != NULL);
+ g_return_if_fail (SP_IS_COLOR_SCALES (_csel));
+ g_return_if_fail (submode < 3);
+
+ switch ( submode )
+ {
+ default:
+ case 0:
+ setMode(SP_COLOR_SCALES_MODE_RGB);
+ break;
+ case 1:
+ setMode(SP_COLOR_SCALES_MODE_HSV);
+ break;
+ case 2:
+ setMode(SP_COLOR_SCALES_MODE_CMYK);
+ break;
+ }
+}
+
+guint ColorScales::getSubmode() const
+{
+ guint submode = 0;
+
+ switch ( _mode )
+ {
+ case SP_COLOR_SCALES_MODE_HSV:
+ submode = 1;
+ break;
+ case SP_COLOR_SCALES_MODE_CMYK:
+ submode = 2;
+ break;
+ case SP_COLOR_SCALES_MODE_RGB:
+ default:
+ submode = 0;
+ }
+
+ return submode;
+}
+
+void ColorScales::_adjustmentAnyChanged( GtkAdjustment *adjustment, SPColorScales *cs )
+{
+// TODO check this. It looks questionable:
+ // if a value is entered between 0 and 1 exclusive, normalize it to (int) 0..255 or 0..100
+ if (adjustment->value > 0.0 && adjustment->value < 1.0) {
+ gtk_adjustment_set_value( adjustment, floor ((adjustment->value) * adjustment->upper + 0.5) );
+ }
+
+ gint channel = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (adjustment), "channel"));
+
+ _adjustmentChanged(cs, channel);
+}
+
+void ColorScales::_sliderAnyGrabbed( SPColorSlider *slider, SPColorScales *cs )
+{
+ ColorScales* scales = (ColorScales*)(SP_COLOR_SELECTOR(cs)->base);
+ if (!scales->_dragging) {
+ scales->_dragging = TRUE;
+ scales->_grabbed();
+ scales->_recalcColor( FALSE );
+ }
+}
+
+void ColorScales::_sliderAnyReleased( SPColorSlider *slider, SPColorScales *cs )
+{
+ ColorScales* scales = (ColorScales*)(SP_COLOR_SELECTOR(cs)->base);
+ if (scales->_dragging) {
+ scales->_dragging = FALSE;
+ scales->_released();
+ scales->_recalcColor( FALSE );
+ }
+}
+
+void ColorScales::_sliderAnyChanged( SPColorSlider *slider, SPColorScales *cs )
+{
+ ColorScales* scales = (ColorScales*)(SP_COLOR_SELECTOR(cs)->base);
+
+ scales->_recalcColor( TRUE );
+}
+
+void ColorScales::_adjustmentChanged( SPColorScales *cs, guint channel )
+{
+ ColorScales* scales = (ColorScales*)(SP_COLOR_SELECTOR(cs)->base);
+ if (scales->_updating) return;
+
+ scales->_updating = TRUE;
+
+ scales->_updateSliders( (1 << channel) );
+
+ scales->_recalcColor (TRUE);
+
+ scales->_updating = FALSE;
+}
+
+void ColorScales::_updateSliders( guint channels )
+{
+ gfloat rgb0[3], rgbm[3], rgb1[3];
+#ifdef SPCS_PREVIEW
+ guint32 rgba;
+#endif
+ switch (_mode) {
+ case SP_COLOR_SCALES_MODE_RGB:
+ if ((channels != CSC_CHANNEL_R) && (channels != CSC_CHANNEL_A)) {
+ /* Update red */
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[0]),
+ SP_RGBA32_F_COMPOSE (0.0, getScaled(_a[1]), getScaled(_a[2]), 1.0),
+ SP_RGBA32_F_COMPOSE (0.5, getScaled(_a[1]), getScaled(_a[2]), 1.0),
+ SP_RGBA32_F_COMPOSE (1.0, getScaled(_a[1]), getScaled(_a[2]), 1.0));
+ }
+ if ((channels != CSC_CHANNEL_G) && (channels != CSC_CHANNEL_A)) {
+ /* Update green */
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[1]),
+ SP_RGBA32_F_COMPOSE (getScaled(_a[0]), 0.0, getScaled(_a[2]), 1.0),
+ SP_RGBA32_F_COMPOSE (getScaled(_a[0]), 0.5, getScaled(_a[2]), 1.0),
+ SP_RGBA32_F_COMPOSE (getScaled(_a[0]), 1.0, getScaled(_a[2]), 1.0));
+ }
+ if ((channels != CSC_CHANNEL_B) && (channels != CSC_CHANNEL_A)) {
+ /* Update blue */
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[2]),
+ SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), 0.0, 1.0),
+ SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), 0.5, 1.0),
+ SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), 1.0, 1.0));
+ }
+ if (channels != CSC_CHANNEL_A) {
+ /* Update alpha */
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[3]),
+ SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.0),
+ SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.5),
+ SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 1.0));
+ }
+ break;
+ case SP_COLOR_SCALES_MODE_HSV:
+ /* Hue is never updated */
+ if ((channels != CSC_CHANNEL_S) && (channels != CSC_CHANNEL_A)) {
+ /* Update saturation */
+ sp_color_hsl_to_rgb_floatv (rgb0, getScaled(_a[0]), 0.0, getScaled(_a[2]));
+ sp_color_hsl_to_rgb_floatv (rgbm, getScaled(_a[0]), 0.5, getScaled(_a[2]));
+ sp_color_hsl_to_rgb_floatv (rgb1, getScaled(_a[0]), 1.0, getScaled(_a[2]));
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[1]),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0));
+ }
+ if ((channels != CSC_CHANNEL_V) && (channels != CSC_CHANNEL_A)) {
+ /* Update value */
+ sp_color_hsl_to_rgb_floatv (rgb0, getScaled(_a[0]), getScaled(_a[1]), 0.0);
+ sp_color_hsl_to_rgb_floatv (rgbm, getScaled(_a[0]), getScaled(_a[1]), 0.5);
+ sp_color_hsl_to_rgb_floatv (rgb1, getScaled(_a[0]), getScaled(_a[1]), 1.0);
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[2]),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0));
+ }
+ if (channels != CSC_CHANNEL_A) {
+ /* Update alpha */
+ sp_color_hsl_to_rgb_floatv (rgb0, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]));
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[3]),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 0.0),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 0.5),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0));
+ }
+ break;
+ case SP_COLOR_SCALES_MODE_CMYK:
+ if ((channels != CSC_CHANNEL_C) && (channels != CSC_CHANNEL_CMYKA)) {
+ /* Update C */
+ sp_color_cmyk_to_rgb_floatv (rgb0, 0.0, getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3]));
+ sp_color_cmyk_to_rgb_floatv (rgbm, 0.5, getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3]));
+ sp_color_cmyk_to_rgb_floatv (rgb1, 1.0, getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3]));
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[0]),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0));
+ }
+ if ((channels != CSC_CHANNEL_M) && (channels != CSC_CHANNEL_CMYKA)) {
+ /* Update M */
+ sp_color_cmyk_to_rgb_floatv (rgb0, getScaled(_a[0]), 0.0, getScaled(_a[2]), getScaled(_a[3]));
+ sp_color_cmyk_to_rgb_floatv (rgbm, getScaled(_a[0]), 0.5, getScaled(_a[2]), getScaled(_a[3]));
+ sp_color_cmyk_to_rgb_floatv (rgb1, getScaled(_a[0]), 1.0, getScaled(_a[2]), getScaled(_a[3]));
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[1]),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0));
+ }
+ if ((channels != CSC_CHANNEL_Y) && (channels != CSC_CHANNEL_CMYKA)) {
+ /* Update Y */
+ sp_color_cmyk_to_rgb_floatv (rgb0, getScaled(_a[0]), getScaled(_a[1]), 0.0, getScaled(_a[3]));
+ sp_color_cmyk_to_rgb_floatv (rgbm, getScaled(_a[0]), getScaled(_a[1]), 0.5, getScaled(_a[3]));
+ sp_color_cmyk_to_rgb_floatv (rgb1, getScaled(_a[0]), getScaled(_a[1]), 1.0, getScaled(_a[3]));
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[2]),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0));
+ }
+ if ((channels != CSC_CHANNEL_K) && (channels != CSC_CHANNEL_CMYKA)) {
+ /* Update K */
+ sp_color_cmyk_to_rgb_floatv (rgb0, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.0);
+ sp_color_cmyk_to_rgb_floatv (rgbm, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.5);
+ sp_color_cmyk_to_rgb_floatv (rgb1, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 1.0);
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[3]),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0),
+ SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0));
+ }
+ if (channels != CSC_CHANNEL_CMYKA) {
+ /* Update alpha */
+ sp_color_cmyk_to_rgb_floatv (rgb0, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3]));
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[4]),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 0.0),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 0.5),
+ SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0));
+ }
+ break;
+ default:
+ g_warning ("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__);
+ break;
+ }
+
+ // Force the internal color to be updated
+ if ( !_updating )
+ {
+ _recalcColor( TRUE );
+ }
+
+#ifdef SPCS_PREVIEW
+ rgba = sp_color_scales_get_rgba32 (cs);
+ sp_color_preview_set_rgba32 (SP_COLOR_PREVIEW (_p), rgba);
+#endif
+}
+
+static const gchar *
+sp_color_scales_hue_map (void)
+{
+ static gchar *map = NULL;
+
+ if (!map) {
+ gchar *p;
+ gint h;
+ map = g_new (gchar, 4 * 1024);
+ p = map;
+ for (h = 0; h < 1024; h++) {
+ gfloat rgb[3];
+ sp_color_hsl_to_rgb_floatv (rgb, h / 1024.0, 1.0, 0.5);
+ *p++ = SP_COLOR_F_TO_U (rgb[0]);
+ *p++ = SP_COLOR_F_TO_U (rgb[1]);
+ *p++ = SP_COLOR_F_TO_U (rgb[2]);
+ *p++ = 255;
+ }
+ }
+
+ return map;
+}
+
+
diff --git a/src/widgets/sp-color-scales.h b/src/widgets/sp-color-scales.h
new file mode 100644
index 000000000..efb557f9b
--- /dev/null
+++ b/src/widgets/sp-color-scales.h
@@ -0,0 +1,108 @@
+#ifndef SEEN_SP_COLOR_SCALES_H
+#define SEEN_SP_COLOR_SCALES_H
+
+#include <glib/gtypes.h>
+#include <gtk/gtktooltips.h>
+#include <gtk/gtkvbox.h>
+
+#include <color.h>
+#include <widgets/sp-color-slider.h>
+#include <widgets/sp-color-selector.h>
+
+
+struct SPColorScales;
+struct SPColorScalesClass;
+
+typedef enum {
+ SP_COLOR_SCALES_MODE_NONE = 0,
+ SP_COLOR_SCALES_MODE_RGB = 1,
+ SP_COLOR_SCALES_MODE_HSV = 2,
+ SP_COLOR_SCALES_MODE_CMYK = 3
+} SPColorScalesMode;
+
+
+
+class ColorScales: public ColorSelector
+{
+public:
+ static gfloat getScaled( const GtkAdjustment *a );
+ static void setScaled( GtkAdjustment *a, gfloat v);
+
+ ColorScales(SPColorSelector *csel);
+ virtual ~ColorScales();
+
+ virtual void init();
+
+ virtual void setSubmode(guint submode);
+ virtual guint getSubmode() const;
+
+ void setMode(SPColorScalesMode mode);
+ SPColorScalesMode getMode() const;
+
+
+protected:
+ virtual void _colorChanged(SPColor const &color, gfloat alpha);
+
+ static void _adjustmentAnyChanged(GtkAdjustment *adjustment, SPColorScales *cs);
+ static void _sliderAnyGrabbed(SPColorSlider *slider, SPColorScales *cs);
+ static void _sliderAnyReleased(SPColorSlider *slider, SPColorScales *cs);
+ static void _sliderAnyChanged(SPColorSlider *slider, SPColorScales *cs);
+ static void _adjustmentChanged(SPColorScales *cs, guint channel);
+
+ void _getRgbaFloatv(gfloat *rgba);
+ void _getCmykaFloatv(gfloat *cmyka);
+ guint32 _getRgba32();
+ void _updateSliders(guint channels);
+ void _recalcColor(gboolean changing);
+
+ void _setRangeLimit( gdouble upper );
+
+ SPColorScalesMode _mode;
+ gdouble _rangeLimit;
+ gboolean _updating : 1;
+ gboolean _dragging : 1;
+ GtkAdjustment *_a[5]; /* Channel adjustments */
+ GtkWidget *_s[5]; /* Channel sliders */
+ GtkWidget *_b[5]; /* Spinbuttons */
+ GtkWidget *_l[5]; /* Labels */
+
+ GtkTooltips *_tt; /* tooltip container */
+
+private:
+ // By default, disallow copy constructor and assignment operator
+ ColorScales(ColorScales const &obj);
+ ColorScales &operator=(ColorScales const &obj );
+};
+
+
+
+#define SP_TYPE_COLOR_SCALES (sp_color_scales_get_type())
+#define SP_COLOR_SCALES(o) (GTK_CHECK_CAST((o), SP_TYPE_COLOR_SCALES, SPColorScales))
+#define SP_COLOR_SCALES_CLASS(k) (GTK_CHECK_CLASS_CAST((k), SP_TYPE_COLOR_SCALES, SPColorScalesClass))
+#define SP_IS_COLOR_SCALES(o) (GTK_CHECK_TYPE((o), SP_TYPE_COLOR_SCALES))
+#define SP_IS_COLOR_SCALES_CLASS(k) (GTK_CHECK_CLASS_TYPE((k), SP_TYPE_COLOR_SCALES))
+
+struct SPColorScales {
+ SPColorSelector parent;
+};
+
+struct SPColorScalesClass {
+ SPColorSelectorClass parent_class;
+};
+
+GType sp_color_scales_get_type();
+
+GtkWidget *sp_color_scales_new();
+
+#endif /* !SEEN_SP_COLOR_SCALES_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/widgets/sp-color-selector.cpp b/src/widgets/sp-color-selector.cpp
new file mode 100644
index 000000000..1532eddd7
--- /dev/null
+++ b/src/widgets/sp-color-selector.cpp
@@ -0,0 +1,351 @@
+/*
+ * bulia byak <buliabyak@users.sf.net>
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <math.h>
+#include <gtk/gtksignal.h>
+#include <glibmm/i18n.h>
+#include "sp-color-selector.h"
+
+enum {
+ GRABBED,
+ DRAGGED,
+ RELEASED,
+ CHANGED,
+ LAST_SIGNAL
+};
+
+#define noDUMP_CHANGE_INFO
+#define FOO_NAME(x) g_type_name( G_TYPE_FROM_INSTANCE(x) )
+
+static void sp_color_selector_class_init (SPColorSelectorClass *klass);
+static void sp_color_selector_init (SPColorSelector *csel);
+static void sp_color_selector_destroy (GtkObject *object);
+
+static void sp_color_selector_show_all (GtkWidget *widget);
+static void sp_color_selector_hide_all (GtkWidget *widget);
+
+static GtkVBoxClass *parent_class;
+static guint csel_signals[LAST_SIGNAL] = {0};
+
+GType
+sp_color_selector_get_type (void)
+{
+ static GType type = 0;
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (SPColorSelectorClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) sp_color_selector_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (SPColorSelector),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) sp_color_selector_init,
+ NULL
+ };
+
+ type = g_type_register_static (GTK_TYPE_VBOX,
+ "SPColorSelector",
+ &info,
+ static_cast< GTypeFlags > (0) );
+ }
+ return type;
+}
+
+static void
+sp_color_selector_class_init (SPColorSelectorClass *klass)
+{
+ static const gchar* nameset[] = {N_("Unnamed"), 0};
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ parent_class = (GtkVBoxClass*)gtk_type_class (GTK_TYPE_VBOX);
+
+ csel_signals[GRABBED] = gtk_signal_new ("grabbed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPColorSelectorClass, grabbed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ csel_signals[DRAGGED] = gtk_signal_new ("dragged",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPColorSelectorClass, dragged),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ csel_signals[RELEASED] = gtk_signal_new ("released",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPColorSelectorClass, released),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ csel_signals[CHANGED] = gtk_signal_new ("changed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPColorSelectorClass, changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ klass->name = nameset;
+ klass->submode_count = 1;
+
+ object_class->destroy = sp_color_selector_destroy;
+
+ widget_class->show_all = sp_color_selector_show_all;
+ widget_class->hide_all = sp_color_selector_hide_all;
+
+}
+
+void sp_color_selector_init (SPColorSelector *csel)
+{
+ if ( csel->base )
+ {
+ csel->base->init();
+ }
+/* gtk_signal_connect (GTK_OBJECT (csel->rgbae), "changed", GTK_SIGNAL_FUNC (sp_color_selector_rgba_entry_changed), csel); */
+}
+
+static void
+sp_color_selector_destroy (GtkObject *object)
+{
+ SPColorSelector *csel;
+
+ csel = SP_COLOR_SELECTOR (object);
+ if ( csel->base )
+ {
+ delete csel->base;
+ csel->base = 0;
+ }
+
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+static void
+sp_color_selector_show_all (GtkWidget *widget)
+{
+ gtk_widget_show (widget);
+}
+
+static void
+sp_color_selector_hide_all (GtkWidget *widget)
+{
+ gtk_widget_hide (widget);
+}
+
+GtkWidget *
+sp_color_selector_new (GType selector_type, SPColorSpaceType colorspace)
+{
+ SPColorSelector *csel;
+ g_return_val_if_fail (g_type_is_a (selector_type, SP_TYPE_COLOR_SELECTOR), NULL);
+
+ csel = SP_COLOR_SELECTOR (g_object_new (selector_type, NULL));
+
+ return GTK_WIDGET (csel);
+}
+
+double ColorSelector::_epsilon = 1e-4;
+
+void ColorSelector::setSubmode( guint submode )
+{
+}
+
+guint ColorSelector::getSubmode() const
+{
+ guint mode = 0;
+ return mode;
+}
+
+SPColorSpaceType ColorSelector::getColorspace() const
+{
+ SPColorSpaceType type = SP_COLORSPACE_TYPE_UNKNOWN;
+
+ return type;
+}
+
+gboolean ColorSelector::setColorspace( SPColorSpaceType colorspace )
+{
+ return false;
+}
+
+ColorSelector::ColorSelector( SPColorSelector* csel )
+ : _csel(csel),
+ _alpha(1.0),
+ _held(FALSE)
+{
+ sp_color_set_rgb_rgba32( &_color, 0 );
+
+ virgin = true;
+}
+
+ColorSelector::~ColorSelector()
+{
+}
+
+void ColorSelector::init()
+{
+ _csel->base = new ColorSelector( _csel );
+}
+
+void ColorSelector::setColor( const SPColor& color )
+{
+ setColorAlpha( color, _alpha );
+}
+
+SPColor ColorSelector::getColor() const
+{
+ return _color;
+}
+
+void ColorSelector::setAlpha( gfloat alpha )
+{
+ g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) );
+ setColorAlpha( _color, alpha );
+}
+
+gfloat ColorSelector::getAlpha() const
+{
+ return _alpha;
+}
+
+/**
+Called from the outside to set the color; optionally emits signal (only when called from
+downstream, e.g. the RGBA value field, but not from the rest of the program)
+*/
+void ColorSelector::setColorAlpha( const SPColor& color, gfloat alpha, bool emit )
+{
+ g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) );
+
+ if ( virgin || !sp_color_is_close( &color, &_color, _epsilon ) ||
+ (fabs ((_alpha) - (alpha)) >= _epsilon )) {
+
+ virgin = false;
+
+ sp_color_copy (&_color, &color);
+ _alpha = alpha;
+ _colorChanged( color, alpha );
+
+ if (emit)
+ gtk_signal_emit (GTK_OBJECT (_csel), csel_signals[CHANGED]);
+ }
+}
+
+void ColorSelector::_grabbed()
+{
+ _held = TRUE;
+#ifdef DUMP_CHANGE_INFO
+ g_message ("%s:%d: About to signal %s in %s", __FILE__, __LINE__,
+ "GRABBED",
+ FOO_NAME(_csel));
+#endif
+ gtk_signal_emit (GTK_OBJECT (_csel), csel_signals[GRABBED]);
+}
+
+void ColorSelector::_released()
+{
+ _held = false;
+#ifdef DUMP_CHANGE_INFO
+ g_message ("%s:%d: About to signal %s in %s", __FILE__, __LINE__,
+ "RELEASED",
+ FOO_NAME(_csel));
+#endif
+ gtk_signal_emit (GTK_OBJECT (_csel), csel_signals[RELEASED]);
+ gtk_signal_emit (GTK_OBJECT (_csel), csel_signals[CHANGED]);
+}
+
+// Called from subclasses to update color and broadcast if needed
+void ColorSelector::_updateInternals( const SPColor& color, gfloat alpha, gboolean held )
+{
+ g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) );
+ gboolean colorDifferent = ( !sp_color_is_close( &color, &_color, _epsilon )
+ || ( fabs((_alpha) - (alpha)) >= _epsilon ) );
+
+ gboolean grabbed = held && !_held;
+ gboolean released = !held && _held;
+
+ // Store these before emmiting any signals
+ _held = held;
+ if ( colorDifferent )
+ {
+ sp_color_copy (&_color, &color);
+ _alpha = alpha;
+ }
+
+ if ( grabbed )
+ {
+#ifdef DUMP_CHANGE_INFO
+ g_message ("%s:%d: About to signal %s to color %08x in %s", __FILE__, __LINE__,
+ "GRABBED",
+ sp_color_get_rgba32_falpha(&color,alpha), FOO_NAME(_csel));
+#endif
+ gtk_signal_emit (GTK_OBJECT (_csel), csel_signals[GRABBED]);
+ }
+ else if ( released )
+ {
+#ifdef DUMP_CHANGE_INFO
+ g_message ("%s:%d: About to signal %s to color %08x in %s", __FILE__, __LINE__,
+ "RELEASED",
+ sp_color_get_rgba32_falpha(&color,alpha), FOO_NAME(_csel));
+#endif
+ gtk_signal_emit (GTK_OBJECT (_csel), csel_signals[RELEASED]);
+ }
+
+ if ( colorDifferent || released )
+ {
+#ifdef DUMP_CHANGE_INFO
+ g_message ("%s:%d: About to signal %s to color %08x in %s", __FILE__, __LINE__,
+ (_held ? "CHANGED" : "DRAGGED" ),
+ sp_color_get_rgba32_falpha(&color,alpha), FOO_NAME(_csel));
+#endif
+ gtk_signal_emit (GTK_OBJECT (_csel), csel_signals[_held ? CHANGED : DRAGGED]);
+ }
+}
+
+void ColorSelector::_colorChanged( const SPColor& color, gfloat alpha )
+{
+}
+
+void ColorSelector::getColorAlpha( SPColor& color, gfloat* alpha ) const
+{
+ gint i = 0;
+
+ sp_color_copy (&color, &_color);
+ if ( alpha )
+ {
+ *alpha = _alpha;
+ }
+
+ // Try to catch uninitialized value usage
+ if ( color.colorspace )
+ {
+ i++;
+ }
+ if ( color.v.c[0] )
+ {
+ i++;
+ }
+ if ( color.v.c[1] )
+ {
+ i++;
+ }
+ if ( color.v.c[2] )
+ {
+ i++;
+ }
+ if ( color.v.c[3] )
+ {
+ i++;
+ }
+ if ( alpha && *alpha )
+ {
+ i++;
+ }
+}
diff --git a/src/widgets/sp-color-selector.h b/src/widgets/sp-color-selector.h
new file mode 100644
index 000000000..b2fd62b1e
--- /dev/null
+++ b/src/widgets/sp-color-selector.h
@@ -0,0 +1,95 @@
+#ifndef __SP_COLOR_SELECTOR_H__
+#define __SP_COLOR_SELECTOR_H__
+
+#include <gtk/gtkvbox.h>
+#include "../color.h"
+
+#include <glib.h>
+
+
+
+
+struct SPColorSelector;
+
+class ColorSelector
+{
+public:
+ ColorSelector( SPColorSelector* csel );
+ virtual ~ColorSelector();
+
+ virtual void init();
+
+ void setColor( const SPColor& color );
+ SPColor getColor() const;
+
+ void setAlpha( gfloat alpha );
+ gfloat getAlpha() const;
+
+ void setColorAlpha( const SPColor& color, gfloat alpha, bool emit = false );
+ void getColorAlpha( SPColor& color, gfloat* alpha ) const;
+
+ virtual void setSubmode( guint submode );
+ virtual guint getSubmode() const;
+
+ virtual SPColorSpaceType getColorspace() const;
+ virtual gboolean setColorspace( SPColorSpaceType colorspace );
+
+protected:
+ void _grabbed();
+ void _released();
+ void _updateInternals( const SPColor& color, gfloat alpha, gboolean held );
+ gboolean _isHeld() const { return _held; }
+
+ virtual void _colorChanged( const SPColor& color, gfloat alpha );
+
+ static double _epsilon;
+
+ SPColorSelector* _csel;
+ SPColor _color;
+ gfloat _alpha; /* guaranteed to be in [0, 1]. */
+
+private:
+ // By default, disallow copy constructor and assignment operator
+ ColorSelector( const ColorSelector& obj );
+ ColorSelector& operator=( const ColorSelector& obj );
+
+ gboolean _held;
+
+ bool virgin; // if true, no color is set yet
+};
+
+
+
+#define SP_TYPE_COLOR_SELECTOR (sp_color_selector_get_type ())
+#define SP_COLOR_SELECTOR(o) (GTK_CHECK_CAST ((o), SP_TYPE_COLOR_SELECTOR, SPColorSelector))
+#define SP_COLOR_SELECTOR_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_COLOR_SELECTOR, SPColorSelectorClass))
+#define SP_IS_COLOR_SELECTOR(o) (GTK_CHECK_TYPE ((o), SP_TYPE_COLOR_SELECTOR))
+#define SP_IS_COLOR_SELECTOR_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_COLOR_SELECTOR))
+#define SP_COLOR_SELECTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SP_TYPE_COLOR_SELECTOR, SPColorSelectorClass))
+
+struct SPColorSelector {
+ GtkVBox vbox;
+
+ ColorSelector* base;
+};
+
+struct SPColorSelectorClass {
+ GtkVBoxClass parent_class;
+
+ const gchar **name;
+ guint submode_count;
+
+ void (* grabbed) (SPColorSelector *rgbsel);
+ void (* dragged) (SPColorSelector *rgbsel);
+ void (* released) (SPColorSelector *rgbsel);
+ void (* changed) (SPColorSelector *rgbsel);
+
+};
+
+GType sp_color_selector_get_type (void);
+
+GtkWidget *sp_color_selector_new (GType selector_type, SPColorSpaceType colorspace);
+
+
+
+#endif
diff --git a/src/widgets/sp-color-slider.cpp b/src/widgets/sp-color-slider.cpp
new file mode 100644
index 000000000..76ef13a8d
--- /dev/null
+++ b/src/widgets/sp-color-slider.cpp
@@ -0,0 +1,687 @@
+#define __SP_COLOR_SLIDER_C__
+
+/*
+ * A slider with colored background
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ *
+ * This code is in public domain
+ */
+
+#include <gtk/gtksignal.h>
+#include "sp-color-scales.h"
+
+#define SLIDER_WIDTH 96
+#define SLIDER_HEIGHT 8
+#define ARROW_SIZE 7
+
+enum {
+ GRABBED,
+ DRAGGED,
+ RELEASED,
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static void sp_color_slider_class_init (SPColorSliderClass *klass);
+static void sp_color_slider_init (SPColorSlider *slider);
+static void sp_color_slider_destroy (GtkObject *object);
+
+static void sp_color_slider_realize (GtkWidget *widget);
+static void sp_color_slider_size_request (GtkWidget *widget, GtkRequisition *requisition);
+static void sp_color_slider_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
+/* static void sp_color_slider_draw (GtkWidget *widget, GdkRectangle *area); */
+/* static void sp_color_slider_draw_focus (GtkWidget *widget); */
+/* static void sp_color_slider_draw_default (GtkWidget *widget); */
+
+static gint sp_color_slider_expose (GtkWidget *widget, GdkEventExpose *event);
+static gint sp_color_slider_button_press (GtkWidget *widget, GdkEventButton *event);
+static gint sp_color_slider_button_release (GtkWidget *widget, GdkEventButton *event);
+static gint sp_color_slider_motion_notify (GtkWidget *widget, GdkEventMotion *event);
+
+static void sp_color_slider_adjustment_changed (GtkAdjustment *adjustment, SPColorSlider *slider);
+static void sp_color_slider_adjustment_value_changed (GtkAdjustment *adjustment, SPColorSlider *slider);
+
+static void sp_color_slider_paint (SPColorSlider *slider, GdkRectangle *area);
+static const guchar *sp_color_slider_render_gradient (gint x0, gint y0, gint width, gint height,
+ gint c[], gint dc[], guint b0, guint b1, guint mask);
+static const guchar *sp_color_slider_render_map (gint x0, gint y0, gint width, gint height,
+ guchar *map, gint start, gint step, guint b0, guint b1, guint mask);
+
+static GtkWidgetClass *parent_class;
+static guint slider_signals[LAST_SIGNAL] = {0};
+
+GtkType
+sp_color_slider_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GtkTypeInfo info = {
+ "SPColorSlider",
+ sizeof (SPColorSlider),
+ sizeof (SPColorSliderClass),
+ (GtkClassInitFunc) sp_color_slider_class_init,
+ (GtkObjectInitFunc) sp_color_slider_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_WIDGET, &info);
+ }
+ return type;
+}
+
+static void
+sp_color_slider_class_init (SPColorSliderClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ parent_class = (GtkWidgetClass*)gtk_type_class (GTK_TYPE_WIDGET);
+
+ slider_signals[GRABBED] = gtk_signal_new ("grabbed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPColorSliderClass, grabbed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ slider_signals[DRAGGED] = gtk_signal_new ("dragged",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPColorSliderClass, dragged),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ slider_signals[RELEASED] = gtk_signal_new ("released",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPColorSliderClass, released),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ slider_signals[CHANGED] = gtk_signal_new ("changed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPColorSliderClass, changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ object_class->destroy = sp_color_slider_destroy;
+
+ widget_class->realize = sp_color_slider_realize;
+ widget_class->size_request = sp_color_slider_size_request;
+ widget_class->size_allocate = sp_color_slider_size_allocate;
+/* widget_class->draw = sp_color_slider_draw; */
+/* widget_class->draw_focus = sp_color_slider_draw_focus; */
+/* widget_class->draw_default = sp_color_slider_draw_default; */
+
+ widget_class->expose_event = sp_color_slider_expose;
+ widget_class->button_press_event = sp_color_slider_button_press;
+ widget_class->button_release_event = sp_color_slider_button_release;
+ widget_class->motion_notify_event = sp_color_slider_motion_notify;
+}
+
+static void
+sp_color_slider_init (SPColorSlider *slider)
+{
+ /* We are widget with window */
+ GTK_WIDGET_UNSET_FLAGS (slider, GTK_NO_WINDOW);
+
+ slider->dragging = FALSE;
+
+ slider->adjustment = NULL;
+ slider->value = 0.0;
+
+ slider->c0[0] = 0x00;
+ slider->c0[1] = 0x00;
+ slider->c0[2] = 0x00;
+ slider->c0[3] = 0xff;
+
+ slider->cm[0] = 0xff;
+ slider->cm[1] = 0x00;
+ slider->cm[2] = 0x00;
+ slider->cm[3] = 0xff;
+
+ slider->c1[0] = 0xff;
+ slider->c1[1] = 0xff;
+ slider->c1[2] = 0xff;
+ slider->c1[3] = 0xff;
+
+ slider->b0 = 0x5f;
+ slider->b1 = 0xa0;
+ slider->bmask = 0x08;
+
+ slider->map = NULL;
+}
+
+static void
+sp_color_slider_destroy (GtkObject *object)
+{
+ SPColorSlider *slider;
+
+ slider = SP_COLOR_SLIDER (object);
+
+ if (slider->adjustment) {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (slider->adjustment), slider);
+ gtk_object_unref (GTK_OBJECT (slider->adjustment));
+ slider->adjustment = NULL;
+ }
+
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+static void
+sp_color_slider_realize (GtkWidget *widget)
+{
+ SPColorSlider *slider;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ slider = SP_COLOR_SLIDER (widget);
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gdk_rgb_get_visual ();
+ attributes.colormap = gdk_rgb_get_cmap ();
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= (GDK_EXPOSURE_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK);
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
+ gdk_window_set_user_data (widget->window, widget);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+}
+
+static void
+sp_color_slider_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+ SPColorSlider *slider;
+
+ slider = SP_COLOR_SLIDER (widget);
+
+ requisition->width = SLIDER_WIDTH + widget->style->xthickness * 2;
+ requisition->height = SLIDER_HEIGHT + widget->style->ythickness * 2;
+}
+
+static void
+sp_color_slider_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ SPColorSlider *slider;
+
+ slider = SP_COLOR_SLIDER (widget);
+
+ widget->allocation = *allocation;
+
+ if (GTK_WIDGET_REALIZED (widget)) {
+ /* Resize GdkWindow */
+ gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
+ }
+}
+
+static gint
+sp_color_slider_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+ SPColorSlider *slider;
+
+ slider = SP_COLOR_SLIDER (widget);
+
+ if (GTK_WIDGET_DRAWABLE (widget)) {
+ gint width, height;
+ width = widget->allocation.width;
+ height = widget->allocation.height;
+ sp_color_slider_paint (slider, &event->area);
+ }
+
+ return FALSE;
+}
+
+static gint
+sp_color_slider_button_press (GtkWidget *widget, GdkEventButton *event)
+{
+ SPColorSlider *slider;
+
+ slider = SP_COLOR_SLIDER (widget);
+
+ if (event->button == 1) {
+ gint cx, cw;
+ cx = widget->style->xthickness;
+ cw = widget->allocation.width - 2 * cx;
+ gtk_signal_emit (GTK_OBJECT (slider), slider_signals[GRABBED]);
+ slider->dragging = TRUE;
+ slider->oldvalue = slider->value;
+ ColorScales::setScaled( slider->adjustment, CLAMP ((gfloat) (event->x - cx) / cw, 0.0, 1.0) );
+ gtk_signal_emit (GTK_OBJECT (slider), slider_signals[DRAGGED]);
+ gdk_pointer_grab (widget->window, FALSE,
+ (GdkEventMask)(GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_RELEASE_MASK),
+ NULL, NULL, event->time);
+ }
+
+ return FALSE;
+}
+
+static gint
+sp_color_slider_button_release (GtkWidget *widget, GdkEventButton *event)
+{
+ SPColorSlider *slider;
+
+ slider = SP_COLOR_SLIDER (widget);
+
+ if (event->button == 1) {
+ gdk_pointer_ungrab (event->time);
+ slider->dragging = FALSE;
+ gtk_signal_emit (GTK_OBJECT (slider), slider_signals[RELEASED]);
+ if (slider->value != slider->oldvalue) gtk_signal_emit (GTK_OBJECT (slider), slider_signals[CHANGED]);
+ }
+
+ return FALSE;
+}
+
+static gint
+sp_color_slider_motion_notify (GtkWidget *widget, GdkEventMotion *event)
+{
+ SPColorSlider *slider;
+
+ slider = SP_COLOR_SLIDER (widget);
+
+ if (slider->dragging) {
+ gint cx, cw;
+ cx = widget->style->xthickness;
+ cw = widget->allocation.width - 2 * cx;
+ ColorScales::setScaled( slider->adjustment, CLAMP ((gfloat) (event->x - cx) / cw, 0.0, 1.0) );
+ gtk_signal_emit (GTK_OBJECT (slider), slider_signals[DRAGGED]);
+ }
+
+ return FALSE;
+}
+
+GtkWidget *
+sp_color_slider_new (GtkAdjustment *adjustment)
+{
+ SPColorSlider *slider;
+
+ slider = (SPColorSlider*)gtk_type_new (SP_TYPE_COLOR_SLIDER);
+
+ sp_color_slider_set_adjustment (slider, adjustment);
+
+ return GTK_WIDGET (slider);
+}
+
+void
+sp_color_slider_set_adjustment (SPColorSlider *slider, GtkAdjustment *adjustment)
+{
+ g_return_if_fail (slider != NULL);
+ g_return_if_fail (SP_IS_COLOR_SLIDER (slider));
+
+ if (!adjustment) {
+ adjustment = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 1.0, 0.01, 0.1, 0.1);
+ }
+
+ if (slider->adjustment != adjustment) {
+ if (slider->adjustment) {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (slider->adjustment), slider);
+ gtk_object_unref (GTK_OBJECT (slider->adjustment));
+ }
+
+ slider->adjustment = adjustment;
+ gtk_object_ref (GTK_OBJECT (adjustment));
+ gtk_object_sink (GTK_OBJECT (adjustment));
+
+ gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
+ GTK_SIGNAL_FUNC (sp_color_slider_adjustment_changed), slider);
+ gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
+ GTK_SIGNAL_FUNC (sp_color_slider_adjustment_value_changed), slider);
+
+ slider->value = ColorScales::getScaled( adjustment );
+
+ sp_color_slider_adjustment_changed (adjustment, slider);
+ }
+}
+
+void
+sp_color_slider_set_colors (SPColorSlider *slider, guint32 start, guint32 mid, guint32 end)
+{
+ g_return_if_fail (slider != NULL);
+ g_return_if_fail (SP_IS_COLOR_SLIDER (slider));
+
+ slider->c0[0] = start >> 24;
+ slider->c0[1] = (start >> 16) & 0xff;
+ slider->c0[2] = (start >> 8) & 0xff;
+ slider->c0[3] = start & 0xff;
+
+ slider->cm[0] = mid >> 24;
+ slider->cm[1] = (mid >> 16) & 0xff;
+ slider->cm[2] = (mid >> 8) & 0xff;
+ slider->cm[3] = mid & 0xff;
+
+ slider->c1[0] = end >> 24;
+ slider->c1[1] = (end >> 16) & 0xff;
+ slider->c1[2] = (end >> 8) & 0xff;
+ slider->c1[3] = end & 0xff;
+
+ gtk_widget_queue_draw (GTK_WIDGET (slider));
+}
+
+void
+sp_color_slider_set_map (SPColorSlider *slider, const guchar *map)
+{
+ g_return_if_fail (slider != NULL);
+ g_return_if_fail (SP_IS_COLOR_SLIDER (slider));
+
+ slider->map = (guchar *) map;
+
+ gtk_widget_queue_draw (GTK_WIDGET (slider));
+}
+
+void
+sp_color_slider_set_background (SPColorSlider *slider, guint dark, guint light, guint size)
+{
+ g_return_if_fail (slider != NULL);
+ g_return_if_fail (SP_IS_COLOR_SLIDER (slider));
+
+ slider->b0 = dark;
+ slider->b1 = light;
+ slider->bmask = size;
+
+ gtk_widget_queue_draw (GTK_WIDGET (slider));
+}
+
+static void
+sp_color_slider_adjustment_changed (GtkAdjustment *adjustment, SPColorSlider *slider)
+{
+ gtk_widget_queue_draw (GTK_WIDGET (slider));
+}
+
+static void
+sp_color_slider_adjustment_value_changed (GtkAdjustment *adjustment, SPColorSlider *slider)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (slider);
+
+ if (slider->value != ColorScales::getScaled( adjustment )) {
+ gint cx, cy, cw, ch;
+ cx = widget->style->xthickness;
+ cy = widget->style->ythickness;
+ cw = widget->allocation.width - 2 * cx;
+ ch = widget->allocation.height - 2 * cy;
+ if ((gint) (ColorScales::getScaled( adjustment ) * cw) != (gint) (slider->value * cw)) {
+ gint ax, ay;
+ gfloat value;
+ value = slider->value;
+ slider->value = ColorScales::getScaled( adjustment );
+ ax = (int)(cx + value * cw - ARROW_SIZE / 2 - 2);
+ ay = cy;
+ gtk_widget_queue_draw_area (widget, ax, ay, ARROW_SIZE + 4, ch);
+ ax = (int)(cx + slider->value * cw - ARROW_SIZE / 2 - 2);
+ ay = cy;
+ gtk_widget_queue_draw_area (widget, ax, ay, ARROW_SIZE + 4, ch);
+ } else {
+ slider->value = ColorScales::getScaled( adjustment );
+ }
+ }
+}
+
+static void
+sp_color_slider_paint (SPColorSlider *slider, GdkRectangle *area)
+{
+ GtkWidget *widget;
+ GdkRectangle warea, carea, aarea;
+ GdkRectangle wpaint, cpaint, apaint;
+ const guchar *b;
+ gint w, x, y1, y2;
+
+ widget = GTK_WIDGET (slider);
+
+ /* Widget area */
+ warea.x = 0;
+ warea.y = 0;
+ warea.width = widget->allocation.width;
+ warea.height = widget->allocation.height;
+
+ /* Color gradient area */
+ carea.x = widget->style->xthickness;
+ carea.y = widget->style->ythickness;
+ carea.width = widget->allocation.width - 2 * carea.x;
+ carea.height = widget->allocation.height - 2 * carea.y;
+
+ /* Arrow area */
+ aarea.x = (int)(slider->value * (carea.width - 1) - ARROW_SIZE / 2 + carea.x);
+ aarea.width = ARROW_SIZE;
+ aarea.y = carea.y;
+ aarea.height = carea.height;
+
+ /* Actual paintable area */
+ if (!gdk_rectangle_intersect (area, &warea, &wpaint)) return;
+
+ b = NULL;
+
+ /* Paintable part of color gradient area */
+ if (gdk_rectangle_intersect (area, &carea, &cpaint)) {
+ if (slider->map) {
+ gint s, d;
+ /* Render map pixelstore */
+ d = (1024 << 16) / carea.width;
+ s = (cpaint.x - carea.x) * d;
+ b = sp_color_slider_render_map (cpaint.x - carea.x, cpaint.y - carea.y, cpaint.width, cpaint.height,
+ slider->map, s, d,
+ slider->b0, slider->b1, slider->bmask);
+ if (b != NULL) {
+ gdk_draw_rgb_image (widget->window, widget->style->black_gc,
+ cpaint.x, cpaint.y,
+ cpaint.width, cpaint.height,
+ GDK_RGB_DITHER_MAX,
+ (guchar *) b, cpaint.width * 3);
+ }
+
+ } else {
+ gint c[4], dc[4];
+ gint i;
+ /* Render gradient */
+
+ // part 1: from c0 to cm
+ if ((cpaint.x - carea.x) <= carea.width/2) {
+ for (i = 0; i < 4; i++) {
+ c[i] = slider->c0[i] << 16;
+ dc[i] = ((slider->cm[i] << 16) - c[i]) / (carea.width/2);
+ c[i] += (cpaint.x - carea.x) * dc[i];
+ }
+ guint wi = MIN(cpaint.x - carea.x + cpaint.width, carea.width/2) - (cpaint.x - carea.x);
+ b = sp_color_slider_render_gradient (cpaint.x - carea.x, cpaint.y - carea.y, wi, cpaint.height,
+ c, dc,
+ slider->b0, slider->b1, slider->bmask);
+
+ /* Draw pixelstore */
+ if (b != NULL) {
+ gdk_draw_rgb_image (widget->window, widget->style->black_gc,
+ cpaint.x, cpaint.y,
+ wi, cpaint.height,
+ GDK_RGB_DITHER_MAX,
+ (guchar *) b, wi * 3);
+ }
+ }
+
+ // part 2: from cm to c1
+ if ((cpaint.x - carea.x + cpaint.width) > carea.width/2) {
+ for (i = 0; i < 4; i++) {
+ c[i] = slider->cm[i] << 16;
+ dc[i] = ((slider->c1[i] << 16) - c[i]) / (carea.width/2);
+ if ((cpaint.x - carea.x) > carea.width/2)
+ c[i] += (cpaint.x - carea.x - carea.width/2) * dc[i];
+ }
+ guint wi = cpaint.width - MAX(0, (carea.width/2 - (cpaint.x - carea.x)));
+ b = sp_color_slider_render_gradient (MAX(cpaint.x - carea.x, carea.width/2), cpaint.y - carea.y, wi, cpaint.height,
+ c, dc,
+ slider->b0, slider->b1, slider->bmask);
+
+ /* Draw pixelstore */
+ if (b != NULL) {
+ gdk_draw_rgb_image (widget->window, widget->style->black_gc,
+ MAX(cpaint.x, carea.width/2 + carea.x), cpaint.y,
+ wi, cpaint.height,
+ GDK_RGB_DITHER_MAX,
+ (guchar *) b, wi * 3);
+ }
+ }
+ }
+ }
+
+ /* Draw shadow */
+ gtk_paint_shadow (widget->style, widget->window,
+ (GtkStateType)widget->state, GTK_SHADOW_IN,
+ area, widget, "colorslider",
+ 0, 0,
+ warea.width, warea.height);
+
+
+ if (gdk_rectangle_intersect (area, &aarea, &apaint)) {
+ /* Draw arrow */
+ gdk_rectangle_intersect (&carea, &apaint, &apaint);
+ gdk_gc_set_clip_rectangle (widget->style->white_gc, &apaint);
+ gdk_gc_set_clip_rectangle (widget->style->black_gc, &apaint);
+
+ x = aarea.x;
+ y1 = carea.y;
+ y2 = aarea.y + aarea.height - 1;
+ w = aarea.width;
+ while ( w > 0 )
+ {
+ gdk_draw_line (widget->window, widget->style->white_gc, x, y1, x + w - 1, y1 );
+ gdk_draw_line (widget->window, widget->style->white_gc, x, y2, x + w - 1, y2 );
+ w -=2;
+ x++;
+ if ( w > 0 )
+ {
+ gdk_draw_line (widget->window, widget->style->black_gc, x, y1, x + w - 1, y1 );
+ gdk_draw_line (widget->window, widget->style->black_gc, x, y2, x + w - 1, y2 );
+ }
+ y1++;
+ y2--;
+ }
+
+ gdk_gc_set_clip_rectangle (widget->style->white_gc, NULL);
+ gdk_gc_set_clip_rectangle (widget->style->black_gc, NULL);
+ }
+}
+
+/* Colors are << 16 */
+
+static const guchar *
+sp_color_slider_render_gradient (gint x0, gint y0, gint width, gint height,
+ gint c[], gint dc[], guint b0, guint b1, guint mask)
+{
+ static guchar *buf = NULL;
+ static gint bs = 0;
+ guchar *dp;
+ gint x, y;
+ guint r, g, b, a;
+
+ if (buf && (bs < width * height)) {
+ g_free (buf);
+ buf = NULL;
+ }
+ if (!buf) {
+ buf = g_new (guchar, width * height * 3);
+ bs = width * height;
+ }
+
+ dp = buf;
+ r = c[0];
+ g = c[1];
+ b = c[2];
+ a = c[3];
+ for (x = x0; x < x0 + width; x++) {
+ gint cr, cg, cb, ca;
+ guchar *d;
+ cr = r >> 16;
+ cg = g >> 16;
+ cb = b >> 16;
+ ca = a >> 16;
+ d = dp;
+ for (y = y0; y < y0 + height; y++) {
+ guint bg, fc;
+ /* Background value */
+ bg = ((x & mask) ^ (y & mask)) ? b0 : b1;
+ fc = (cr - bg) * ca;
+ d[0] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
+ fc = (cg - bg) * ca;
+ d[1] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
+ fc = (cb - bg) * ca;
+ d[2] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
+ d += 3 * width;
+ }
+ r += dc[0];
+ g += dc[1];
+ b += dc[2];
+ a += dc[3];
+ dp += 3;
+ }
+
+ return buf;
+}
+
+/* Positions are << 16 */
+
+static const guchar *
+sp_color_slider_render_map (gint x0, gint y0, gint width, gint height,
+ guchar *map, gint start, gint step, guint b0, guint b1, guint mask)
+{
+ static guchar *buf = NULL;
+ static gint bs = 0;
+ guchar *dp, *sp;
+ gint x, y;
+
+ if (buf && (bs < width * height)) {
+ g_free (buf);
+ buf = NULL;
+ }
+ if (!buf) {
+ buf = g_new (guchar, width * height * 3);
+ bs = width * height;
+ }
+
+ dp = buf;
+ for (x = x0; x < x0 + width; x++) {
+ gint cr, cg, cb, ca;
+ guchar *d;
+ sp = map + 4 * (start >> 16);
+ cr = *sp++;
+ cg = *sp++;
+ cb = *sp++;
+ ca = *sp++;
+ d = dp;
+ for (y = y0; y < y0 + height; y++) {
+ guint bg, fc;
+ /* Background value */
+ bg = ((x & mask) ^ (y & mask)) ? b0 : b1;
+ fc = (cr - bg) * ca;
+ d[0] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
+ fc = (cg - bg) * ca;
+ d[1] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
+ fc = (cb - bg) * ca;
+ d[2] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
+ d += 3 * width;
+ }
+ dp += 3;
+ start += step;
+ }
+
+ return buf;
+}
+
diff --git a/src/widgets/sp-color-slider.h b/src/widgets/sp-color-slider.h
new file mode 100644
index 000000000..bdeb3e4b6
--- /dev/null
+++ b/src/widgets/sp-color-slider.h
@@ -0,0 +1,67 @@
+#ifndef __SP_COLOR_SLIDER_H__
+#define __SP_COLOR_SLIDER_H__
+
+/*
+ * A slider with colored background
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ *
+ * This code is in public domain
+ */
+
+#include <gtk/gtkwidget.h>
+
+#include <glib.h>
+
+
+
+struct SPColorSlider;
+struct SPColorSliderClass;
+
+#define SP_TYPE_COLOR_SLIDER (sp_color_slider_get_type ())
+#define SP_COLOR_SLIDER(o) (GTK_CHECK_CAST ((o), SP_TYPE_COLOR_SLIDER, SPColorSlider))
+#define SP_COLOR_SLIDER_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_COLOR_SLIDER, SPColorSliderClass))
+#define SP_IS_COLOR_SLIDER(o) (GTK_CHECK_TYPE ((o), SP_TYPE_COLOR_SLIDER))
+#define SP_IS_COLOR_SLIDER_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_COLOR_SLIDER))
+
+struct SPColorSlider {
+ GtkWidget widget;
+
+ guint dragging : 1;
+
+ GtkAdjustment *adjustment;
+
+ gfloat value;
+ gfloat oldvalue;
+ guchar c0[4], cm[4], c1[4];
+ guchar b0, b1;
+ guchar bmask;
+
+ gint mapsize;
+ guchar *map;
+};
+
+struct SPColorSliderClass {
+ GtkWidgetClass parent_class;
+
+ void (* grabbed) (SPColorSlider *slider);
+ void (* dragged) (SPColorSlider *slider);
+ void (* released) (SPColorSlider *slider);
+ void (* changed) (SPColorSlider *slider);
+};
+
+GtkType sp_color_slider_get_type (void);
+
+GtkWidget *sp_color_slider_new (GtkAdjustment *adjustment);
+
+void sp_color_slider_set_adjustment (SPColorSlider *slider, GtkAdjustment *adjustment);
+void sp_color_slider_set_colors (SPColorSlider *slider, guint32 start, guint32 mid, guint32 end);
+void sp_color_slider_set_map (SPColorSlider *slider, const guchar *map);
+void sp_color_slider_set_background (SPColorSlider *slider, guint dark, guint light, guint size);
+
+
+
+#endif
diff --git a/src/widgets/sp-color-wheel-selector.cpp b/src/widgets/sp-color-wheel-selector.cpp
new file mode 100644
index 000000000..584a75dd0
--- /dev/null
+++ b/src/widgets/sp-color-wheel-selector.cpp
@@ -0,0 +1,295 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <math.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtkspinbutton.h>
+#include <glibmm/i18n.h>
+#include "../dialogs/dialog-events.h"
+#include "sp-color-wheel-selector.h"
+#include "sp-color-scales.h"
+
+
+G_BEGIN_DECLS
+
+static void sp_color_wheel_selector_class_init (SPColorWheelSelectorClass *klass);
+static void sp_color_wheel_selector_init (SPColorWheelSelector *cs);
+static void sp_color_wheel_selector_destroy (GtkObject *object);
+
+static void sp_color_wheel_selector_show_all (GtkWidget *widget);
+static void sp_color_wheel_selector_hide_all (GtkWidget *widget);
+
+
+G_END_DECLS
+
+static SPColorSelectorClass *parent_class;
+
+#define XPAD 4
+#define YPAD 1
+
+GType
+sp_color_wheel_selector_get_type (void)
+{
+ static GType type = 0;
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (SPColorWheelSelectorClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) sp_color_wheel_selector_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (SPColorWheelSelector),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) sp_color_wheel_selector_init,
+ 0, /* value_table */
+ };
+
+ type = g_type_register_static (SP_TYPE_COLOR_SELECTOR,
+ "SPColorWheelSelector",
+ &info,
+ static_cast< GTypeFlags > (0) );
+ }
+ return type;
+}
+
+static void
+sp_color_wheel_selector_class_init (SPColorWheelSelectorClass *klass)
+{
+ static const gchar* nameset[] = {N_("Wheel"), 0};
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ SPColorSelectorClass *selector_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+ selector_class = SP_COLOR_SELECTOR_CLASS (klass);
+
+ parent_class = SP_COLOR_SELECTOR_CLASS (g_type_class_peek_parent (klass));
+
+ selector_class->name = nameset;
+ selector_class->submode_count = 1;
+
+ object_class->destroy = sp_color_wheel_selector_destroy;
+
+ widget_class->show_all = sp_color_wheel_selector_show_all;
+ widget_class->hide_all = sp_color_wheel_selector_hide_all;
+}
+
+ColorWheelSelector::ColorWheelSelector( SPColorSelector* csel )
+ : ColorSelector( csel ),
+ _updating( FALSE ),
+ _dragging( FALSE ),
+ _adj(0),
+ _wheel(0),
+ _sbtn(0),
+ _label(0)
+{
+}
+
+ColorWheelSelector::~ColorWheelSelector()
+{
+ _adj = 0;
+ _wheel = 0;
+ _sbtn = 0;
+ _label = 0;
+}
+
+void sp_color_wheel_selector_init (SPColorWheelSelector *cs)
+{
+ SP_COLOR_SELECTOR(cs)->base = new ColorWheelSelector( SP_COLOR_SELECTOR(cs) );
+
+ if ( SP_COLOR_SELECTOR(cs)->base )
+ {
+ SP_COLOR_SELECTOR(cs)->base->init();
+ }
+}
+
+void ColorWheelSelector::init()
+{
+ GtkWidget *t;
+ gint row = 0;
+
+ _updating = FALSE;
+ _dragging = FALSE;
+
+ _tt = gtk_tooltips_new();
+
+ t = gtk_table_new (5, 3, FALSE);
+ gtk_widget_show (t);
+ gtk_box_pack_start (GTK_BOX (_csel), t, TRUE, TRUE, 0);
+
+ /* Create components */
+ row = 0;
+
+ _wheel = sp_color_wheel_new ();
+ gtk_widget_show (_wheel);
+ gtk_table_attach (GTK_TABLE (t), _wheel, 0, 3, row, row + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), XPAD, YPAD);
+
+ row++;
+
+ /* Label */
+ _label = gtk_label_new_with_mnemonic (_("_A"));
+ gtk_misc_set_alignment (GTK_MISC (_label), 1.0, 0.5);
+ gtk_widget_show (_label);
+ gtk_table_attach (GTK_TABLE (t), _label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, XPAD, YPAD);
+
+ /* Adjustment */
+ _adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 255.0, 1.0, 10.0, 10.0);
+
+ /* Slider */
+ _slider = sp_color_slider_new (_adj);
+ gtk_tooltips_set_tip (_tt, _slider, _("Alpha (opacity)"), NULL);
+ gtk_widget_show (_slider);
+ gtk_table_attach (GTK_TABLE (t), _slider, 1, 2, row, row + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)GTK_FILL, XPAD, YPAD);
+
+ sp_color_slider_set_colors (SP_COLOR_SLIDER (_slider),
+ SP_RGBA32_F_COMPOSE (1.0, 1.0, 1.0, 0.0),
+ SP_RGBA32_F_COMPOSE (1.0, 1.0, 1.0, 0.5),
+ SP_RGBA32_F_COMPOSE (1.0, 1.0, 1.0, 1.0));
+
+
+ /* Spinbutton */
+ _sbtn = gtk_spin_button_new (GTK_ADJUSTMENT (_adj), 1.0, 0);
+ gtk_tooltips_set_tip (_tt, _sbtn, _("Alpha (opacity)"), NULL);
+ sp_dialog_defocus_on_enter (_sbtn);
+ gtk_label_set_mnemonic_widget (GTK_LABEL(_label), _sbtn);
+ gtk_widget_show (_sbtn);
+ gtk_table_attach (GTK_TABLE (t), _sbtn, 2, 3, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, XPAD, YPAD);
+
+ /* Signals */
+ gtk_signal_connect (GTK_OBJECT (_adj), "value_changed",
+ GTK_SIGNAL_FUNC (_adjustmentChanged), _csel);
+
+ gtk_signal_connect (GTK_OBJECT (_slider), "grabbed",
+ GTK_SIGNAL_FUNC (_sliderGrabbed), _csel);
+ gtk_signal_connect (GTK_OBJECT (_slider), "released",
+ GTK_SIGNAL_FUNC (_sliderReleased), _csel);
+ gtk_signal_connect (GTK_OBJECT (_slider), "changed",
+ GTK_SIGNAL_FUNC (_sliderChanged), _csel);
+
+ gtk_signal_connect (GTK_OBJECT(_wheel), "changed",
+ GTK_SIGNAL_FUNC (_wheelChanged), _csel);
+}
+
+static void
+sp_color_wheel_selector_destroy (GtkObject *object)
+{
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+static void
+sp_color_wheel_selector_show_all (GtkWidget *widget)
+{
+ gtk_widget_show (widget);
+}
+
+static void
+sp_color_wheel_selector_hide_all (GtkWidget *widget)
+{
+ gtk_widget_hide (widget);
+}
+
+GtkWidget *
+sp_color_wheel_selector_new (void)
+{
+ SPColorWheelSelector *csel;
+
+ csel = (SPColorWheelSelector*)gtk_type_new (SP_TYPE_COLOR_WHEEL_SELECTOR);
+
+ return GTK_WIDGET (csel);
+}
+
+/* Helpers for setting color value */
+
+void ColorWheelSelector::_colorChanged( const SPColor& color, gfloat alpha )
+{
+ _updating = TRUE;
+ sp_color_wheel_set_color( SP_COLOR_WHEEL( _wheel ), &color );
+
+ guint32 start = sp_color_get_rgba32_ualpha( &color, 0x00 );
+ guint32 mid = sp_color_get_rgba32_ualpha( &color, 0x7f );
+ guint32 end = sp_color_get_rgba32_ualpha( &color, 0xff );
+
+ sp_color_slider_set_colors (SP_COLOR_SLIDER(_slider), start, mid, end);
+
+ ColorScales::setScaled(_adj, alpha);
+
+ _updating = FALSE;
+}
+
+void ColorWheelSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorWheelSelector *cs )
+{
+// TODO check this. It looks questionable:
+ // if a value is entered between 0 and 1 exclusive, normalize it to (int) 0..255 or 0..100
+ if (adjustment->value > 0.0 && adjustment->value < 1.0) {
+ gtk_adjustment_set_value( adjustment, floor ((adjustment->value) * adjustment->upper + 0.5) );
+ }
+
+ ColorWheelSelector* wheelSelector = (ColorWheelSelector*)(SP_COLOR_SELECTOR(cs)->base);
+ if (wheelSelector->_updating) return;
+
+ wheelSelector->_updating = TRUE;
+
+ wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging );
+
+ wheelSelector->_updating = FALSE;
+}
+
+void ColorWheelSelector::_sliderGrabbed( SPColorSlider *slider, SPColorWheelSelector *cs )
+{
+ ColorWheelSelector* wheelSelector = (ColorWheelSelector*)(SP_COLOR_SELECTOR(cs)->base);
+ if (!wheelSelector->_dragging) {
+ wheelSelector->_dragging = TRUE;
+ wheelSelector->_grabbed();
+ wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging );
+ }
+}
+
+void ColorWheelSelector::_sliderReleased( SPColorSlider *slider, SPColorWheelSelector *cs )
+{
+ ColorWheelSelector* wheelSelector = (ColorWheelSelector*)(SP_COLOR_SELECTOR(cs)->base);
+ if (wheelSelector->_dragging) {
+ wheelSelector->_dragging = FALSE;
+ wheelSelector->_released();
+ wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging );
+ }
+}
+
+void ColorWheelSelector::_sliderChanged( SPColorSlider *slider, SPColorWheelSelector *cs )
+{
+ ColorWheelSelector* wheelSelector = (ColorWheelSelector*)(SP_COLOR_SELECTOR(cs)->base);
+
+ wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging );
+}
+
+void ColorWheelSelector::_wheelChanged( SPColorWheel *wheel, SPColorWheelSelector *cs )
+{
+ ColorWheelSelector* wheelSelector = (ColorWheelSelector*)(SP_COLOR_SELECTOR(cs)->base);
+ SPColor color;
+
+ sp_color_wheel_get_color( wheel, &color );
+
+ guint32 start = sp_color_get_rgba32_ualpha( &color, 0x00 );
+ guint32 mid = sp_color_get_rgba32_ualpha( &color, 0x7f );
+ guint32 end = sp_color_get_rgba32_ualpha( &color, 0xff );
+
+ sp_color_slider_set_colors (SP_COLOR_SLIDER(wheelSelector->_slider), start, mid, end);
+
+ wheelSelector->_updateInternals( color, wheelSelector->_alpha, sp_color_wheel_is_adjusting( wheel ) );
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/sp-color-wheel-selector.h b/src/widgets/sp-color-wheel-selector.h
new file mode 100644
index 000000000..de358d5ac
--- /dev/null
+++ b/src/widgets/sp-color-wheel-selector.h
@@ -0,0 +1,90 @@
+#ifndef __SP_COLOR_WHEEL_SELECTOR_H__
+#define __SP_COLOR_WHEEL_SELECTOR_H__
+
+#include <glib/gtypes.h>
+#include <gtk/gtktooltips.h>
+#include <gtk/gtkvbox.h>
+
+#include "../color.h"
+#include "sp-color-wheel.h"
+#include "sp-color-slider.h"
+#include "sp-color-selector.h"
+
+
+
+struct SPColorWheelSelector;
+struct SPColorWheelSelectorClass;
+
+
+class ColorWheelSelector: public ColorSelector
+{
+public:
+ ColorWheelSelector( SPColorSelector* csel );
+ virtual ~ColorWheelSelector();
+
+ virtual void init();
+
+protected:
+ virtual void _colorChanged( const SPColor& color, gfloat alpha );
+
+ static void _adjustmentChanged ( GtkAdjustment *adjustment, SPColorWheelSelector *cs );
+
+ static void _sliderGrabbed( SPColorSlider *slider, SPColorWheelSelector *cs );
+ static void _sliderReleased( SPColorSlider *slider, SPColorWheelSelector *cs );
+ static void _sliderChanged( SPColorSlider *slider, SPColorWheelSelector *cs );
+ static void _wheelChanged( SPColorWheel* wheel, SPColorWheelSelector *cs );
+
+ static void _fooChanged( GtkWidget foo, SPColorWheelSelector *cs );
+
+ void _recalcColor( gboolean changing );
+
+ gboolean _updating : 1;
+ gboolean _dragging : 1;
+ GtkAdjustment* _adj; /* Channel adjustment */
+ GtkWidget* _wheel;
+ GtkWidget* _slider;
+ GtkWidget* _sbtn; /* Spinbutton */
+ GtkWidget* _label; /* Label */
+
+ GtkTooltips* _tt; /* tooltip container */
+
+private:
+ // By default, disallow copy constructor and assignment operator
+ ColorWheelSelector( const ColorWheelSelector& obj );
+ ColorWheelSelector& operator=( const ColorWheelSelector& obj );
+};
+
+
+
+#define SP_TYPE_COLOR_WHEEL_SELECTOR (sp_color_wheel_selector_get_type ())
+#define SP_COLOR_WHEEL_SELECTOR(o) (GTK_CHECK_CAST ((o), SP_TYPE_COLOR_WHEEL_SELECTOR, SPColorWheelSelector))
+#define SP_COLOR_WHEEL_SELECTOR_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_COLOR_WHEEL_SELECTOR, SPColorWheelSelectorClass))
+#define SP_IS_COLOR_WHEEL_SELECTOR(o) (GTK_CHECK_TYPE ((o), SP_TYPE_COLOR_WHEEL_SELECTOR))
+#define SP_IS_COLOR_WHEEL_SELECTOR_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_COLOR_WHEEL_SELECTOR))
+
+struct SPColorWheelSelector {
+ SPColorSelector parent;
+};
+
+struct SPColorWheelSelectorClass {
+ SPColorSelectorClass parent_class;
+};
+
+GType sp_color_wheel_selector_get_type (void);
+
+GtkWidget *sp_color_wheel_selector_new (void);
+
+
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/sp-color-wheel.cpp b/src/widgets/sp-color-wheel.cpp
new file mode 100644
index 000000000..9038b6d38
--- /dev/null
+++ b/src/widgets/sp-color-wheel.cpp
@@ -0,0 +1,1163 @@
+#define __SP_COLOR_WHEEL_C__
+
+/*
+ * A wheel color widget
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2003-2004 Authors
+ *
+ * This code is in public domain
+ */
+
+#include <gtk/gtksignal.h>
+#include "sp-color-wheel.h"
+
+#include "libnr/nr-rotate-ops.h"
+
+#define WHEEL_SIZE 96
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static void sp_color_wheel_class_init (SPColorWheelClass *klass);
+static void sp_color_wheel_init (SPColorWheel *wheel);
+static void sp_color_wheel_destroy (GtkObject *object);
+
+static void sp_color_wheel_realize (GtkWidget *widget);
+static void sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition);
+static void sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
+
+static gint sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event);
+static gint sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event);
+static gint sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event);
+static gint sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event);
+
+static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue);
+static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value );
+static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel);
+
+static void sp_color_wheel_paint (SPColorWheel *wheel, GdkRectangle *area);
+static void sp_color_wheel_render_hue_wheel (SPColorWheel *wheel);
+static void sp_color_wheel_render_triangle (SPColorWheel *wheel);
+
+
+static gboolean sp_color_wheel_focus(GtkWidget *widget,
+ GtkDirectionType direction);
+
+static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y );
+
+static GtkWidgetClass *parent_class;
+static guint wheel_signals[LAST_SIGNAL] = {0};
+
+/*
+static double
+get_time (void)
+{
+ GTimeVal tv;
+ g_get_current_time (&tv);
+ return tv.tv_sec + 1e-6 * tv.tv_usec;
+}
+*/
+
+GtkType
+sp_color_wheel_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GtkTypeInfo info = {
+ "SPColorWheel",
+ sizeof (SPColorWheel),
+ sizeof (SPColorWheelClass),
+ (GtkClassInitFunc) sp_color_wheel_class_init,
+ (GtkObjectInitFunc) sp_color_wheel_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_WIDGET, &info);
+ }
+ return type;
+}
+
+static void
+sp_color_wheel_class_init (SPColorWheelClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ parent_class = (GtkWidgetClass*)gtk_type_class (GTK_TYPE_WIDGET);
+
+ wheel_signals[CHANGED] = gtk_signal_new ("changed",
+ (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPColorWheelClass, changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ object_class->destroy = sp_color_wheel_destroy;
+
+ widget_class->realize = sp_color_wheel_realize;
+ widget_class->size_request = sp_color_wheel_size_request;
+ widget_class->size_allocate = sp_color_wheel_size_allocate;
+
+ widget_class->focus = sp_color_wheel_focus;
+
+ widget_class->expose_event = sp_color_wheel_expose;
+ widget_class->button_press_event = sp_color_wheel_button_press;
+ widget_class->button_release_event = sp_color_wheel_button_release;
+ widget_class->motion_notify_event = sp_color_wheel_motion_notify;
+}
+
+static void
+sp_color_wheel_init (SPColorWheel *wheel)
+{
+ /* We are widget with window */
+ GTK_WIDGET_UNSET_FLAGS (wheel, GTK_NO_WINDOW);
+ GTK_WIDGET_SET_FLAGS (wheel, GTK_CAN_FOCUS );
+
+ wheel->dragging = FALSE;
+
+ wheel->_inTriangle = FALSE;
+ wheel->_triDirty = TRUE;
+ wheel->_triangle = NULL;
+ for ( guint i = 0; i < G_N_ELEMENTS(wheel->_triPoints); i++ )
+ {
+ wheel->_triPoints[i].x = 0;
+ wheel->_triPoints[i].y = 0;
+ }
+ wheel->_triImage = NULL;
+ wheel->_triBs = 0;
+
+ wheel->_image = NULL;
+ wheel->_bs = 0;
+ wheel->_hue = 0.0;
+ wheel->_sat = 0.9;
+ wheel->_value = 0.25;
+ wheel->_inner = 0;
+ wheel->_center = 0;
+ wheel->_spotValue = 1.0;
+}
+
+static void
+sp_color_wheel_destroy (GtkObject *object)
+{
+ SPColorWheel *wheel;
+
+ wheel = SP_COLOR_WHEEL (object);
+
+ if ( wheel->_image )
+ {
+ g_free( wheel->_image );
+ wheel->_image = NULL;
+ wheel->_bs = 0;
+ }
+
+ if ( wheel->_triImage )
+ {
+ g_free( wheel->_triImage );
+ wheel->_triImage = NULL;
+ wheel->_triBs = 0;
+ }
+
+ if (((GtkObjectClass *) (parent_class))->destroy)
+ (* ((GtkObjectClass *) (parent_class))->destroy) (object);
+}
+
+
+void sp_color_wheel_get_color( SPColorWheel *wheel, SPColor* color )
+{
+ float rgb[3];
+ gint i;
+ g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
+ g_return_if_fail (wheel != NULL);
+ g_return_if_fail (color != NULL);
+
+ sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
+ for ( i = 0; i < 3; i++ )
+ {
+ rgb[i] = (rgb[i] * wheel->_sat) + (wheel->_value * (1.0 - wheel->_sat));
+ }
+
+ sp_color_set_rgb_float (color, rgb[0], rgb[1], rgb[2]);
+}
+
+void sp_color_wheel_set_color( SPColorWheel *wheel, const SPColor* color )
+{
+ g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
+ g_return_if_fail (wheel != NULL);
+ g_return_if_fail (color != NULL);
+
+ float hue;
+ float scratch[3];
+ float rgb[3];
+
+ sp_color_get_rgb_floatv (color, rgb);
+ sp_color_rgb_to_hsv_floatv (scratch, rgb[0], rgb[1], rgb[2]);
+ hue = scratch[0];
+
+ sp_color_hsv_to_rgb_floatv (scratch, hue, 1.0, 1.0);
+
+ gint lowInd = 0;
+ gint hiInd = 0;
+ for ( int i = 1; i < 3; i++ )
+ {
+ if ( scratch[i] < scratch[lowInd] )
+ {
+ lowInd = i;
+ }
+ if ( scratch[i] > scratch[hiInd] )
+ {
+ hiInd = i;
+ }
+ }
+ // scratch[lowInd] should always be 0
+ gdouble sat = (rgb[hiInd] - rgb[lowInd])/(scratch[hiInd]-scratch[lowInd]);
+ gdouble val = sat < 1.0 ? (rgb[hiInd] - sat * scratch[hiInd])/(1.0-sat) : 0.0;
+
+
+ sp_color_wheel_set_hue(wheel, hue);
+ sp_color_wheel_set_sv(wheel, sat, val);
+}
+
+gboolean sp_color_wheel_is_adjusting( SPColorWheel *wheel )
+{
+ g_return_val_if_fail( SP_IS_COLOR_WHEEL(wheel), FALSE );
+ return wheel->dragging;
+}
+
+static void
+sp_color_wheel_realize (GtkWidget *widget)
+{
+ SPColorWheel *wheel;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ wheel = SP_COLOR_WHEEL (widget);
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gdk_rgb_get_visual ();
+ attributes.colormap = gdk_rgb_get_cmap ();
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= (GDK_EXPOSURE_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_FOCUS_CHANGE_MASK );
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
+ gdk_window_set_user_data (widget->window, widget);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+}
+
+static void
+sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+ SPColorWheel *wheel;
+
+ wheel = SP_COLOR_WHEEL (widget);
+
+ requisition->width = WHEEL_SIZE + widget->style->xthickness * 2;
+ requisition->height = WHEEL_SIZE + widget->style->ythickness * 2;
+}
+
+static void
+sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ SPColorWheel *wheel;
+
+ wheel = SP_COLOR_WHEEL (widget);
+
+ widget->allocation = *allocation;
+
+ wheel->_center = MIN(allocation->width, allocation->height)/2;
+ wheel->_inner = (3 * wheel->_center)/4;
+ if ( wheel->_image )
+ {
+ g_free( wheel->_image );
+ wheel->_image = NULL;
+ wheel->_bs = 0;
+ }
+
+ // Need to render the gradient before we do the triangle over
+ sp_color_wheel_render_hue_wheel(wheel);
+ sp_color_wheel_recalc_triangle(wheel);
+ sp_color_wheel_render_triangle(wheel);
+
+ if (GTK_WIDGET_REALIZED (widget)) {
+ /* Resize GdkWindow */
+ gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
+ }
+}
+
+static gint
+sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+ SPColorWheel *wheel;
+
+ wheel = SP_COLOR_WHEEL (widget);
+
+ if (GTK_WIDGET_DRAWABLE (widget)) {
+ gint width, height;
+ width = widget->allocation.width;
+ height = widget->allocation.height;
+ sp_color_wheel_paint (wheel, &event->area);
+ }
+
+ return TRUE;
+}
+
+static gint
+sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event)
+{
+ SPColorWheel *wheel;
+
+ wheel = SP_COLOR_WHEEL (widget);
+
+ if (event->button == 1) {
+ gint cx, cw;
+ cx = widget->style->xthickness;
+ cw = widget->allocation.width - 2 * cx;
+ gboolean grabbed = FALSE;
+
+ {
+ double dx = event->x - wheel->_center;
+ double dy = event->y - wheel->_center;
+ gint hyp = static_cast<gint>(ABS(dx*dx) + ABS(dy*dy));
+ if ( hyp <= (wheel->_center*wheel->_center) )
+ {
+ if ( hyp >= (wheel->_inner*wheel->_inner) )
+ {
+ gdouble rot = atan2( dy, -dx );
+ sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
+
+ wheel->_inTriangle = FALSE;
+ grabbed = TRUE;
+ }
+ else if ( wheel->_triangle && gdk_region_point_in( wheel->_triangle, (gint)event->x, (gint)event->y ) )
+ {
+ wheel->_inTriangle = TRUE;
+ sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
+ grabbed = TRUE;
+ }
+ }
+ }
+
+ if ( grabbed )
+ {
+ gtk_widget_queue_draw( widget );
+ gtk_widget_grab_focus( widget );
+
+ wheel->dragging = TRUE;
+ gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
+ gdk_pointer_grab (widget->window, FALSE,
+ (GdkEventMask)(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK),
+ NULL, NULL, event->time);
+ }
+ }
+
+ return TRUE;
+}
+
+static gint
+sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event)
+{
+ SPColorWheel *wheel;
+
+ wheel = SP_COLOR_WHEEL (widget);
+
+ if (event->button == 1) {
+ gdk_pointer_ungrab (event->time);
+ wheel->dragging = FALSE;
+
+ gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
+ }
+
+ return TRUE;
+}
+
+static gint
+sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event)
+{
+ SPColorWheel *wheel;
+
+ wheel = SP_COLOR_WHEEL (widget);
+
+ if (wheel->dragging) {
+ double dx = event->x - wheel->_center;
+ double dy = event->y - wheel->_center;
+ if ( !wheel->_inTriangle )
+ {
+ gdouble rot = atan2( dy, -dx );
+ sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
+ }
+ else
+ {
+ sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
+ }
+
+ gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
+ }
+
+ return TRUE;
+}
+
+GtkWidget *
+sp_color_wheel_new ()
+{
+ SPColorWheel *wheel;
+
+ wheel = (SPColorWheel*)gtk_type_new (SP_TYPE_COLOR_WHEEL);
+
+ return GTK_WIDGET (wheel);
+}
+
+static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue)
+{
+ g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
+
+ if ( wheel->_hue != hue )
+ {
+ wheel->_hue = hue;
+
+ sp_color_wheel_recalc_triangle(wheel);
+
+ SPColor color;
+ gfloat rgb[3];
+ sp_color_wheel_get_color( wheel, &color );
+ sp_color_get_rgb_floatv (&color, rgb);
+
+ wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
+
+ gtk_widget_queue_draw (GTK_WIDGET (wheel));
+ }
+}
+
+
+static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value )
+{
+ static gdouble epsilon = 1e-6;
+ gboolean changed = FALSE;
+
+ if ( ABS( wheel->_sat - sat ) > epsilon )
+ {
+ wheel->_sat = sat;
+ changed = TRUE;
+ }
+ if ( ABS( wheel->_value - value ) > epsilon )
+ {
+ wheel->_value = value;
+ changed = TRUE;
+ }
+
+ if ( changed )
+ {
+ SPColor color;
+ gfloat rgb[3];
+ sp_color_wheel_get_color( wheel, &color );
+ sp_color_get_rgb_floatv (&color, rgb);
+
+ wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
+
+ gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
+ }
+ gtk_widget_queue_draw (GTK_WIDGET (wheel));
+}
+
+static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel)
+{
+ if ( wheel->_triangle )
+ {
+ gdk_region_destroy( wheel->_triangle );
+ wheel->_triangle = NULL;
+ }
+ wheel->_triDirty = TRUE;
+
+ if ( wheel->_center > 0 && wheel->_inner > 0 )
+ {
+ gdouble dx = cos( M_PI * 2 * wheel->_hue );
+ gdouble dy = -sin( M_PI * 2 * wheel->_hue );
+
+ wheel->_triPoints[0].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
+ wheel->_triPoints[0].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
+
+ dx = cos( M_PI * 2 * wheel->_hue + ((M_PI * 2)/ 3) );
+ dy = -sin( M_PI * 2 * wheel->_hue + ((M_PI * 2) / 3) );
+ wheel->_triPoints[1].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
+ wheel->_triPoints[1].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
+
+ dx = cos( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
+ dy = -sin( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
+ wheel->_triPoints[2].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
+ wheel->_triPoints[2].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
+
+
+ wheel->_triangle = gdk_region_polygon( wheel->_triPoints,
+ 3,
+ GDK_EVEN_ODD_RULE );
+ }
+}
+
+#define VERT_SWAP( X, Y ) { \
+ gfloat tmpF; \
+ \
+ tmpF = point##X.x; \
+ point##X.x = point##Y.x; \
+ point##Y.x = tmpF; \
+ \
+ tmpF = point##X.y; \
+ point##X.y = point##Y.y; \
+ point##Y.y = tmpF; \
+ \
+ tmpF = rgb##X[0]; \
+ rgb##X[0] = rgb##Y[0]; \
+ rgb##Y[0] = tmpF; \
+ \
+ tmpF = rgb##X[1]; \
+ rgb##X[1] = rgb##Y[1]; \
+ rgb##Y[1] = tmpF; \
+ \
+ tmpF = rgb##X[2]; \
+ rgb##X[2] = rgb##Y[2]; \
+ rgb##Y[2] = tmpF; \
+}
+
+#define VERT_COPY( dst, src ) { \
+ point##dst.x = point##src.x; \
+ \
+ point##dst.y = point##src.y; \
+ \
+ rgb##dst[0] = rgb##src[0]; \
+ rgb##dst[1] = rgb##src[1]; \
+ rgb##dst[2] = rgb##src[2]; \
+}
+
+typedef struct {
+ gfloat x;
+ gfloat y;
+} PointF;
+
+static void sp_color_wheel_render_triangle (SPColorWheel *wheel)
+{
+ if ( wheel->_image )
+ {
+ if ( wheel->_triBs < wheel->_bs )
+ {
+ g_free( wheel->_triImage );
+ wheel->_triImage = NULL;
+ }
+
+ if (wheel->_triDirty || !wheel->_triImage)
+ {
+ if ( !wheel->_triImage )
+ {
+ wheel->_triBs = wheel->_bs;
+ wheel->_triImage = g_new (guchar, wheel->_triBs * 3);
+ //g_message( "just allocated %fKB for tri", ((wheel->_triBs * 3.0)/1024.0) );
+ }
+
+ memcpy( wheel->_triImage, wheel->_image, wheel->_bs * 3 );
+
+ PointF pointA, pointB, pointC;
+ pointA.x = wheel->_triPoints[0].x;
+ pointA.y = wheel->_triPoints[0].y;
+ pointB.x = wheel->_triPoints[1].x;
+ pointB.y = wheel->_triPoints[1].y;
+ pointC.x = wheel->_triPoints[2].x;
+ pointC.y = wheel->_triPoints[2].y;
+
+ gfloat rgbA[3];
+ gfloat rgbB[3] = {0.0, 0.0, 0.0};
+ gfloat rgbC[3] = {1.0, 1.0, 1.0};
+
+ sp_color_hsv_to_rgb_floatv (rgbA, wheel->_hue, 1.0, 1.0);
+
+// Start of Gouraud fill ============================================================
+ gint rowStride = wheel->_center * 2 * 3;
+ guchar* dst = wheel->_triImage;
+
+ if ( pointC.y < pointB.y )
+ VERT_SWAP( C, B );
+
+ if ( pointC.y < pointA.y )
+ VERT_SWAP( C, A );
+
+ if ( pointB.y < pointA.y )
+ VERT_SWAP( B, A );
+
+ if ( pointA.y == pointB.y && pointB.x < pointA.x )
+ VERT_SWAP( A, B );
+
+ gfloat dr, dg, db;
+
+ gfloat dx1,dx2,dx3;
+ gfloat dr1,dr2,dr3;
+ gfloat dg1,dg2,dg3;
+ gfloat db1,db2,db3;
+
+
+ PointF pointS;
+ PointF pointE;
+ PointF pointP;
+ gfloat rgbS[3];
+ gfloat rgbE[3];
+ gfloat rgbP[3];
+
+
+ if (pointB.y-pointA.y > 0)
+ {
+ dx1=(pointB.x-pointA.x)/(pointB.y-pointA.y);
+ dr1=(rgbB[0]-rgbA[0])/(pointB.y-pointA.y);
+ dg1=(rgbB[1]-rgbA[1])/(pointB.y-pointA.y);
+ db1=(rgbB[2]-rgbA[2])/(pointB.y-pointA.y);
+ }
+ else
+ {
+ dx1=dr1=dg1=db1=0;
+ }
+
+ if (pointC.y-pointA.y > 0)
+ {
+ dx2=(pointC.x-pointA.x)/(pointC.y-pointA.y);
+ dr2=(rgbC[0]-rgbA[0])/(pointC.y-pointA.y);
+ dg2=(rgbC[1]-rgbA[1])/(pointC.y-pointA.y);
+ db2=(rgbC[2]-rgbA[2])/(pointC.y-pointA.y);
+ }
+ else
+ {
+ dx2=dr2=dg2=db2=0;
+ }
+
+ if (pointC.y-pointB.y > 0)
+ {
+ dx3=(pointC.x-pointB.x)/(pointC.y-pointB.y);
+ dr3=(rgbC[0]-rgbB[0])/(pointC.y-pointB.y);
+ dg3=(rgbC[1]-rgbB[1])/(pointC.y-pointB.y);
+ db3=(rgbC[2]-rgbB[2])/(pointC.y-pointB.y);
+ }
+ else
+ {
+ dx3=dr3=dg3=db3=0;
+ }
+
+ VERT_COPY(S, A);
+ VERT_COPY(E, A);
+
+ if ( dx1 == 0 )
+ {
+ VERT_COPY(E,B);
+ for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
+ {
+ if (pointE.x-pointS.x > 0)
+ {
+ dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
+ dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
+ db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
+ }
+ else
+ {
+ dr=dg=db=0;
+ }
+ VERT_COPY(P,S);
+ dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
+ dst += static_cast<gint>(pointP.x) * 3;
+ for(;pointP.x < pointE.x;pointP.x++)
+ {
+ //putpixel(P);
+ dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
+ dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
+ dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
+ dst += 3;
+ rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
+ }
+ pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
+ pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
+ }
+ }
+ else if (dx1 > dx2)
+ {
+ for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
+ {
+ if (pointE.x-pointS.x > 0)
+ {
+ dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
+ dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
+ db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
+ }
+ else
+ {
+ dr=dg=db=0;
+ }
+ VERT_COPY(P,S);
+ dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
+ dst += static_cast<gint>(pointP.x) * 3;
+ for(;pointP.x < pointE.x;pointP.x++)
+ {
+ //putpixel(P);
+ dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
+ dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
+ dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
+ dst += 3;
+ rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
+ }
+ pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
+ pointE.x+=dx1; rgbE[0]+=dr1; rgbE[1]+=dg1; rgbE[2]+=db1;
+ }
+ VERT_COPY(E,B);
+ for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
+ {
+ if (pointE.x-pointS.x > 0)
+ {
+ dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
+ dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
+ db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
+ }
+ else
+ {
+ dr=dg=db=0;
+ }
+ VERT_COPY(P,S);
+ dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
+ dst += static_cast<gint>(pointP.x) * 3;
+ for(;pointP.x < pointE.x;pointP.x++)
+ {
+ //putpixel(P);
+ dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
+ dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
+ dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
+ dst += 3;
+ rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
+ }
+ pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
+ pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
+ }
+ }
+ else if ( dx1 )
+ {
+ for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
+ {
+ if (pointE.x-pointS.x > 0)
+ {
+ dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
+ dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
+ db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
+ }
+ else
+ {
+ dr=dg=db=0;
+ }
+ VERT_COPY(P,S);
+ dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
+ dst += static_cast<gint>(pointP.x) * 3;
+ for(;pointP.x < pointE.x;pointP.x++)
+ {
+ //putpixel(P);
+ dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
+ dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
+ dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
+ dst += 3;
+ rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
+ }
+ pointS.x+=dx1; rgbS[0]+=dr1; rgbS[1]+=dg1; rgbS[2]+=db1;
+ pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
+ }
+ VERT_COPY(S,B);
+ for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
+ {
+ if (pointE.x-pointS.x > 0)
+ {
+ dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
+ dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
+ db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
+ }
+ else
+ {
+ dr=dg=db=0;
+ }
+ VERT_COPY(P,S);
+ dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
+ dst += static_cast<gint>(pointP.x) * 3;
+ for(;pointP.x < pointE.x;pointP.x++)
+ {
+ //putpixel(P);
+ dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
+ dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
+ dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
+ dst += 3;
+ rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
+ }
+ pointS.x+=dx3; rgbS[0]+=dr3; rgbS[1]+=dg3; rgbS[2]+=db3;
+ pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
+ }
+ }
+
+// End of Gouraud fill ============================================================
+
+ wheel->_triDirty = FALSE;
+ //g_message( "Just updated triangle" );
+ }
+ }
+}
+
+static void
+sp_color_wheel_paint (SPColorWheel *wheel, GdkRectangle *area)
+{
+ GtkWidget *widget;
+ GdkRectangle warea, carea;
+ GdkRectangle wpaint, cpaint;
+
+ widget = GTK_WIDGET (wheel);
+
+ /* Widget area */
+ warea.x = 0;
+ warea.y = 0;
+ warea.width = widget->allocation.width;
+ warea.height = widget->allocation.height;
+
+ /* Color gradient area */
+ carea.x = widget->style->xthickness;
+ carea.y = widget->style->ythickness;
+ carea.width = widget->allocation.width - 2 * carea.x;
+ carea.height = widget->allocation.height - 2 * carea.y;
+
+ /* Actual paintable area */
+ if (!gdk_rectangle_intersect (area, &warea, &wpaint)) return;
+
+ //g_message( "Painted as state %d", widget->state );
+
+ /* Paintable part of color gradient area */
+ if (gdk_rectangle_intersect (area, &carea, &cpaint)) {
+ sp_color_wheel_render_hue_wheel (wheel);
+ sp_color_wheel_render_triangle (wheel);
+ }
+
+/*
+ gtk_draw_box (widget->style,
+ widget->window,
+ (GtkStateType)widget->state,
+ GTK_SHADOW_NONE,
+ warea.x,
+ warea.y,
+ warea.width,
+ warea.height);
+*/
+
+ gtk_style_apply_default_background( widget->style,
+ widget->window,
+ TRUE,
+ (GtkStateType)widget->state,
+ NULL,
+ 0,
+ 0,
+ warea.width,
+ warea.height);
+
+
+ /* Draw shadow */
+/*
+ gtk_paint_shadow (widget->style, widget->window,
+ (GtkStateType)widget->state, GTK_SHADOW_IN,
+ NULL, widget, "colorwheel",
+ 0, 0,
+ warea.width, warea.height);
+*/
+
+
+ /* Draw pixelstore */
+ if (wheel->_triImage != NULL) {
+ //gdouble start, end;
+ //start = get_time();
+ gdk_draw_rgb_image (widget->window, widget->style->black_gc,
+ 0, 0,//cpaint.x, cpaint.y,
+ //cpaint.width, cpaint.height,
+ wheel->_center * 2, wheel->_center * 2,
+ GDK_RGB_DITHER_MAX,
+ wheel->_triImage, wheel->_center * 6);
+
+ //end = get_time();
+ //g_message( "blits took %f", (end-start) );
+ }
+
+ {
+ gdouble dx = cos( M_PI * 2 * wheel->_hue );
+ gdouble dy = -sin( M_PI * 2 * wheel->_hue );
+
+ gfloat rgb[3];
+ sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
+
+ GdkGC *line_gc = (( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) ) < 0.5) ? widget->style->white_gc : widget->style->black_gc;
+
+ gint inx = wheel->_center + static_cast<gint>(dx * wheel->_inner);
+ gint iny = wheel->_center + static_cast<gint>(dy * wheel->_inner);
+
+
+ gdk_draw_line (widget->window, line_gc,
+ inx, iny,
+ wheel->_center + static_cast<gint>(dx * wheel->_center), wheel->_center + static_cast<gint>(dy * wheel->_center) );
+
+
+ GdkGCValues values;
+
+ if ( GTK_WIDGET_HAS_FOCUS(wheel) )
+ {
+ line_gc = widget->style->black_gc;
+
+ gdk_gc_get_values ( line_gc, &values );
+
+ gdk_gc_set_line_attributes ( line_gc,
+ 3, // Line width
+ values.line_style, //GDK_LINE_SOLID,
+ values.cap_style, //GDK_CAP_BUTT,
+ values.join_style ); //GDK_JOIN_MITER );
+
+ if ( wheel->_inTriangle )
+ {
+ gdk_draw_line (widget->window, line_gc,
+ wheel->_triPoints[0].x, wheel->_triPoints[0].y,
+ wheel->_triPoints[1].x, wheel->_triPoints[1].y );
+
+ gdk_draw_line (widget->window, line_gc,
+ wheel->_triPoints[1].x, wheel->_triPoints[1].y,
+ wheel->_triPoints[2].x, wheel->_triPoints[2].y );
+
+ gdk_draw_line (widget->window, line_gc,
+ wheel->_triPoints[2].x, wheel->_triPoints[2].y,
+ wheel->_triPoints[0].x, wheel->_triPoints[0].y );
+ }
+ else
+ {
+ gdk_draw_arc (widget->window, line_gc,
+ FALSE, // filled
+ 0, 0,
+ wheel->_center * 2, wheel->_center * 2,
+ 0, 64 * 360 );
+
+ gint diff = wheel->_center - wheel->_inner;
+
+ gdk_draw_arc (widget->window, line_gc,
+ FALSE, // filled
+ diff, diff,
+ wheel->_inner * 2, wheel->_inner * 2,
+ 0, 64 * 360 );
+
+ }
+ gdk_gc_set_line_attributes ( line_gc,
+ values.line_width, // Line width
+ values.line_style, //GDK_LINE_SOLID,
+ values.cap_style, //GDK_CAP_BUTT,
+ values.join_style ); //GDK_JOIN_MITER );
+ }
+// ==========
+
+// line_gc = (p[3] < 0x80) ? widget->style->white_gc : widget->style->black_gc;
+ line_gc = (wheel->_spotValue < 0.5) ? widget->style->white_gc : widget->style->black_gc;
+
+ gdk_gc_get_values ( line_gc, &values );
+
+ gdk_gc_set_line_attributes ( line_gc,
+ 2, // Line width
+ values.line_style, //GDK_LINE_SOLID,
+ values.cap_style, //GDK_CAP_BUTT,
+ values.join_style ); //GDK_JOIN_MITER );
+
+ gint pointX = (gint)( (1.0 - wheel->_sat) * ((1.0-wheel->_value)*(gdouble)wheel->_triPoints[1].x + wheel->_value*(gdouble)wheel->_triPoints[2].x)
+ + (wheel->_sat * wheel->_triPoints[0].x) );
+
+ gint pointY = (gint)( (1.0 - wheel->_sat) * ((1.0-wheel->_value)*(gdouble)wheel->_triPoints[1].y + wheel->_value*(gdouble)wheel->_triPoints[2].y)
+ + (wheel->_sat * wheel->_triPoints[0].y) );
+
+
+ gdk_gc_set_line_attributes ( line_gc,
+ values.line_width, // Line width
+ values.line_style, //GDK_LINE_SOLID,
+ values.cap_style, //GDK_CAP_BUTT,
+ values.join_style ); //GDK_JOIN_MITER );
+
+ gdk_draw_arc (widget->window, line_gc,
+ FALSE, // filled
+ pointX - 4, pointY - 4,
+ 8, 8,
+ 0, 64 * 360 );
+
+ gdk_draw_arc (widget->window, line_gc,
+ FALSE, // filled
+ pointX - 3, pointY - 3,
+ 6, 6,
+ 0, 64 * 360 );
+
+
+
+ }
+}
+
+/* Colors are << 16 */
+
+static void
+sp_color_wheel_render_hue_wheel (SPColorWheel *wheel)
+{
+ guchar *dp;
+ gint x, y;
+ guint r, g, b;
+ gint size = wheel->_center * 2;
+ gboolean dirty = FALSE;
+
+ if (wheel->_image && (wheel->_bs < (size * size) )) {
+ g_free (wheel->_image);
+ wheel->_image = NULL;
+ wheel->_bs = 0;
+
+ if ( wheel->_triImage )
+ {
+ g_free( wheel->_triImage );
+ wheel->_triImage = NULL;
+ wheel->_triBs = 0;
+ wheel->_triDirty = TRUE;
+ }
+ }
+
+ if (!wheel->_image) {
+ wheel->_image = g_new (guchar, size * size * 3);
+ wheel->_bs = size * size;
+ dirty = TRUE;
+ //g_message( "just allocated %fKB for hue", ((wheel->_bs * 3.0)/1024.0) );
+ }
+
+ if ( dirty )
+ {
+ GtkWidget* widget = GTK_WIDGET (wheel);
+ dp = wheel->_image;
+ r = widget->style->bg[widget->state].red >> 8;
+ g = widget->style->bg[widget->state].green >> 8;
+ b = widget->style->bg[widget->state].blue >> 8;
+ //g_message( "Rendered as state %d", widget->state );
+
+ gint offset = wheel->_center;
+ gint inner = wheel->_inner * wheel->_inner;
+ gint rad = wheel->_center * wheel->_center;
+
+ for (x = 0; x < size; x++) {
+ guchar *d = dp;
+ for (y = 0; y < size; y++) {
+ gint dx = x - offset;
+ gint dy = y - offset;
+ gint hyp = (ABS(dx*dx) + ABS(dy*dy));
+ if ( hyp >= inner && hyp <= rad)
+ {
+ gdouble rot = atan2( static_cast<gdouble>(dy), static_cast<gdouble>(-dx) );
+
+ gfloat rgb[3];
+ sp_color_hsv_to_rgb_floatv (rgb, (rot + M_PI) / (2.0 * M_PI), 1.0, 1.0);
+
+ d[0] = SP_COLOR_F_TO_U (rgb[0]);
+ d[1] = SP_COLOR_F_TO_U (rgb[1]);
+ d[2] = SP_COLOR_F_TO_U (rgb[2]);
+ }
+ else
+ {
+ /* Background value */
+ d[0] = r;
+ d[1] = g;
+ d[2] = b;
+ }
+
+ d += 3 * size;
+ }
+ dp += 3;
+ }
+ }
+}
+
+static gboolean sp_color_wheel_focus(GtkWidget *widget,
+ GtkDirectionType direction)
+{
+ gboolean focusKept = FALSE;
+ gboolean wasFocused = GTK_WIDGET_HAS_FOCUS(widget);
+ SPColorWheel* wheel = SP_COLOR_WHEEL(widget);
+ gboolean goingUp = FALSE;
+
+ switch ( direction )
+ {
+ case GTK_DIR_TAB_FORWARD:
+ case GTK_DIR_UP:
+ case GTK_DIR_LEFT:
+ goingUp = TRUE;
+ break;
+
+ case GTK_DIR_TAB_BACKWARD:
+ case GTK_DIR_DOWN:
+ case GTK_DIR_RIGHT:
+ goingUp = FALSE;
+ break;
+ default:
+ ;
+ }
+
+ if ( !wasFocused )
+ {
+ wheel->_inTriangle = !goingUp;
+ gtk_widget_grab_focus (widget);
+ focusKept = TRUE;
+ }
+ else if ( (!wheel->_inTriangle) == (!goingUp) )
+ {
+ focusKept = FALSE;
+ }
+ else
+ {
+ wheel->_inTriangle = !wheel->_inTriangle;
+ gtk_widget_queue_draw( widget );
+ focusKept = TRUE;
+ }
+
+ return focusKept;
+}
+
+static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y )
+{
+// njh: dot(rot90(B-C), x) = saturation
+// njh: dot(B-C, x) = value
+ NR::Point delta( x - (((gdouble)(wheel->_triPoints[1].x + wheel->_triPoints[2].x)) / 2.0),
+ y - (((gdouble)(wheel->_triPoints[1].y + wheel->_triPoints[2].y)) / 2.0) );
+
+ gdouble rot = (M_PI * 2 * wheel->_hue );
+
+ NR::Point result = delta * NR::rotate(rot);
+
+ gdouble sat = CLAMP( result[NR::X] / (wheel->_inner * 1.5), 0.0, 1.0 );
+
+ gdouble halfHeight = (wheel->_inner * sin(M_PI/3.0)) * (1.0 - sat);
+ gdouble value = CLAMP( ((result[NR::Y]+ halfHeight) / (2.0*halfHeight)), 0.0, 1.0 );
+
+ wheel->_triDirty = TRUE;
+
+ sp_color_wheel_set_sv( wheel, sat, value );
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/sp-color-wheel.h b/src/widgets/sp-color-wheel.h
new file mode 100644
index 000000000..d8fceb908
--- /dev/null
+++ b/src/widgets/sp-color-wheel.h
@@ -0,0 +1,80 @@
+#ifndef __SP_COLOR_WHEEL_H__
+#define __SP_COLOR_WHEEL_H__
+
+/*
+ * A wheel color widget
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001-2004 Authors
+ *
+ * This code is in public domain
+ */
+
+#include <gtk/gtkwidget.h>
+
+#include <glib.h>
+#include "color.h"
+
+
+#define SP_TYPE_COLOR_WHEEL (sp_color_wheel_get_type ())
+#define SP_COLOR_WHEEL(o) (GTK_CHECK_CAST ((o), SP_TYPE_COLOR_WHEEL, SPColorWheel))
+#define SP_COLOR_WHEEL_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_COLOR_WHEEL, SPColorWheelClass))
+#define SP_IS_COLOR_WHEEL(o) (GTK_CHECK_TYPE ((o), SP_TYPE_COLOR_WHEEL))
+#define SP_IS_COLOR_WHEEL_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_COLOR_WHEEL))
+
+struct SPColorWheel {
+ GtkWidget widget;
+
+ gboolean dragging : 1;
+
+ gboolean _inTriangle;
+ gboolean _triDirty;
+ GdkRegion* _triangle;
+ GdkPoint _triPoints[3];
+ guchar *_triImage;
+ gint _triBs;
+
+ guchar *_image;
+ gint _bs;
+
+ gdouble _spotValue;
+
+ gdouble _hue;
+ gdouble _sat;
+ gdouble _value;
+
+ gint _inner;
+ gint _center;
+};
+
+struct SPColorWheelClass {
+ GtkWidgetClass parent_class;
+
+ void (* changed) (SPColorWheel *wheel);
+};
+
+GtkType sp_color_wheel_get_type (void);
+
+GtkWidget *sp_color_wheel_new ();
+
+void sp_color_wheel_get_color( SPColorWheel *wheel, SPColor* color );
+void sp_color_wheel_set_color( SPColorWheel *wheel, const SPColor* color );
+
+gboolean sp_color_wheel_is_adjusting( SPColorWheel *wheel );
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/sp-widget.cpp b/src/widgets/sp-widget.cpp
new file mode 100644
index 000000000..696daa9d2
--- /dev/null
+++ b/src/widgets/sp-widget.cpp
@@ -0,0 +1,260 @@
+#define __SP_WIDGET_C__
+
+/*
+ * Abstract base class for dynamic control widgets
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "macros.h"
+#include "../document.h"
+#include "sp-widget.h"
+
+enum {
+ CONSTRUCT,
+ MODIFY_SELECTION,
+ CHANGE_SELECTION,
+ SET_SELECTION,
+ LAST_SIGNAL
+};
+
+static void sp_widget_class_init (SPWidgetClass *klass);
+static void sp_widget_init (SPWidget *widget);
+
+static void sp_widget_destroy (GtkObject *object);
+
+static void sp_widget_show (GtkWidget *widget);
+static void sp_widget_hide (GtkWidget *widget);
+static gint sp_widget_expose (GtkWidget *widget, GdkEventExpose *event);
+static void sp_widget_size_request (GtkWidget *widget, GtkRequisition *requisition);
+static void sp_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
+
+static void sp_widget_modify_selection (Inkscape::Application *inkscape, Inkscape::Selection *selection, guint flags, SPWidget *spw);
+static void sp_widget_change_selection (Inkscape::Application *inkscape, Inkscape::Selection *selection, SPWidget *spw);
+static void sp_widget_set_selection (Inkscape::Application *inkscape, Inkscape::Selection *selection, SPWidget *spw);
+
+static GtkBinClass *parent_class;
+static guint signals[LAST_SIGNAL] = {0};
+
+GtkType
+sp_widget_get_type (void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ static const GtkTypeInfo info = {
+ "SPWidget",
+ sizeof (SPWidget),
+ sizeof (SPWidgetClass),
+ (GtkClassInitFunc) sp_widget_class_init,
+ (GtkObjectInitFunc) sp_widget_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_BIN, &info);
+ }
+ return type;
+}
+
+static void
+sp_widget_class_init (SPWidgetClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ parent_class = (GtkBinClass*)gtk_type_class (GTK_TYPE_BIN);
+
+ object_class->destroy = sp_widget_destroy;
+
+ signals[CONSTRUCT] = gtk_signal_new ("construct",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPWidgetClass, construct),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+ signals[CHANGE_SELECTION] = gtk_signal_new ("change_selection",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPWidgetClass, change_selection),
+ gtk_marshal_NONE__POINTER,
+ GTK_TYPE_NONE, 1,
+ GTK_TYPE_POINTER);
+ signals[MODIFY_SELECTION] = gtk_signal_new ("modify_selection",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPWidgetClass, modify_selection),
+ gtk_marshal_NONE__POINTER_UINT,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_POINTER, GTK_TYPE_UINT);
+ signals[SET_SELECTION] = gtk_signal_new ("set_selection",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE(object_class),
+ GTK_SIGNAL_OFFSET (SPWidgetClass, set_selection),
+ gtk_marshal_NONE__POINTER,
+ GTK_TYPE_NONE, 1,
+ GTK_TYPE_POINTER);
+
+ widget_class->show = sp_widget_show;
+ widget_class->hide = sp_widget_hide;
+ widget_class->expose_event = sp_widget_expose;
+ widget_class->size_request = sp_widget_size_request;
+ widget_class->size_allocate = sp_widget_size_allocate;
+}
+
+static void
+sp_widget_init (SPWidget *spw)
+{
+ spw->inkscape = NULL;
+}
+
+static void
+sp_widget_destroy (GtkObject *object)
+{
+ SPWidget *spw;
+
+ spw = (SPWidget *) object;
+
+ if (spw->inkscape) {
+ /* Disconnect signals */
+ // the checks are necessary because when destroy is caused by the the program shutting down,
+ // the inkscape object may already be (partly?) invalid --bb
+ if (G_IS_OBJECT(spw->inkscape) && G_OBJECT_GET_CLASS(G_OBJECT(spw->inkscape)))
+ sp_signal_disconnect_by_data (spw->inkscape, spw);
+ spw->inkscape = NULL;
+ }
+
+ if (((GtkObjectClass *) parent_class)->destroy)
+ (* ((GtkObjectClass *) parent_class)->destroy) (object);
+}
+
+static void
+sp_widget_show (GtkWidget *widget)
+{
+ SPWidget *spw;
+
+ spw = SP_WIDGET (widget);
+
+ if (spw->inkscape) {
+ /* Connect signals */
+ g_signal_connect (G_OBJECT (spw->inkscape), "modify_selection", G_CALLBACK (sp_widget_modify_selection), spw);
+ g_signal_connect (G_OBJECT (spw->inkscape), "change_selection", G_CALLBACK (sp_widget_change_selection), spw);
+ g_signal_connect (G_OBJECT (spw->inkscape), "set_selection", G_CALLBACK (sp_widget_set_selection), spw);
+ }
+
+ if (((GtkWidgetClass *) parent_class)->show)
+ (* ((GtkWidgetClass *) parent_class)->show) (widget);
+}
+
+static void
+sp_widget_hide (GtkWidget *widget)
+{
+ SPWidget *spw;
+
+ spw = SP_WIDGET (widget);
+
+ if (spw->inkscape) {
+ /* Disconnect signals */
+ sp_signal_disconnect_by_data (spw->inkscape, spw);
+ }
+
+ if (((GtkWidgetClass *) parent_class)->hide)
+ (* ((GtkWidgetClass *) parent_class)->hide) (widget);
+}
+
+static gint
+sp_widget_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+ GtkBin *bin;
+
+ bin = GTK_BIN (widget);
+
+ gtk_container_propagate_expose (GTK_CONTAINER(widget), bin->child, event);
+ /*
+ if ((bin->child) && (GTK_WIDGET_NO_WINDOW (bin->child))) {
+ GdkEventExpose ce;
+ ce = *event;
+ gtk_widget_event (bin->child, (GdkEvent *) &ce);
+ }
+ */
+
+ return FALSE;
+}
+
+static void
+sp_widget_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+ if (((GtkBin *) widget)->child)
+ gtk_widget_size_request (((GtkBin *) widget)->child, requisition);
+}
+
+static void
+sp_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ widget->allocation = *allocation;
+
+ if (((GtkBin *) widget)->child)
+ gtk_widget_size_allocate (((GtkBin *) widget)->child, allocation);
+}
+
+/* Methods */
+
+GtkWidget *
+sp_widget_new_global (Inkscape::Application *inkscape)
+{
+ SPWidget *spw;
+
+ spw = (SPWidget*)gtk_type_new (SP_TYPE_WIDGET);
+
+ if (!sp_widget_construct_global (spw, inkscape)) {
+ gtk_object_unref (GTK_OBJECT (spw));
+ return NULL;
+ }
+
+ return (GtkWidget *) spw;
+}
+
+GtkWidget *
+sp_widget_construct_global (SPWidget *spw, Inkscape::Application *inkscape)
+{
+ g_return_val_if_fail (!spw->inkscape, NULL);
+
+ spw->inkscape = inkscape;
+ if (GTK_WIDGET_VISIBLE (spw)) {
+ g_signal_connect (G_OBJECT (inkscape), "modify_selection", G_CALLBACK (sp_widget_modify_selection), spw);
+ g_signal_connect (G_OBJECT (inkscape), "change_selection", G_CALLBACK (sp_widget_change_selection), spw);
+ g_signal_connect (G_OBJECT (inkscape), "set_selection", G_CALLBACK (sp_widget_set_selection), spw);
+ }
+
+ g_signal_emit (G_OBJECT (spw), signals[CONSTRUCT], 0);
+
+ return (GtkWidget *) spw;
+}
+
+static void
+sp_widget_modify_selection (Inkscape::Application *inkscape, Inkscape::Selection *selection, guint flags, SPWidget *spw)
+{
+ g_signal_emit (G_OBJECT (spw), signals[MODIFY_SELECTION], 0, selection, flags);
+}
+
+static void
+sp_widget_change_selection (Inkscape::Application *inkscape, Inkscape::Selection *selection, SPWidget *spw)
+{
+ g_signal_emit (G_OBJECT (spw), signals[CHANGE_SELECTION], 0, selection);
+}
+
+static void
+sp_widget_set_selection (Inkscape::Application *inkscape, Inkscape::Selection *selection, SPWidget *spw)
+{
+ /* Emit "set_selection" signal */
+ g_signal_emit (G_OBJECT (spw), signals[SET_SELECTION], 0, selection);
+ /* Inkscape will force "change_selection" anyways */
+}
+
diff --git a/src/widgets/sp-widget.h b/src/widgets/sp-widget.h
new file mode 100644
index 000000000..e9f9cfe73
--- /dev/null
+++ b/src/widgets/sp-widget.h
@@ -0,0 +1,52 @@
+#ifndef __SP_WIDGET_H__
+#define __SP_WIDGET_H__
+
+/*
+ * Abstract base class for dynamic control widgets
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib.h>
+
+#define SP_TYPE_WIDGET (sp_widget_get_type ())
+#define SP_WIDGET(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_WIDGET, SPWidget))
+#define SP_WIDGET_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_WIDGET, SPWidgetClass))
+#define SP_IS_WIDGET(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_WIDGET))
+#define SP_IS_WIDGET_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_WIDGET))
+
+#include <gtk/gtkbin.h>
+
+namespace Inkscape {
+ class Application;
+ class Selection;
+}
+
+struct SPWidget {
+ GtkBin bin;
+ Inkscape::Application *inkscape;
+};
+
+struct SPWidgetClass {
+ GtkBinClass bin_class;
+ void (* construct) (SPWidget *spw);
+ /* Selection change handlers */
+ void (* modify_selection) (SPWidget *spw, Inkscape::Selection *selection, guint flags);
+ void (* change_selection) (SPWidget *spw, Inkscape::Selection *selection);
+ void (* set_selection) (SPWidget *spw, Inkscape::Selection *selection);
+};
+
+GtkType sp_widget_get_type (void);
+
+/* fixme: Think (Lauris) */
+/* Generic constructor for global widget */
+GtkWidget *sp_widget_new_global (Inkscape::Application *inkscape);
+GtkWidget *sp_widget_construct_global (SPWidget *spw, Inkscape::Application *inkscape);
+
+#endif
diff --git a/src/widgets/sp-xmlview-attr-list.cpp b/src/widgets/sp-xmlview-attr-list.cpp
new file mode 100644
index 000000000..c8dee1a42
--- /dev/null
+++ b/src/widgets/sp-xmlview-attr-list.cpp
@@ -0,0 +1,181 @@
+#define __SP_XMLVIEW_ATTR_LIST_C__
+
+/*
+ * Specialization of GtkCList for the XML tree view
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2002 MenTaLguY
+ *
+ * Released under the GNU GPL; see COPYING for details
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "helper/sp-marshal.h"
+#include <glibmm/i18n.h>
+#include "../xml/node-event-vector.h"
+#include "sp-xmlview-attr-list.h"
+
+static void sp_xmlview_attr_list_class_init (SPXMLViewAttrListClass * klass);
+static void sp_xmlview_attr_list_init (SPXMLViewAttrList * list);
+static void sp_xmlview_attr_list_destroy (GtkObject * object);
+
+static void event_attr_changed (Inkscape::XML::Node * repr, const gchar * name, const gchar * old_value, const gchar * new_value, bool is_interactive, gpointer data);
+
+static GtkCListClass * parent_class = NULL;
+
+static Inkscape::XML::NodeEventVector repr_events = {
+ NULL, /* child_added */
+ NULL, /* child_removed */
+ event_attr_changed,
+ NULL, /* content_changed */
+ NULL /* order_changed */
+};
+
+GtkWidget *
+sp_xmlview_attr_list_new (Inkscape::XML::Node * repr)
+{
+ SPXMLViewAttrList * list;
+
+ list = (SPXMLViewAttrList*)g_object_new (SP_TYPE_XMLVIEW_ATTR_LIST, "n_columns", 2, NULL);
+
+ gtk_clist_set_column_title (GTK_CLIST (list), 0, _("Attribute"));
+ gtk_clist_set_column_title (GTK_CLIST (list), 1, _("Value"));
+ gtk_clist_column_titles_show (GTK_CLIST (list));
+
+ gtk_clist_column_titles_passive (GTK_CLIST (list));
+ gtk_clist_set_column_auto_resize (GTK_CLIST (list), 0, TRUE);
+ gtk_clist_set_column_auto_resize (GTK_CLIST (list), 1, TRUE);
+ gtk_clist_set_sort_column (GTK_CLIST (list), 0);
+ gtk_clist_set_auto_sort (GTK_CLIST (list), TRUE);
+
+ sp_xmlview_attr_list_set_repr (list, repr);
+
+ return (GtkWidget *) list;
+}
+
+void
+sp_xmlview_attr_list_set_repr (SPXMLViewAttrList * list, Inkscape::XML::Node * repr)
+{
+ if ( repr == list->repr ) return;
+ gtk_clist_freeze (GTK_CLIST (list));
+ if (list->repr) {
+ gtk_clist_clear (GTK_CLIST (list));
+ sp_repr_remove_listener_by_data (list->repr, list);
+ Inkscape::GC::release(list->repr);
+ }
+ list->repr = repr;
+ if (repr) {
+ Inkscape::GC::anchor(repr);
+ sp_repr_add_listener (repr, &repr_events, list);
+ sp_repr_synthesize_events (repr, &repr_events, list);
+ }
+ gtk_clist_thaw (GTK_CLIST (list));
+}
+
+GtkType
+sp_xmlview_attr_list_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (!type) {
+ static const GtkTypeInfo info = {
+ "SPXMLViewAttrList",
+ sizeof (SPXMLViewAttrList),
+ sizeof (SPXMLViewAttrListClass),
+ (GtkClassInitFunc) sp_xmlview_attr_list_class_init,
+ (GtkObjectInitFunc) sp_xmlview_attr_list_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_CLIST, &info);
+ }
+
+ return type;
+}
+
+void
+sp_xmlview_attr_list_class_init (SPXMLViewAttrListClass * klass)
+{
+ GtkObjectClass * object_class;
+
+ object_class = (GtkObjectClass *) klass;
+ object_class->destroy = sp_xmlview_attr_list_destroy;
+
+ parent_class = (GtkCListClass*)gtk_type_class (GTK_TYPE_CLIST);
+
+ g_signal_new ( "row-value-changed",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SPXMLViewAttrListClass, row_changed),
+ NULL, NULL,
+ sp_marshal_NONE__UINT,
+ G_TYPE_NONE, 1,
+ G_TYPE_UINT);
+}
+
+void
+sp_xmlview_attr_list_init (SPXMLViewAttrList * list)
+{
+ list->repr = NULL;
+}
+
+void
+sp_xmlview_attr_list_destroy (GtkObject * object)
+{
+ SPXMLViewAttrList * list;
+
+ list = SP_XMLVIEW_ATTR_LIST (object);
+
+ sp_xmlview_attr_list_set_repr (list, NULL);
+
+ GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+void
+event_attr_changed (Inkscape::XML::Node * repr, const gchar * name, const gchar * old_value, const gchar * new_value, bool is_interactive, gpointer data)
+{
+ gint row;
+ SPXMLViewAttrList * list;
+ gchar new_text[128 + 4];
+ gchar *gtktext;
+
+ list = SP_XMLVIEW_ATTR_LIST (data);
+
+ gtk_clist_freeze (GTK_CLIST (list));
+
+ if (new_value) {
+ strncpy (new_text, new_value, 128);
+ if (strlen (new_value) >= 128) {
+ strcpy (new_text + 128, "...");
+ }
+ gtktext = new_text;
+ } else {
+ gtktext = NULL;
+ }
+
+ row = gtk_clist_find_row_from_data (GTK_CLIST (list), GINT_TO_POINTER (g_quark_from_string (name)));
+ if (row != -1) {
+ if (new_value) {
+ gtk_clist_set_text (GTK_CLIST (list), row, 1, gtktext);
+ } else {
+ gtk_clist_remove (GTK_CLIST (list), row);
+ }
+ } else if (new_value != NULL) {
+ const gchar * text[2];
+
+ text[0] = name;
+ text[1] = gtktext;
+
+ row = gtk_clist_append (GTK_CLIST (list), (gchar **)text);
+ gtk_clist_set_row_data (GTK_CLIST (list), row, GINT_TO_POINTER (g_quark_from_string (name)));
+ }
+
+ gtk_clist_thaw (GTK_CLIST (list));
+
+ // send a "changed" signal so widget owners will know I've updated
+ g_signal_emit_by_name(G_OBJECT (list), "row-value-changed", row );
+}
+
diff --git a/src/widgets/sp-xmlview-attr-list.h b/src/widgets/sp-xmlview-attr-list.h
new file mode 100644
index 000000000..7c6f00b20
--- /dev/null
+++ b/src/widgets/sp-xmlview-attr-list.h
@@ -0,0 +1,54 @@
+#ifndef __SP_XMLVIEW_ATTR_LIST_H__
+#define __SP_XMLVIEW_ATTR_LIST_H__
+
+/*
+ * Specialization of GtkCList for editing XML node attributes
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2002 MenTaLguY
+ *
+ * Released under the GNU GPL; see COPYING for details
+ */
+
+#include <stdio.h>
+#include <gtk/gtkclist.h>
+#include "../xml/repr.h"
+
+#include <glib.h>
+
+
+
+#define SP_TYPE_XMLVIEW_ATTR_LIST (sp_xmlview_attr_list_get_type ())
+#define SP_XMLVIEW_ATTR_LIST(o) (GTK_CHECK_CAST ((o), SP_TYPE_XMLVIEW_ATTR_LIST, SPXMLViewAttrList))
+#define SP_IS_XMLVIEW_ATTR_LIST(o) (GTK_CHECK_TYPE ((o), SP_TYPE_XMLVIEW_ATTR_LIST))
+#define SP_XMLVIEW_ATTR_LIST_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_XMLVIEW_ATTR_LIST))
+
+struct SPXMLViewAttrList
+{
+ GtkCList list;
+
+ Inkscape::XML::Node * repr;
+};
+
+struct SPXMLViewAttrListClass
+{
+ GtkCListClass parent_class;
+
+ void (* row_changed) (SPXMLViewAttrList *list, gint row);
+};
+
+GtkType sp_xmlview_attr_list_get_type (void);
+GtkWidget * sp_xmlview_attr_list_new (Inkscape::XML::Node * repr);
+
+#define SP_XMLVIEW_ATTR_LIST_GET_REPR(list) (SP_XMLVIEW_ATTR_LIST (list)->repr)
+
+#define sp_xmlview_attr_list_get_row_key(list, row) (GPOINTER_TO_INT (gtk_clist_get_row_data ((list), (row))))
+#define sp_xmlview_attr_list_find_row_from_key(list, key) (gtk_clist_find_row_from_data ((list), GINT_TO_POINTER ((key))))
+
+void sp_xmlview_attr_list_set_repr (SPXMLViewAttrList * list, Inkscape::XML::Node * repr);
+
+
+
+#endif
diff --git a/src/widgets/sp-xmlview-content.cpp b/src/widgets/sp-xmlview-content.cpp
new file mode 100644
index 000000000..0d8878436
--- /dev/null
+++ b/src/widgets/sp-xmlview-content.cpp
@@ -0,0 +1,165 @@
+#define __SP_XMLVIEW_CONTENT_C__
+
+/*
+ * Specialization of GtkTextView for the XML tree view
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2002 MenTaLguY
+ *
+ * Released under the GNU GPL; see COPYING for details
+ */
+
+
+#include "xml/node-event-vector.h"
+#include "sp-xmlview-content.h"
+#include "desktop-handles.h"
+#include "document-private.h"
+#include "inkscape.h"
+
+static void sp_xmlview_content_class_init (SPXMLViewContentClass * klass);
+static void sp_xmlview_content_init (SPXMLViewContent * text);
+static void sp_xmlview_content_destroy (GtkObject * object);
+
+void sp_xmlview_content_changed (GtkTextBuffer *tb, SPXMLViewContent *text);
+
+static void event_content_changed (Inkscape::XML::Node * repr, const gchar * old_content, const gchar * new_content, gpointer data);
+
+static GtkTextViewClass * parent_class = NULL;
+
+static Inkscape::XML::NodeEventVector repr_events = {
+ NULL, /* child_added */
+ NULL, /* child_removed */
+ NULL, /* attr_changed */
+ event_content_changed,
+ NULL /* order_changed */
+};
+
+GtkWidget *
+sp_xmlview_content_new (Inkscape::XML::Node * repr)
+{
+ GtkTextBuffer *tb;
+ SPXMLViewContent *text;
+
+ tb = gtk_text_buffer_new (NULL);
+ text = (SPXMLViewContent*)gtk_type_new (SP_TYPE_XMLVIEW_CONTENT);
+ gtk_text_view_set_buffer (GTK_TEXT_VIEW (text), tb);
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text), GTK_WRAP_CHAR);
+
+ g_signal_connect (G_OBJECT (tb), "changed", G_CALLBACK (sp_xmlview_content_changed), text);
+
+ /* should we alter the scrolling adjustments here? */
+
+ sp_xmlview_content_set_repr (text, repr);
+
+ return (GtkWidget *) text;
+}
+
+void
+sp_xmlview_content_set_repr (SPXMLViewContent * text, Inkscape::XML::Node * repr)
+{
+ if ( repr == text->repr ) return;
+ if (text->repr) {
+ sp_repr_remove_listener_by_data (text->repr, text);
+ Inkscape::GC::release(text->repr);
+ }
+ text->repr = repr;
+ if (repr) {
+ Inkscape::GC::anchor(repr);
+ sp_repr_add_listener (repr, &repr_events, text);
+ sp_repr_synthesize_events (repr, &repr_events, text);
+ } else {
+ gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)), "", 0);
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (text), FALSE);
+ }
+}
+
+GtkType
+sp_xmlview_content_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (!type) {
+ static const GtkTypeInfo info = {
+ "SPXMLViewContent",
+ sizeof (SPXMLViewContent),
+ sizeof (SPXMLViewContentClass),
+ (GtkClassInitFunc) sp_xmlview_content_class_init,
+ (GtkObjectInitFunc) sp_xmlview_content_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_TEXT_VIEW, &info);
+ }
+
+ return type;
+}
+
+void
+sp_xmlview_content_class_init (SPXMLViewContentClass * klass)
+{
+ GtkObjectClass * object_class;
+
+ object_class = (GtkObjectClass *) klass;
+
+ parent_class = (GtkTextViewClass*)gtk_type_class (GTK_TYPE_TEXT_VIEW);
+
+ object_class->destroy = sp_xmlview_content_destroy;
+}
+
+void
+sp_xmlview_content_init (SPXMLViewContent *text)
+{
+ text->repr = NULL;
+ text->blocked = FALSE;
+}
+
+void
+sp_xmlview_content_destroy (GtkObject * object)
+{
+ SPXMLViewContent * text;
+
+ text = SP_XMLVIEW_CONTENT (object);
+
+ sp_xmlview_content_set_repr (text, NULL);
+
+ GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+void
+event_content_changed (Inkscape::XML::Node * repr, const gchar * old_content, const gchar * new_content, gpointer data)
+{
+ SPXMLViewContent * text;
+ text = SP_XMLVIEW_CONTENT (data);
+
+ if (text->blocked) return;
+
+ text->blocked = TRUE;
+
+ if (new_content) {
+ gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)), new_content, strlen (new_content));
+ } else {
+ gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)), "", 0);
+ }
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (text), new_content != NULL);
+
+ text->blocked = FALSE;
+}
+
+void
+sp_xmlview_content_changed (GtkTextBuffer *tb, SPXMLViewContent *text)
+{
+ if (text->blocked) return;
+
+ if (text->repr) {
+ GtkTextIter start, end;
+ gchar *data;
+ text->blocked = TRUE;
+ gtk_text_buffer_get_bounds (tb, &start, &end);
+ data = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
+ text->repr->setContent(data);
+ g_free (data);
+ text->blocked = FALSE;
+ sp_document_done (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP));
+ }
+}
diff --git a/src/widgets/sp-xmlview-content.h b/src/widgets/sp-xmlview-content.h
new file mode 100644
index 000000000..3077b2251
--- /dev/null
+++ b/src/widgets/sp-xmlview-content.h
@@ -0,0 +1,53 @@
+#ifndef __SP_XMLVIEW_CONTENT_H__
+#define __SP_XMLVIEW_CONTENT_H__
+
+/*
+ * Specialization of GtkTextView for editing XML node text
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2002 MenTaLguY
+ *
+ * Released under the GNU GPL; see COPYING for details
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include <gtk/gtktextview.h>
+#include "../xml/repr.h"
+
+#include <glib.h>
+
+
+
+#define SP_TYPE_XMLVIEW_CONTENT (sp_xmlview_content_get_type ())
+#define SP_XMLVIEW_CONTENT(o) (GTK_CHECK_CAST ((o), SP_TYPE_XMLVIEW_CONTENT, SPXMLViewContent))
+#define SP_IS_XMLVIEW_CONTENT(o) (GTK_CHECK_TYPE ((o), SP_TYPE_XMLVIEW_CONTENT))
+#define SP_XMLVIEW_CONTENT_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_XMLVIEW_CONTENT))
+
+struct SPXMLViewContent
+{
+ GtkTextView textview;
+
+ Inkscape::XML::Node * repr;
+ gint blocked;
+};
+
+struct SPXMLViewContentClass
+{
+ GtkTextViewClass parent_class;
+};
+
+GtkType sp_xmlview_content_get_type (void);
+GtkWidget * sp_xmlview_content_new (Inkscape::XML::Node * repr);
+
+#define SP_XMLVIEW_CONTENT_GET_REPR(text) (SP_XMLVIEW_CONTENT (text)->repr)
+
+void sp_xmlview_content_set_repr (SPXMLViewContent * text, Inkscape::XML::Node * repr);
+
+
+
+#endif
diff --git a/src/widgets/sp-xmlview-tree.cpp b/src/widgets/sp-xmlview-tree.cpp
new file mode 100644
index 000000000..d6fd92d78
--- /dev/null
+++ b/src/widgets/sp-xmlview-tree.cpp
@@ -0,0 +1,424 @@
+#define __SP_XMLVIEW_TREE_C__
+
+/*
+ * Specialization of GtkCTree for the XML tree view
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2002 MenTaLguY
+ *
+ * Released under the GNU GPL; see COPYING for details
+ */
+
+#include "../xml/node-event-vector.h"
+#include "sp-xmlview-tree.h"
+
+struct NodeData {
+ SPXMLViewTree * tree;
+ GtkCTreeNode * node;
+ Inkscape::XML::Node * repr;
+};
+
+#define NODE_DATA(node) ((NodeData *)(GTK_CTREE_ROW ((node))->row.data))
+
+static void sp_xmlview_tree_class_init (SPXMLViewTreeClass * klass);
+static void sp_xmlview_tree_init (SPXMLViewTree * tree);
+static void sp_xmlview_tree_destroy (GtkObject * object);
+
+static NodeData * node_data_new (SPXMLViewTree * tree, GtkCTreeNode * node, Inkscape::XML::Node * repr);
+static void node_data_free (gpointer data);
+
+static GtkCTreeNode * add_node (SPXMLViewTree * tree, GtkCTreeNode * parent, GtkCTreeNode * before, Inkscape::XML::Node * repr);
+
+static void element_child_added (Inkscape::XML::Node * repr, Inkscape::XML::Node * child, Inkscape::XML::Node * ref, gpointer data);
+static void element_attr_changed (Inkscape::XML::Node * repr, const gchar * key, const gchar * old_value, const gchar * new_value, bool is_interactive, gpointer data);
+static void element_child_removed (Inkscape::XML::Node * repr, Inkscape::XML::Node * child, Inkscape::XML::Node * ref, gpointer data);
+static void element_order_changed (Inkscape::XML::Node * repr, Inkscape::XML::Node * child, Inkscape::XML::Node * oldref, Inkscape::XML::Node * newref, gpointer data);
+
+static void text_content_changed (Inkscape::XML::Node * repr, const gchar * old_content, const gchar * new_content, gpointer data);
+static void comment_content_changed (Inkscape::XML::Node * repr, const gchar * old_content, const gchar * new_content, gpointer data);
+
+static void tree_move (GtkCTree * tree, GtkCTreeNode * node, GtkCTreeNode * new_parent, GtkCTreeNode * new_sibling);
+
+static gboolean check_drag (GtkCTree * tree, GtkCTreeNode * node, GtkCTreeNode * new_parent, GtkCTreeNode * new_sibling);
+
+static GtkCTreeNode * ref_to_sibling (GtkCTreeNode * parent, Inkscape::XML::Node * ref);
+static GtkCTreeNode * repr_to_child (GtkCTreeNode * parent, Inkscape::XML::Node * repr);
+static Inkscape::XML::Node * sibling_to_ref (GtkCTreeNode * parent, GtkCTreeNode * sibling);
+
+static gint match_node_data_by_repr(gconstpointer data_p, gconstpointer repr);
+
+static const Inkscape::XML::NodeEventVector element_repr_events = {
+ element_child_added,
+ element_child_removed,
+ element_attr_changed,
+ NULL, /* content_changed */
+ element_order_changed
+};
+
+static const Inkscape::XML::NodeEventVector text_repr_events = {
+ NULL, /* child_added */
+ NULL, /* child_removed */
+ NULL, /* attr_changed */
+ text_content_changed,
+ NULL /* order_changed */
+};
+
+static const Inkscape::XML::NodeEventVector comment_repr_events = {
+ NULL, /* child_added */
+ NULL, /* child_removed */
+ NULL, /* attr_changed */
+ comment_content_changed,
+ NULL /* order_changed */
+};
+
+static GtkCTreeClass * parent_class = NULL;
+
+GtkWidget *
+sp_xmlview_tree_new (Inkscape::XML::Node * repr, void * factory, void * data)
+{
+ SPXMLViewTree * tree;
+
+ tree = (SPXMLViewTree*)g_object_new (SP_TYPE_XMLVIEW_TREE, "n_columns", 1, "tree_column", 0, NULL);
+
+ gtk_clist_column_titles_hide (GTK_CLIST (tree));
+ gtk_ctree_set_line_style (GTK_CTREE (tree), GTK_CTREE_LINES_NONE);
+ gtk_ctree_set_expander_style (GTK_CTREE (tree), GTK_CTREE_EXPANDER_TRIANGLE);
+ gtk_clist_set_column_auto_resize (GTK_CLIST (tree), 0, TRUE);
+ gtk_clist_set_reorderable (GTK_CLIST (tree), TRUE);
+ gtk_ctree_set_drag_compare_func (GTK_CTREE (tree), check_drag);
+
+ sp_xmlview_tree_set_repr (tree, repr);
+
+ return (GtkWidget *) tree;
+}
+
+void
+sp_xmlview_tree_set_repr (SPXMLViewTree * tree, Inkscape::XML::Node * repr)
+{
+ if ( tree->repr == repr ) return;
+ gtk_clist_freeze (GTK_CLIST (tree));
+ if (tree->repr) {
+ gtk_clist_clear (GTK_CLIST (tree));
+ Inkscape::GC::release(tree->repr);
+ }
+ tree->repr = repr;
+ if (repr) {
+ GtkCTreeNode * node;
+ Inkscape::GC::anchor(repr);
+ node = add_node (tree, NULL, NULL, repr);
+ gtk_ctree_expand (GTK_CTREE (tree), node);
+ }
+ gtk_clist_thaw (GTK_CLIST (tree));
+}
+
+GtkType
+sp_xmlview_tree_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (!type) {
+ static const GtkTypeInfo info = {
+ "SPXMLViewTree",
+ sizeof (SPXMLViewTree),
+ sizeof (SPXMLViewTreeClass),
+ (GtkClassInitFunc) sp_xmlview_tree_class_init,
+ (GtkObjectInitFunc) sp_xmlview_tree_init,
+ NULL, NULL, NULL
+ };
+ type = gtk_type_unique (GTK_TYPE_CTREE, &info);
+ }
+
+ return type;
+}
+
+void
+sp_xmlview_tree_class_init (SPXMLViewTreeClass * klass)
+{
+ GtkObjectClass * object_class;
+
+ object_class = (GtkObjectClass *) klass;
+ parent_class = (GtkCTreeClass *) gtk_type_class (GTK_TYPE_CTREE);
+
+ GTK_CTREE_CLASS (object_class)->tree_move = tree_move;
+
+ object_class->destroy = sp_xmlview_tree_destroy;
+}
+
+void
+sp_xmlview_tree_init (SPXMLViewTree * tree)
+{
+ tree->repr = NULL;
+ tree->blocked = 0;
+}
+
+void
+sp_xmlview_tree_destroy (GtkObject * object)
+{
+ SPXMLViewTree * tree;
+
+ tree = SP_XMLVIEW_TREE (object);
+
+ sp_xmlview_tree_set_repr (tree, NULL);
+
+ GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+GtkCTreeNode *
+add_node (SPXMLViewTree * tree, GtkCTreeNode * parent, GtkCTreeNode * before, Inkscape::XML::Node * repr)
+{
+ NodeData * data;
+ GtkCTreeNode * node;
+ const Inkscape::XML::NodeEventVector * vec;
+ static const gchar *default_text[] = { "???" };
+
+ g_assert (tree != NULL);
+ g_assert (repr != NULL);
+
+ node = gtk_ctree_insert_node (GTK_CTREE (tree), parent, before, (gchar **)default_text, 2, NULL, NULL, NULL, NULL, ( repr->type() != Inkscape::XML::ELEMENT_NODE ), FALSE);
+ g_assert (node != NULL);
+
+ data = node_data_new (tree, node, repr);
+ g_assert (data != NULL);
+
+ gtk_ctree_node_set_row_data_full (GTK_CTREE (tree), data->node, data, node_data_free);
+
+ if ( repr->type() == Inkscape::XML::TEXT_NODE ) {
+ vec = &text_repr_events;
+ } else if ( repr->type() == Inkscape::XML::COMMENT_NODE ) {
+ vec = &comment_repr_events;
+ } else if ( repr->type() == Inkscape::XML::ELEMENT_NODE ) {
+ vec = &element_repr_events;
+ } else {
+ vec = NULL;
+ }
+
+ if (vec) {
+ gtk_clist_freeze (GTK_CLIST (tree));
+ /* cheat a little to get the id upated properly */
+ if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
+ element_attr_changed (repr, "id", NULL, NULL, false, data);
+ }
+ sp_repr_add_listener (repr, vec, data);
+ sp_repr_synthesize_events (repr, vec, data);
+ gtk_clist_thaw (GTK_CLIST (tree));
+ }
+
+ return node;
+}
+
+NodeData *
+node_data_new (SPXMLViewTree * tree, GtkCTreeNode * node, Inkscape::XML::Node * repr)
+{
+ NodeData * data;
+ data = g_new (NodeData, 1);
+ data->tree = tree;
+ data->node = node;
+ data->repr = repr;
+ Inkscape::GC::anchor(repr);
+ return data;
+}
+
+void
+node_data_free (gpointer ptr) {
+ NodeData * data;
+ data = (NodeData *) ptr;
+ sp_repr_remove_listener_by_data (data->repr, data);
+ g_assert (data->repr != NULL);
+ Inkscape::GC::release(data->repr);
+ g_free (data);
+}
+
+void
+element_child_added (Inkscape::XML::Node * repr, Inkscape::XML::Node * child, Inkscape::XML::Node * ref, gpointer ptr)
+{
+ NodeData * data;
+ GtkCTreeNode * before;
+
+ data = (NodeData *) ptr;
+
+ if (data->tree->blocked) return;
+
+ before = ref_to_sibling (data->node, ref);
+
+ add_node (data->tree, data->node, before, child);
+}
+
+void
+element_attr_changed (Inkscape::XML::Node * repr, const gchar * key, const gchar * old_value, const gchar * new_value, bool is_interactive, gpointer ptr)
+{
+ NodeData * data;
+ gchar *label;
+
+ data = (NodeData *) ptr;
+
+ if (data->tree->blocked) return;
+
+ if (strcmp (key, "id")) return;
+
+ if (new_value) {
+ label = g_strdup_printf ("<%s id=\"%s\">", repr->name(), new_value);
+ } else {
+ label = g_strdup_printf ("<%s>", repr->name());
+ }
+ gtk_ctree_node_set_text (GTK_CTREE (data->tree), data->node, 0, label);
+ g_free (label);
+}
+
+void
+element_child_removed (Inkscape::XML::Node * repr, Inkscape::XML::Node * child, Inkscape::XML::Node * ref, gpointer ptr)
+{
+ NodeData * data;
+
+ data = (NodeData *) ptr;
+
+ if (data->tree->blocked) return;
+
+ gtk_ctree_remove_node (GTK_CTREE (data->tree), repr_to_child (data->node, child));
+}
+
+void
+element_order_changed (Inkscape::XML::Node * repr, Inkscape::XML::Node * child, Inkscape::XML::Node * oldref, Inkscape::XML::Node * newref, gpointer ptr)
+{
+ NodeData * data;
+ GtkCTreeNode * before, * node;
+
+ data = (NodeData *) ptr;
+
+ if (data->tree->blocked) return;
+
+ before = ref_to_sibling (data->node, newref);
+ node = repr_to_child (data->node, child);
+
+ if ( before == node ) before = GTK_CTREE_ROW (before)->sibling;
+
+ parent_class->tree_move (GTK_CTREE (data->tree), node, data->node, before);
+}
+
+void
+text_content_changed (Inkscape::XML::Node * repr, const gchar * old_content, const gchar * new_content, gpointer ptr)
+{
+ NodeData *data;
+ gchar *label;
+
+ data = (NodeData *) ptr;
+
+ if (data->tree->blocked) return;
+
+ label = g_strdup_printf ("\"%s\"", new_content);
+ gtk_ctree_node_set_text (GTK_CTREE (data->tree), data->node, 0, label);
+ g_free (label);
+}
+
+void
+comment_content_changed (Inkscape::XML::Node *repr, const gchar * old_content, const gchar *new_content, gpointer ptr)
+{
+ NodeData *data;
+ gchar *label;
+
+ data = (NodeData *) ptr;
+
+ if (data->tree->blocked) return;
+
+ label = g_strdup_printf ("<!--%s-->", new_content);
+ gtk_ctree_node_set_text (GTK_CTREE (data->tree), data->node, 0, label);
+ g_free (label);
+}
+
+void
+tree_move (GtkCTree * tree, GtkCTreeNode * node, GtkCTreeNode * new_parent, GtkCTreeNode * new_sibling)
+{
+ GtkCTreeNode * old_parent;
+ Inkscape::XML::Node * ref;
+
+ old_parent = GTK_CTREE_ROW (node)->parent;
+ if ( !old_parent || !new_parent ) return;
+
+ ref = sibling_to_ref (new_parent, new_sibling);
+
+ gtk_clist_freeze (GTK_CLIST (tree));
+
+ SP_XMLVIEW_TREE (tree)->blocked++;
+ if (new_parent == old_parent) {
+ NODE_DATA (old_parent)->repr->changeOrder(NODE_DATA (node)->repr, ref);
+ } else {
+ NODE_DATA (old_parent)->repr->removeChild(NODE_DATA (node)->repr);
+ NODE_DATA (new_parent)->repr->addChild(NODE_DATA (node)->repr, ref);
+ }
+ SP_XMLVIEW_TREE (tree)->blocked--;
+
+ parent_class->tree_move (tree, node, new_parent, new_sibling);
+
+ gtk_clist_thaw (GTK_CLIST (tree));
+}
+
+GtkCTreeNode *
+ref_to_sibling (GtkCTreeNode * parent, Inkscape::XML::Node * ref)
+{
+ if (ref) {
+ GtkCTreeNode * before;
+ before = repr_to_child (parent, ref);
+ g_assert (before != NULL);
+ before = GTK_CTREE_ROW (before)->sibling;
+ return before;
+ } else {
+ return GTK_CTREE_ROW (parent)->children;
+ }
+}
+
+GtkCTreeNode *
+repr_to_child (GtkCTreeNode * parent, Inkscape::XML::Node * repr)
+{
+ GtkCTreeNode * child;
+ child = GTK_CTREE_ROW (parent)->children;
+ while ( child && NODE_DATA (child)->repr != repr ) {
+ child = GTK_CTREE_ROW (child)->sibling;
+ }
+ return child;
+}
+
+Inkscape::XML::Node *
+sibling_to_ref (GtkCTreeNode * parent, GtkCTreeNode * sibling)
+{
+ GtkCTreeNode * child;
+ child = GTK_CTREE_ROW (parent)->children;
+ if ( child == sibling ) return NULL;
+ while ( child && GTK_CTREE_ROW (child)->sibling != sibling ) {
+ child = GTK_CTREE_ROW (child)->sibling;
+ }
+ return NODE_DATA (child)->repr;
+}
+
+gboolean
+check_drag (GtkCTree * tree, GtkCTreeNode * node, GtkCTreeNode * new_parent, GtkCTreeNode * new_sibling)
+{
+ GtkCTreeNode * old_parent;
+
+ old_parent = GTK_CTREE_ROW (node)->parent;
+
+ if (!old_parent || !new_parent) return FALSE;
+ if (NODE_DATA (new_parent)->repr->type() != Inkscape::XML::ELEMENT_NODE) return FALSE;
+
+ /* fixme: we need add_child/remove_child/etc repr events without side-effects, so we can check here and give better visual feedback */
+
+ return TRUE;
+}
+
+Inkscape::XML::Node *
+sp_xmlview_tree_node_get_repr (SPXMLViewTree * tree, GtkCTreeNode * node)
+{
+ return NODE_DATA (node)->repr;
+}
+
+GtkCTreeNode *
+sp_xmlview_tree_get_repr_node (SPXMLViewTree * tree, Inkscape::XML::Node * repr)
+{
+ return gtk_ctree_find_by_row_data_custom (GTK_CTREE (tree), NULL, repr, match_node_data_by_repr);
+}
+
+gint
+match_node_data_by_repr(gconstpointer data_p, gconstpointer repr)
+{
+ return ((const NodeData *)data_p)->repr != (const Inkscape::XML::Node *)repr;
+}
diff --git a/src/widgets/sp-xmlview-tree.h b/src/widgets/sp-xmlview-tree.h
new file mode 100644
index 000000000..a5dadbb61
--- /dev/null
+++ b/src/widgets/sp-xmlview-tree.h
@@ -0,0 +1,55 @@
+#ifndef __SP_XMLVIEW_TREE_H__
+#define __SP_XMLVIEW_TREE_H__
+
+/*
+ * Specialization of GtkCTree for the XML editor
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2002 MenTaLguY
+ *
+ * Released under the GNU GPL; see COPYING for details
+ */
+
+#include <gtk/gtkctree.h>
+#include "../xml/repr.h"
+
+#include <glib.h>
+
+
+
+#define SP_TYPE_XMLVIEW_TREE (sp_xmlview_tree_get_type ())
+#define SP_XMLVIEW_TREE(o) (GTK_CHECK_CAST ((o), SP_TYPE_XMLVIEW_TREE, SPXMLViewTree))
+#define SP_IS_XMLVIEW_TREE(o) (GTK_CHECK_TYPE ((o), SP_TYPE_XMLVIEW_TREE))
+#define SP_XMLVIEW_TREE_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_XMLVIEW_TREE))
+
+struct SPXMLViewTree;
+struct SPXMLViewTreeClass;
+
+struct SPXMLViewTree
+{
+ GtkCTree tree;
+
+ Inkscape::XML::Node * repr;
+ gint blocked;
+};
+
+struct SPXMLViewTreeClass
+{
+ GtkCTreeClass parent_class;
+};
+
+GtkType sp_xmlview_tree_get_type (void);
+GtkWidget * sp_xmlview_tree_new (Inkscape::XML::Node * repr, void * factory, void * data);
+
+#define SP_XMLVIEW_TREE_REPR(tree) (SP_XMLVIEW_TREE (tree)->repr)
+
+void sp_xmlview_tree_set_repr (SPXMLViewTree * tree, Inkscape::XML::Node * repr);
+
+Inkscape::XML::Node * sp_xmlview_tree_node_get_repr (SPXMLViewTree * tree, GtkCTreeNode * node);
+GtkCTreeNode * sp_xmlview_tree_get_repr_node (SPXMLViewTree * tree, Inkscape::XML::Node * repr);
+
+
+
+#endif
diff --git a/src/widgets/spinbutton-events.cpp b/src/widgets/spinbutton-events.cpp
new file mode 100644
index 000000000..14ac51422
--- /dev/null
+++ b/src/widgets/spinbutton-events.cpp
@@ -0,0 +1,137 @@
+/*
+ * Common callbacks for spinbuttons
+ *
+ * Authors:
+ * bulia byak <bulia@users.sourceforge.net>
+ *
+ * Copyright (C) 2003 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "../event-context.h"
+
+#include "sp-widget.h"
+#include "widget-sizes.h"
+
+
+gboolean
+spinbutton_focus_in (GtkWidget *w, GdkEventKey *event, gpointer data)
+{
+ gdouble *ini;
+
+ ini = (gdouble *) gtk_object_get_data (GTK_OBJECT (w), "ini");
+ if (ini) g_free (ini); // free the old value if any
+
+ // retrieve the value
+ ini = g_new (gdouble, 1);
+ *ini = gtk_spin_button_get_value (GTK_SPIN_BUTTON(w));
+
+ // remember it
+ gtk_object_set_data (GTK_OBJECT (w), "ini", ini);
+
+ return FALSE; // I didn't consume the event
+}
+
+void
+spinbutton_undo (GtkWidget *w)
+{
+ gdouble *ini = (gdouble *) gtk_object_get_data (GTK_OBJECT (w), "ini");
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), *ini);
+}
+
+void
+spinbutton_defocus (GtkObject *container)
+{
+ // defocus spinbuttons by moving focus to the canvas, unless "stay" is on
+ gboolean stay = GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (container), "stay"));
+ if (stay) {
+ gtk_object_set_data (GTK_OBJECT (container), "stay", GINT_TO_POINTER (FALSE));
+ } else {
+ GtkWidget *canvas = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (container), "dtw");
+ if (canvas) {
+ gtk_widget_grab_focus (GTK_WIDGET(canvas));
+ }
+ }
+}
+
+gboolean
+spinbutton_keypress (GtkWidget *w, GdkEventKey *event, gpointer data)
+{
+ SPWidget *spw = (SPWidget *) data;
+ gdouble v;
+
+ switch (get_group0_keyval (event)) {
+ case GDK_Escape: // defocus
+ spinbutton_undo (w);
+ spinbutton_defocus (GTK_OBJECT (spw));
+ return TRUE; // I consumed the event
+ break;
+ case GDK_Return: // defocus
+ case GDK_KP_Enter:
+ spinbutton_defocus (GTK_OBJECT (spw));
+ return TRUE; // I consumed the event
+ break;
+ case GDK_Tab:
+ case GDK_ISO_Left_Tab:
+ // set the flag meaning "do not leave toolbar when changing value"
+ gtk_object_set_data (GTK_OBJECT (spw), "stay", GINT_TO_POINTER(TRUE));
+ return FALSE; // I didn't consume the event
+ break;
+
+ // The following keys are processed manually because GTK implements them in strange ways
+ // (increments start with double step value and seem to grow as you press the key continuously)
+
+ case GDK_Up:
+ case GDK_KP_Up:
+ gtk_object_set_data (GTK_OBJECT (spw), "stay", GINT_TO_POINTER(TRUE));
+ v = gtk_spin_button_get_value(GTK_SPIN_BUTTON (w));
+ v += SPIN_STEP;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), v);
+ return TRUE; // I consumed the event
+ break;
+ case GDK_Down:
+ case GDK_KP_Down:
+ gtk_object_set_data (GTK_OBJECT (spw), "stay", GINT_TO_POINTER(TRUE));
+ v = gtk_spin_button_get_value(GTK_SPIN_BUTTON (w));
+ v -= SPIN_STEP;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), v);
+ return TRUE; // I consumed the event
+ break;
+ case GDK_Page_Up:
+ case GDK_KP_Page_Up:
+ gtk_object_set_data (GTK_OBJECT (spw), "stay", GINT_TO_POINTER(TRUE));
+ v = gtk_spin_button_get_value(GTK_SPIN_BUTTON (w));
+ v += SPIN_PAGE_STEP;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), v);
+ return TRUE; // I consumed the event
+ break;
+ case GDK_Page_Down:
+ case GDK_KP_Page_Down:
+ gtk_object_set_data (GTK_OBJECT (spw), "stay", GINT_TO_POINTER(TRUE));
+ v = gtk_spin_button_get_value(GTK_SPIN_BUTTON (w));
+ v -= SPIN_PAGE_STEP;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), v);
+ return TRUE; // I consumed the event
+ break;
+ case GDK_z:
+ case GDK_Z:
+ gtk_object_set_data (GTK_OBJECT (spw), "stay", GINT_TO_POINTER(TRUE));
+ if (event->state & GDK_CONTROL_MASK) {
+ spinbutton_undo (w);
+ return TRUE; // I consumed the event
+ }
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+ return FALSE; // I didn't consume the event
+}
diff --git a/src/widgets/spinbutton-events.h b/src/widgets/spinbutton-events.h
new file mode 100644
index 000000000..868bc195a
--- /dev/null
+++ b/src/widgets/spinbutton-events.h
@@ -0,0 +1,30 @@
+/*
+ * Common callbacks for spinbuttons
+ *
+ * Authors:
+ * bulia byak <bulia@users.sourceforge.net>
+ *
+ * Copyright (C) 2003 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib/gtypes.h>
+#include <gtk/gtkstyle.h> /* GtkWidget */
+#include <gtk/gtktypeutils.h> /* GtkObject */
+
+gboolean spinbutton_focus_in (GtkWidget *w, GdkEventKey *event, gpointer data);
+void spinbutton_undo (GtkWidget *w);
+gboolean spinbutton_keypress (GtkWidget *w, GdkEventKey *event, gpointer data);
+void spinbutton_defocus (GtkObject *container);
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/spw-utilities.cpp b/src/widgets/spw-utilities.cpp
new file mode 100644
index 000000000..c3bcefa49
--- /dev/null
+++ b/src/widgets/spw-utilities.cpp
@@ -0,0 +1,255 @@
+#define __SPW_UTILITIES_C__
+
+/*
+ * Inkscape Widget Utilities
+ *
+ * Authors:
+ * Bryce W. Harrington <brycehar@bryceharrington.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 2003 Bryce W. Harrington
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+
+#include <gtk/gtk.h>
+
+#include "selection.h"
+
+#include "helper/unit-menu.h"
+
+/**
+ * Creates a label widget with the given text, at the given col, row
+ * position in the table.
+ */
+GtkWidget *
+spw_label(GtkWidget * table, const gchar *label_text, int col, int row)
+{
+ GtkWidget *label_widget;
+
+ label_widget = gtk_label_new (label_text);
+ g_assert(label_widget != NULL);
+ gtk_misc_set_alignment (GTK_MISC (label_widget), 1.0, 0.5);
+ gtk_widget_show (label_widget);
+ gtk_table_attach (GTK_TABLE (table), label_widget, col, col+1, row, row+1,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)0, 4, 0);
+ return label_widget;
+}
+
+/**
+ * Creates a horizontal layout manager with 4-pixel spacing between children
+ * and space for 'width' columns.
+ */
+GtkWidget *
+spw_hbox(GtkWidget * table, int width, int col, int row)
+{
+ GtkWidget *hb;
+ /* Create a new hbox with a 4-pixel spacing between children */
+ hb = gtk_hbox_new (FALSE, 4);
+ g_assert(hb != NULL);
+ gtk_widget_show (hb);
+ gtk_table_attach (GTK_TABLE (table), hb, col, col+width, row, row+1,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)0, 0, 0);
+ return hb;
+}
+
+/**
+ * Creates a checkbutton widget and adds it to a vbox.
+ * This is a compound widget that includes a label.
+ */
+GtkWidget *spw_vbox_checkbutton(GtkWidget *dialog, GtkWidget *vbox,
+ const gchar *label, const gchar *tip, gchar *key, GCallback cb)
+{
+ g_assert (dialog != NULL);
+ g_assert (vbox != NULL);
+
+ GtkTooltips *tt = gtk_tooltips_new ();
+
+ GtkWidget *b = gtk_check_button_new_with_label (label);
+ gtk_tooltips_set_tip(tt, b, tip, NULL);
+ g_assert (b != NULL);
+ gtk_widget_show (b);
+ gtk_box_pack_start (GTK_BOX (vbox), b, FALSE, FALSE, 0);
+ gtk_object_set_data (GTK_OBJECT (b), "key", key);
+ gtk_object_set_data (GTK_OBJECT (dialog), key, b);
+ g_signal_connect (G_OBJECT (b), "toggled", cb, dialog);
+ return b;
+}
+
+
+/**
+ * Creates a checkbutton widget and adds it to a table.
+ * This is a compound widget that includes a label.
+ */
+GtkWidget *
+spw_checkbutton(GtkWidget * dialog, GtkWidget * table,
+ const gchar * label, gchar * key, int col, int row,
+ int insensitive, GCallback cb)
+{
+ GtkWidget *b;
+
+ g_assert(dialog != NULL);
+ g_assert(table != NULL);
+
+ GtkWidget *l = gtk_label_new (label);
+ gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
+ gtk_widget_show (l);
+ gtk_table_attach (GTK_TABLE (table), l, 0, 1, row, row+1,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)0, 0, 0);
+
+ b = gtk_check_button_new ();
+ gtk_widget_show (b);
+ gtk_table_attach (GTK_TABLE (table), b, 1, 2, row, row+1,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)0, 0, 0);
+ gtk_object_set_data (GTK_OBJECT (b), "key", key);
+ gtk_object_set_data (GTK_OBJECT (dialog), key, b);
+ g_signal_connect (G_OBJECT (b), "toggled", cb, dialog);
+ if (insensitive == 1) {
+ gtk_widget_set_sensitive (b, FALSE);
+ }
+ return b;
+}
+
+/**
+ * Creates a dropdown widget. This is a compound widget that includes
+ * a label as well as the dropdown.
+ */
+GtkWidget *
+spw_dropdown(GtkWidget * dialog, GtkWidget * table,
+ const gchar * label_text, gchar * key, int row,
+ GtkWidget * selector
+ )
+{
+ g_assert(dialog != NULL);
+ g_assert(table != NULL);
+ g_assert(selector != NULL);
+
+ spw_label(table, label_text, 0, row);
+
+ gtk_widget_show (selector);
+ gtk_table_attach (GTK_TABLE (table), selector, 1, 2, row, row+1,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)0, 0, 0);
+ gtk_object_set_data (GTK_OBJECT (dialog), key, selector);
+ return selector;
+}
+
+/**
+ * Creates a unit selector widget, used for selecting whether one wishes
+ * to measure screen elements in millimeters, points, etc. This is a
+ * compound unit that includes a label as well as the dropdown selector.
+ */
+GtkWidget *
+spw_unit_selector(GtkWidget * dialog, GtkWidget * table,
+ const gchar * label_text, gchar * key, int row,
+ GtkWidget * us, GCallback cb, bool can_be_negative)
+{
+ GtkWidget * sb;
+ GtkObject * a;
+
+ g_assert(dialog != NULL);
+ g_assert(table != NULL);
+ g_assert(us != NULL);
+
+ spw_label(table, label_text, 0, row);
+
+ a = gtk_adjustment_new (0.0, can_be_negative?-1e6:0, 1e6, 1.0, 10.0, 10.0);
+ g_assert(a != NULL);
+ gtk_object_set_data (GTK_OBJECT (a), "key", key);
+ gtk_object_set_data (GTK_OBJECT (a), "unit_selector", us);
+ gtk_object_set_data (GTK_OBJECT (dialog), key, a);
+ sp_unit_selector_add_adjustment (SP_UNIT_SELECTOR (us), GTK_ADJUSTMENT (a));
+ sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, 4);
+ g_assert(sb != NULL);
+ gtk_widget_show (sb);
+ gtk_table_attach (GTK_TABLE (table), sb, 1, 2, row, row+1,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)0, 0, 0);
+ g_signal_connect (G_OBJECT (a), "value_changed", cb, dialog);
+ return sb;
+}
+
+void
+sp_set_font_size_recursive (GtkWidget *w, gpointer font)
+{
+ guint size = GPOINTER_TO_UINT (font);
+
+ PangoFontDescription* pan = pango_font_description_new ();
+ pango_font_description_set_size (pan, size);
+
+ gtk_widget_modify_font (w, pan);
+
+ if (GTK_IS_CONTAINER(w)) {
+ gtk_container_foreach (GTK_CONTAINER(w), (GtkCallback) sp_set_font_size_recursive, font);
+ }
+
+ pango_font_description_free (pan);
+}
+
+void
+sp_set_font_size (GtkWidget *w, guint font)
+{
+ sp_set_font_size_recursive (w, GUINT_TO_POINTER(font));
+}
+
+void
+sp_set_font_size_smaller (GtkWidget *w)
+{
+ PangoContext *pc = gtk_widget_get_pango_context (w);
+ PangoFontDescription* pfd = pango_context_get_font_description (pc);
+ guint size = pango_font_description_get_size (pfd);
+ sp_set_font_size_recursive (w, GUINT_TO_POINTER((int) (0.8*size)));
+}
+
+/**
+\brief Finds the descendant of w which has the data with the given key and returns the data, or NULL if there's none
+*/
+gpointer
+sp_search_by_data_recursive (GtkWidget *w, gpointer key)
+{
+ gpointer r = NULL;
+
+ if (w && GTK_IS_OBJECT(w)) {
+ r = gtk_object_get_data (GTK_OBJECT(w), (gchar *) key);
+ }
+ if (r) return r;
+
+ if (GTK_IS_CONTAINER(w)) {
+ GList *ch = gtk_container_get_children (GTK_CONTAINER(w));
+ for (GList *i = ch; i != NULL; i = i->next) {
+ r = sp_search_by_data_recursive(GTK_WIDGET(i->data), key);
+ if (r) return r;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+\brief Returns the descendant of w which has the given key and value pair, or NULL if there's none
+*/
+GtkWidget *
+sp_search_by_value_recursive (GtkWidget *w, gchar *key, gchar *value)
+{
+ gchar *r = NULL;
+ GtkWidget *child;
+
+ if (w && GTK_IS_OBJECT(w)) {
+ r = (gchar *) gtk_object_get_data (GTK_OBJECT(w), key);
+ }
+ if (r && !strcmp (r, value)) return w;
+
+ if (GTK_IS_CONTAINER(w)) {
+ GList *ch = gtk_container_get_children (GTK_CONTAINER(w));
+ for (GList *i = ch; i != NULL; i = i->next) {
+ child = sp_search_by_value_recursive(GTK_WIDGET(i->data), key, value);
+ if (child) return child;
+ }
+ }
+
+ return NULL;
+}
+
diff --git a/src/widgets/spw-utilities.h b/src/widgets/spw-utilities.h
new file mode 100644
index 000000000..7df08a2ce
--- /dev/null
+++ b/src/widgets/spw-utilities.h
@@ -0,0 +1,56 @@
+#ifndef __SPW_UTILITIES_H__
+#define __SPW_UTILITIES_H__
+
+/*
+ * Inkscape Widget Utilities
+ *
+ * Author:
+ * Bryce W. Harrington <brycehar@bryceharrington.com>
+ *
+ * Copyright (C) 2003 Bryce Harrington
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+/* The following are helper routines for making Inkscape dialog widgets.
+ All are prefixed with spw_, short for inkscape_widget. This is not to
+ be confused with SPWidget, an existing datatype associated with Inkscape::XML::Node/
+ SPObject, that reacts to modification.
+*/
+
+#include <glib/gtypes.h>
+#include <gtk/gtkstyle.h> /* GtkWidget */
+
+GtkWidget *
+spw_label(GtkWidget *table, gchar const *label_text, int col, int row);
+
+GtkWidget *
+spw_hbox(GtkWidget *table, int width, int col, int row);
+
+GtkWidget *
+spw_vbox_checkbutton(GtkWidget *dialog, GtkWidget *table,
+ const gchar *label, const gchar *tip, gchar *key, GCallback cb);
+
+GtkWidget *
+spw_checkbutton(GtkWidget *dialog, GtkWidget *table,
+ gchar const *label, gchar *key, int col, int row,
+ int sensitive, GCallback cb);
+
+GtkWidget *
+spw_dropdown(GtkWidget *dialog, GtkWidget *table,
+ gchar const *label, gchar *key, int row,
+ GtkWidget *selector
+ );
+
+GtkWidget *
+spw_unit_selector(GtkWidget *dialog, GtkWidget *table,
+ gchar const *label, gchar *key, int row,
+ GtkWidget *us, GCallback cb, bool can_be_negative = false);
+
+void sp_set_font_size (GtkWidget *w, guint font);
+void sp_set_font_size_smaller (GtkWidget *w);
+
+gpointer sp_search_by_data_recursive(GtkWidget *w, gpointer data);
+GtkWidget *sp_search_by_value_recursive(GtkWidget *w, gchar *key, gchar *value);
+
+#endif
diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp
new file mode 100644
index 000000000..7ebbf7a85
--- /dev/null
+++ b/src/widgets/toolbox.cpp
@@ -0,0 +1,3028 @@
+#define __SP_MAINTOOLBOX_C__
+
+/** \file
+ * Controls bars for some of Inkscape's tools
+ * (for some tools, they are in their own files)
+ */
+
+/*
+*
+* Authors:
+* MenTaLguY <mental@rydia.net>
+* Lauris Kaplinski <lauris@kaplinski.com>
+* bulia byak <buliabyak@users.sf.net>
+* Frank Felfe <innerspace@iname.com>
+* John Cliff <simarilius@yahoo.com>
+* David Turner <novalis@gnu.org>
+* Josh Andler <scislac@scislac.com>
+*
+* Copyright (C) 2004 David Turner
+* Copyright (C) 2003 MenTaLguY
+* Copyright (C) 1999-2005 authors
+* Copyright (C) 2001-2002 Ximian, Inc.
+*
+* Released under GNU GPL, read the file 'COPYING' for more information
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "widgets/button.h"
+#include "widgets/widget-sizes.h"
+#include "widgets/spw-utilities.h"
+#include "widgets/spinbutton-events.h"
+
+#include "prefs-utils.h"
+#include "verbs.h"
+#include "sp-namedview.h"
+#include "desktop.h"
+#include "desktop-handles.h"
+#include "nodepath.h"
+#include "xml/repr.h"
+#include "xml/node-event-vector.h"
+#include <glibmm/i18n.h>
+#include "helper/unit-menu.h"
+#include "helper/units.h"
+
+
+
+#include "select-toolbar.h"
+#include "gradient-toolbar.h"
+
+#include "connector-context.h"
+#include "sp-rect.h"
+#include "sp-star.h"
+#include "sp-spiral.h"
+#include "sp-ellipse.h"
+#include "selection.h"
+#include "document-private.h"
+
+#include "mod360.h"
+
+#include "toolbox.h"
+
+typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
+typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
+
+static GtkWidget *sp_node_toolbox_new(SPDesktop *desktop);
+static GtkWidget *sp_zoom_toolbox_new(SPDesktop *desktop);
+static GtkWidget *sp_star_toolbox_new(SPDesktop *desktop);
+static GtkWidget *sp_arc_toolbox_new(SPDesktop *desktop);
+static GtkWidget *sp_rect_toolbox_new(SPDesktop *desktop);
+static GtkWidget *sp_spiral_toolbox_new(SPDesktop *desktop);
+static GtkWidget *sp_calligraphy_toolbox_new(SPDesktop *desktop);
+static GtkWidget *sp_dropper_toolbox_new(SPDesktop *desktop);
+static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
+static GtkWidget *sp_text_toolbox_new(SPDesktop *desktop);
+static GtkWidget *sp_connector_toolbox_new(SPDesktop *desktop);
+
+
+static struct {
+ gchar const *type_name;
+ gchar const *data_name;
+ sp_verb_t verb;
+ sp_verb_t doubleclick_verb;
+} const tools[] = {
+ { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
+ { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
+ { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
+ { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
+ { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
+ { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
+ { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
+ { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
+ { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
+ { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
+ { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
+ { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
+ { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
+ { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
+ { NULL, NULL, 0, 0 }
+};
+
+static struct {
+ gchar const *type_name;
+ gchar const *data_name;
+ GtkWidget *(*create_func)(SPDesktop *desktop);
+} const aux_toolboxes[] = {
+ { "SPSelectContext", "select_toolbox", sp_select_toolbox_new },
+ { "SPNodeContext", "node_toolbox", sp_node_toolbox_new },
+ { "SPZoomContext", "zoom_toolbox", sp_zoom_toolbox_new },
+ { "SPStarContext", "star_toolbox", sp_star_toolbox_new },
+ { "SPRectContext", "rect_toolbox", sp_rect_toolbox_new },
+ { "SPArcContext", "arc_toolbox", sp_arc_toolbox_new },
+ { "SPSpiralContext", "spiral_toolbox", sp_spiral_toolbox_new },
+ { "SPPencilContext", "pencil_toolbox", NULL },
+ { "SPPenContext", "pen_toolbox", NULL },
+ { "SPDynaDrawContext", "calligraphy_toolbox", sp_calligraphy_toolbox_new },
+ { "SPTextContext", "text_toolbox", sp_text_toolbox_new },
+ { "SPDropperContext", "dropper_toolbox", sp_dropper_toolbox_new },
+ { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new },
+ { "SPConnectorContext", "connector_toolbox", sp_connector_toolbox_new },
+ { NULL, NULL, NULL }
+};
+
+static void toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
+
+static void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
+static void update_tool_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
+static void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
+static void update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
+static void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
+static void update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
+
+/* Global text entry widgets necessary for update */
+/* GtkWidget *dropper_rgb_entry,
+ *dropper_opacity_entry ; */
+// should be made a private member once this is converted to class
+
+static void delete_connection(GObject *obj, sigc::connection *connection) {
+ connection->disconnect();
+ delete connection;
+}
+
+static GtkWidget *
+sp_toolbox_button_new(GtkWidget *t, GtkIconSize size, gchar const *pxname, GtkSignalFunc handler,
+ GtkTooltips *tt, gchar const *tip)
+{
+ GtkWidget *b = sp_button_new_from_data(size, SP_BUTTON_TYPE_NORMAL, NULL, pxname, tip, tt);
+ gtk_widget_show(b);
+ if (handler) gtk_signal_connect(GTK_OBJECT(b), "clicked", handler, NULL);
+ gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
+
+ return b;
+}
+
+
+GtkWidget *
+sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, GtkIconSize size, SPButtonType type,
+ Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
+ Inkscape::UI::View::View *view, GtkTooltips *tt)
+{
+ SPAction *action = verb->get_action(view);
+ if (!action) return NULL;
+
+ SPAction *doubleclick_action;
+ if (doubleclick_verb)
+ doubleclick_action = doubleclick_verb->get_action(view);
+ else
+ doubleclick_action = NULL;
+
+ /* fixme: Handle sensitive/unsensitive */
+ /* fixme: Implement sp_button_new_from_action */
+ GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
+ gtk_widget_show(b);
+ gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
+
+ return b;
+}
+
+GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, GtkIconSize size, SPButtonType type, Inkscape::Verb *verb,
+ Inkscape::UI::View::View *view, GtkTooltips *tt)
+{
+ return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
+}
+
+GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, GtkIconSize size, Inkscape::Verb *verb,
+ Inkscape::UI::View::View *view, GtkTooltips *tt)
+{
+ return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
+}
+
+GtkWidget *
+sp_tool_toolbox_new()
+{
+ GtkTooltips *tt = gtk_tooltips_new();
+ GtkWidget *tb = gtk_vbox_new(FALSE, 0);
+
+ g_object_set_data(G_OBJECT(tb), "desktop", NULL);
+ g_object_set_data(G_OBJECT(tb), "tooltips", tt);
+
+ gtk_widget_set_sensitive(tb, FALSE);
+
+ GtkWidget *hb = gtk_handle_box_new();
+ gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
+ gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
+ gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
+
+ gtk_container_add(GTK_CONTAINER(hb), tb);
+ gtk_widget_show(GTK_WIDGET(tb));
+
+ sigc::connection* conn = new sigc::connection;
+ g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
+
+ return hb;
+}
+
+static void
+aux_toolbox_attached(GtkHandleBox *toolbox, GtkWidget *child)
+{
+ g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
+ gtk_widget_queue_resize(child);
+}
+
+static void
+aux_toolbox_detached(GtkHandleBox *toolbox, GtkWidget *child)
+{
+ g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
+ gtk_widget_queue_resize(child);
+}
+
+GtkWidget *
+sp_aux_toolbox_new()
+{
+ GtkWidget *tb = gtk_vbox_new(FALSE, 0);
+
+ GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
+ GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
+ gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
+ gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(tb), "desktop", NULL);
+ g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
+
+ gtk_widget_set_sensitive(tb, FALSE);
+
+ GtkWidget *hb = gtk_handle_box_new();
+ gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
+ gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
+ gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
+
+ g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
+ g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
+
+ gtk_container_add(GTK_CONTAINER(hb), tb);
+ gtk_widget_show(GTK_WIDGET(tb));
+
+ sigc::connection* conn = new sigc::connection;
+ g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
+
+ return hb;
+}
+
+//####################################
+//# Commands Bar
+//####################################
+
+GtkWidget *
+sp_commands_toolbox_new()
+{
+ GtkWidget *tb = gtk_vbox_new(FALSE, 0);
+
+ GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
+ GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
+ gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
+ gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(tb), "desktop", NULL);
+ gtk_widget_set_sensitive(tb, FALSE);
+
+ GtkWidget *hb = gtk_handle_box_new();
+ gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
+ gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
+ gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
+
+ g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
+ g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
+
+ gtk_container_add(GTK_CONTAINER(hb), tb);
+ gtk_widget_show(GTK_WIDGET(tb));
+
+ sigc::connection* conn = new sigc::connection;
+ g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
+
+ return hb;
+}
+
+
+//####################################
+//# node editing callbacks
+//####################################
+
+void
+sp_node_path_edit_add(void)
+{
+ sp_node_selected_add_node();
+}
+
+void
+sp_node_path_edit_delete(void)
+{
+ sp_node_selected_delete();
+}
+
+void
+sp_node_path_edit_delete_segment(void)
+{
+ sp_node_selected_delete_segment();
+}
+
+void
+sp_node_path_edit_break(void)
+{
+ sp_node_selected_break();
+}
+
+void
+sp_node_path_edit_join(void)
+{
+ sp_node_selected_join();
+}
+
+void
+sp_node_path_edit_join_segment(void)
+{
+ sp_node_selected_join_segment();
+}
+
+void
+sp_node_path_edit_toline(void)
+{
+ sp_node_selected_set_line_type(NR_LINETO);
+}
+
+void
+sp_node_path_edit_tocurve(void)
+{
+ sp_node_selected_set_line_type(NR_CURVETO);
+}
+
+void
+sp_node_path_edit_cusp(void)
+{
+ sp_node_selected_set_type(Inkscape::NodePath::NODE_CUSP);
+}
+
+void
+sp_node_path_edit_smooth(void)
+{
+ sp_node_selected_set_type(Inkscape::NodePath::NODE_SMOOTH);
+}
+
+void
+sp_node_path_edit_symmetrical(void)
+{
+ sp_node_selected_set_type(Inkscape::NodePath::NODE_SYMM);
+}
+
+
+
+//################################
+//## Node Editing Toolbox ##
+//################################
+
+static GtkWidget *
+sp_node_toolbox_new(SPDesktop *desktop)
+{
+ Inkscape::UI::View::View *view = desktop;
+
+ GtkTooltips *tt = gtk_tooltips_new();
+ GtkWidget *tb = gtk_hbox_new(FALSE, 0);
+
+ gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, "node_insert",
+ GTK_SIGNAL_FUNC(sp_node_path_edit_add), tt, _("Insert new nodes into selected segments"));
+ sp_toolbox_button_new(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, "node_delete",
+ GTK_SIGNAL_FUNC(sp_node_path_edit_delete), tt, _("Delete selected nodes"));
+
+ gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, "node_join",
+ GTK_SIGNAL_FUNC(sp_node_path_edit_join), tt, _("Join paths at selected nodes"));
+ sp_toolbox_button_new(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, "node_join_segment",
+ GTK_SIGNAL_FUNC(sp_node_path_edit_join_segment), tt, _("Join paths at selected nodes with new segment"));
+
+ sp_toolbox_button_new(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, "node_delete_segment",
+ GTK_SIGNAL_FUNC(sp_node_path_edit_delete_segment), tt, _("Split path between two non-endpoint nodes"));
+
+ sp_toolbox_button_new(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, "node_break",
+ GTK_SIGNAL_FUNC(sp_node_path_edit_break), tt, _("Break path at selected nodes"));
+
+ gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, "node_cusp",
+ GTK_SIGNAL_FUNC(sp_node_path_edit_cusp), tt, _("Make selected nodes corner"));
+
+ sp_toolbox_button_new(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, "node_smooth",
+ GTK_SIGNAL_FUNC(sp_node_path_edit_smooth), tt, _("Make selected nodes smooth"));
+
+ sp_toolbox_button_new(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, "node_symmetric",
+ GTK_SIGNAL_FUNC(sp_node_path_edit_symmetrical), tt, _("Make selected nodes symmetric"));
+
+ gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, "node_line",
+ GTK_SIGNAL_FUNC(sp_node_path_edit_toline), tt, _("Make selected segments lines"));
+
+ sp_toolbox_button_new(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, "node_curve",
+ GTK_SIGNAL_FUNC(sp_node_path_edit_tocurve), tt, _("Make selected segments curves"));
+
+ gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_normal_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_OBJECT_TO_CURVE), view, tt);
+
+ sp_toolbox_button_normal_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_SELECTION_OUTLINE), view, tt);
+
+ gtk_widget_show_all(tb);
+
+ return tb;
+
+} // end of sp_node_toolbox_new()
+
+
+//########################
+//## Zoom Toolbox ##
+//########################
+
+static GtkWidget *
+sp_zoom_toolbox_new(SPDesktop *desktop)
+{
+ Inkscape::UI::View::View *view=desktop;
+
+ GtkTooltips *tt = gtk_tooltips_new();
+ GtkWidget *tb = gtk_hbox_new(FALSE, 0);
+
+ gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0),
+ FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_IN), view, tt);
+
+ sp_toolbox_button_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_OUT), view, tt);
+
+ gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_SELECTION), view, tt);
+
+ sp_toolbox_button_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_DRAWING), view, tt);
+
+ sp_toolbox_button_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_PAGE), view, tt);
+
+ sp_toolbox_button_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_PAGE_WIDTH), view, tt);
+
+ gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_PREV), view, tt);
+
+ sp_toolbox_button_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_NEXT), view, tt);
+
+ gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_1_1), view, tt);
+
+ sp_toolbox_button_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_1_2), view, tt);
+
+ sp_toolbox_button_new_from_verb(tb, GTK_ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_2_1), view, tt);
+
+ gtk_widget_show_all(tb);
+
+ return tb;
+
+} // end of sp_zoom_toolbox_new()
+
+void
+sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
+{
+ toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_tool_toolbox, update_tool_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
+}
+
+
+void
+sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
+{
+ toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_aux_toolbox, update_aux_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
+}
+
+void
+sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
+{
+ toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_commands_toolbox, update_commands_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
+}
+
+
+static void
+toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
+{
+ gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
+ SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
+
+ if (old_desktop) {
+ GList *children, *iter;
+
+ children = gtk_container_get_children(GTK_CONTAINER(toolbox));
+ for ( iter = children ; iter ; iter = iter->next ) {
+ gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
+ }
+ g_list_free(children);
+ }
+
+ g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
+
+ if (desktop) {
+ gtk_widget_set_sensitive(toolbox, TRUE);
+ setup_func(toolbox, desktop);
+ update_func(desktop, desktop->event_context, toolbox);
+ *conn = desktop->connectEventContextChanged
+ (sigc::bind (sigc::ptr_fun(update_func), toolbox));
+ } else {
+ gtk_widget_set_sensitive(toolbox, FALSE);
+ }
+
+} // end of toolbox_set_desktop()
+
+
+static void
+setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
+{
+ GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
+ gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
+ GtkIconSize toolboxSize = shrinkLeft ? GTK_ICON_SIZE_SMALL_TOOLBAR : GTK_ICON_SIZE_LARGE_TOOLBAR;
+
+ for (int i = 0 ; tools[i].type_name ; i++ ) {
+ GtkWidget *button =
+ sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
+ SP_BUTTON_TYPE_TOGGLE,
+ Inkscape::Verb::get(tools[i].verb),
+ Inkscape::Verb::get(tools[i].doubleclick_verb),
+ desktop,
+ tooltips );
+
+ g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
+ (gpointer)button );
+ }
+}
+
+
+static void
+update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
+{
+ gchar const *const tname = ( eventcontext
+ ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
+ : NULL );
+ for (int i = 0 ; tools[i].type_name ; i++ ) {
+ SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
+ sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
+ }
+}
+
+static void
+setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
+{
+ GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
+
+ for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
+ GtkWidget *sub_toolbox;
+ if (aux_toolboxes[i].create_func == NULL)
+ sub_toolbox = sp_empty_toolbox_new(desktop);
+ else
+ sub_toolbox = aux_toolboxes[i].create_func(desktop);
+
+ gtk_size_group_add_widget( grouper, sub_toolbox );
+
+ gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
+ g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
+ }
+ g_object_unref( G_OBJECT(grouper) );
+}
+
+static void
+update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
+{
+ gchar const *tname = ( eventcontext
+ ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
+ : NULL );
+ for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
+ GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
+ if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
+ gtk_widget_show_all(sub_toolbox);
+ g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
+ } else {
+ gtk_widget_hide(sub_toolbox);
+ }
+ }
+}
+
+static void
+setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
+{
+ Inkscape::UI::View::View *view = desktop;
+
+ GtkTooltips *tt = gtk_tooltips_new();
+ GtkWidget *tb = gtk_hbox_new(FALSE, 0);
+
+ gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
+ GtkIconSize toolboxSize = shrinkTop ? GTK_ICON_SIZE_SMALL_TOOLBAR : GTK_ICON_SIZE_LARGE_TOOLBAR;
+
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_NEW), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_OPEN), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_SAVE), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_PRINT), view, tt);
+
+ aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_IMPORT), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_EXPORT), view, tt);
+
+ aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_REDO), view, tt);
+
+ aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_COPY), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_CUT), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view, tt);
+
+ aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_SELECTION), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_DRAWING), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_PAGE), view, tt);
+
+ aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_CLONE), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_UNLINK_CLONE), view, tt);
+
+ aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_normal_new_from_verb(tb, toolboxSize, Inkscape::Verb::get(SP_VERB_SELECTION_GROUP), view, tt);
+ sp_toolbox_button_normal_new_from_verb(tb, toolboxSize, Inkscape::Verb::get(SP_VERB_SELECTION_UNGROUP), view, tt);
+
+ // disabled until we have icons for them:
+
+ //find
+
+ //sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_TILE), view, tt);
+ //sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_UNTILE), view, tt);
+
+ aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_FILL_STROKE), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_TEXT), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_XML_EDITOR), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_ALIGN_DISTRIBUTE), view, tt);
+
+ aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_DISPLAY), view, tt);
+ sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_NAMEDVIEW), view, tt);
+
+ gtk_widget_show_all(tb);
+
+ gtk_container_add(GTK_CONTAINER(toolbox), tb);
+}
+
+static void
+update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
+{
+}
+
+void show_aux_toolbox(GtkWidget *toolbox_toplevel)
+{
+ gtk_widget_show(toolbox_toplevel);
+ GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
+
+ GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
+ if (!shown_toolbox) {
+ return;
+ }
+ gtk_widget_show(toolbox);
+
+ // need to show the spacer, or the padding will be off
+ GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
+ gtk_widget_show(spacer);
+
+ gtk_widget_show_all(shown_toolbox);
+}
+
+void
+aux_toolbox_space(GtkWidget *tb, gint space)
+{
+ gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
+}
+
+static GtkWidget *
+sp_empty_toolbox_new(SPDesktop *desktop)
+{
+ GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
+ gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
+ gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
+
+ gtk_widget_show_all(tbl);
+ sp_set_font_size_smaller (tbl);
+
+ return tbl;
+}
+
+// helper UI functions
+
+GtkWidget *
+sp_tb_spinbutton(
+ gchar *label, gchar const *tooltip,
+ gchar const *path, gchar const *data, gdouble def,
+ GtkWidget *us,
+ GtkWidget *tbl,
+ gboolean altx, gchar const *altx_mark,
+ gdouble lower, gdouble upper, gdouble step, gdouble page,
+ void (*callback)(GtkAdjustment *, GtkWidget *),
+ gdouble climb = 0.1, guint digits = 3)
+{
+ GtkTooltips *tt = gtk_tooltips_new();
+
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+
+ GtkWidget *l = gtk_label_new(label);
+ gtk_widget_show(l);
+ gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
+ gtk_container_add(GTK_CONTAINER(hb), l);
+
+ GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def),
+ lower, upper, step, page, page);
+ gtk_object_set_data(GTK_OBJECT(tbl), data, a);
+ if (us)
+ sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
+
+ GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
+ gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
+ if (altx)
+ gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
+ gtk_widget_set_size_request(sb, AUX_SPINBUTTON_WIDTH_SMALL, AUX_SPINBUTTON_HEIGHT);
+ gtk_widget_show(sb);
+ gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
+ gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
+ gtk_container_add(GTK_CONTAINER(hb), sb);
+ gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
+
+ return hb;
+}
+
+#define MODE_LABEL_WIDTH 70
+
+//########################
+//## Star ##
+//########################
+
+static void
+sp_stb_magnitude_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
+
+ if (sp_document_get_undo_sensitive(SP_DT_DOCUMENT(desktop))) {
+ // do not remember prefs if this call is initiated by an undo change, because undoing object
+ // creation sets bogus values to its attributes before it is deleted
+ prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
+ }
+
+ // quit if run by the attr_changed listener
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent listener from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ bool modmade = FALSE;
+
+ Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+ GSList const *items = selection->itemList();
+ for (; items != NULL; items = items->next) {
+ if (SP_IS_STAR((SPItem *) items->data)) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
+ sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
+ sp_repr_set_svg_double(repr, "sodipodi:arg2",
+ (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
+ + M_PI / (gint)adj->value));
+ SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
+ modmade = true;
+ }
+ }
+ if (modmade) sp_document_done(SP_DT_DOCUMENT(desktop));
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void
+sp_stb_proportion_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
+
+ if (sp_document_get_undo_sensitive(SP_DT_DOCUMENT(desktop))) {
+ prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
+ }
+
+ // quit if run by the attr_changed listener
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent listener from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ bool modmade = FALSE;
+ Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+ GSList const *items = selection->itemList();
+ for (; items != NULL; items = items->next) {
+ if (SP_IS_STAR((SPItem *) items->data)) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
+
+ gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
+ gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
+ if (r2 < r1) {
+ sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
+ } else {
+ sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
+ }
+
+ SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
+ modmade = true;
+ }
+ }
+
+ if (modmade) sp_document_done(SP_DT_DOCUMENT(desktop));
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void
+sp_stb_sides_flat_state_changed(GtkWidget *widget, GtkObject *tbl)
+{
+ SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
+
+ if (sp_document_get_undo_sensitive(SP_DT_DOCUMENT(desktop))) {
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
+ prefs_set_string_attribute("tools.shapes.star", "isflatsided", "true");
+ } else {
+ prefs_set_string_attribute("tools.shapes.star", "isflatsided", "false");
+ }
+ }
+
+ // quit if run by the attr_changed listener
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent listener from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+ GSList const *items = selection->itemList();
+ GtkWidget *prop_widget = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "prop_widget");
+ bool modmade = FALSE;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
+ gtk_widget_set_sensitive(GTK_WIDGET(prop_widget), FALSE);
+ for (; items != NULL; items = items->next) {
+ if (SP_IS_STAR((SPItem *) items->data)) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
+ repr->setAttribute("inkscape:flatsided", "true");
+ SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
+ modmade = true;
+ }
+ }
+ } else {
+ gtk_widget_set_sensitive(GTK_WIDGET(prop_widget), TRUE);
+ for (; items != NULL; items = items->next) {
+ if (SP_IS_STAR((SPItem *) items->data)) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
+ repr->setAttribute("inkscape:flatsided", "false");
+ SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
+ modmade = true;
+ }
+ }
+ }
+ if (modmade) sp_document_done(SP_DT_DOCUMENT(desktop));
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void
+sp_stb_rounded_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
+
+ if (sp_document_get_undo_sensitive(SP_DT_DOCUMENT(desktop))) {
+ prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
+ }
+
+ // quit if run by the attr_changed listener
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent listener from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ bool modmade = FALSE;
+
+ Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+ GSList const *items = selection->itemList();
+ for (; items != NULL; items = items->next) {
+ if (SP_IS_STAR((SPItem *) items->data)) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
+ sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
+ SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
+ modmade = true;
+ }
+ }
+ if (modmade) sp_document_done(SP_DT_DOCUMENT(desktop));
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+
+static void
+sp_stb_randomized_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
+
+ if (sp_document_get_undo_sensitive(SP_DT_DOCUMENT(desktop))) {
+ prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
+ }
+
+ // quit if run by the attr_changed listener
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent listener from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ bool modmade = FALSE;
+
+ Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+ GSList const *items = selection->itemList();
+ for (; items != NULL; items = items->next) {
+ if (SP_IS_STAR((SPItem *) items->data)) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
+ sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
+ SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
+ modmade = true;
+ }
+ }
+ if (modmade) sp_document_done(SP_DT_DOCUMENT(desktop));
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+
+static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
+ gchar const *old_value, gchar const *new_value,
+ bool is_interactive, gpointer data)
+{
+ GtkWidget *tbl = GTK_WIDGET(data);
+
+ // quit if run by the _changed callbacks
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent callbacks from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ GtkAdjustment *adj;
+
+ if (!strcmp(name, "inkscape:randomized")) {
+ adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "randomized");
+ gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
+ } else if (!strcmp(name, "inkscape:rounded")) {
+ adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "rounded");
+ gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
+ } else if (!strcmp(name, "inkscape:flatsided")) {
+ GtkWidget *fscb = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "flat_checkbox");
+ GtkWidget *prop_widget = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "prop_widget");
+ char const *flatsides = repr->attribute("inkscape:flatsided");
+ if (flatsides && !strcmp(flatsides,"false" )) {
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(fscb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(prop_widget), TRUE);
+ } else {
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(fscb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(prop_widget), FALSE);
+ }
+ } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
+ adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
+ gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
+ gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
+ if (r2 < r1) {
+ gtk_adjustment_set_value(adj, r2/r1);
+ } else {
+ gtk_adjustment_set_value(adj, r1/r2);
+ }
+ } else if (!strcmp(name, "sodipodi:sides")) {
+ adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
+ gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
+ }
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+}
+
+
+static Inkscape::XML::NodeEventVector star_tb_repr_events =
+{
+ NULL, /* child_added */
+ NULL, /* child_removed */
+ star_tb_event_attr_changed,
+ NULL, /* content_changed */
+ NULL /* order_changed */
+};
+
+
+/**
+ * \param selection Should not be NULL.
+ */
+static void
+sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GtkObject *tbl)
+{
+ int n_selected = 0;
+ Inkscape::XML::Node *repr = NULL;
+ Inkscape::XML::Node *oldrepr = NULL;
+
+ for (GSList const *items = selection->itemList();
+ items != NULL;
+ items = items->next)
+ {
+ if (SP_IS_STAR((SPItem *) items->data)) {
+ n_selected++;
+ repr = SP_OBJECT_REPR((SPItem *) items->data);
+ }
+ }
+
+ GtkWidget *l = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(tbl), "mode_label"));
+
+ if (n_selected == 0) {
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>New:</b>"));
+ } else if (n_selected == 1) {
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
+
+ oldrepr = (Inkscape::XML::Node *) gtk_object_get_data(GTK_OBJECT(tbl), "repr");
+ if (oldrepr) { // remove old listener
+ sp_repr_remove_listener_by_data(oldrepr, tbl);
+ Inkscape::GC::release(oldrepr);
+ oldrepr = 0;
+ g_object_set_data(G_OBJECT(tbl), "repr", NULL);
+ }
+
+ if (repr) {
+ g_object_set_data(G_OBJECT(tbl), "repr", repr);
+ Inkscape::GC::anchor(repr);
+ sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
+ sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
+ }
+ } else {
+ // FIXME: implement averaging of all parameters for multiple selected stars
+ //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
+ }
+}
+
+
+static void
+sp_stb_defaults(GtkWidget *widget, GtkWidget *tbl)
+{
+ // FIXME: in this and all other _default functions, set some flag telling the value_changed
+ // callbacks to lump all the changes for all selected objects in one undo step
+
+ GtkAdjustment *adj;
+
+ // fixme: make settable in prefs!
+ gint mag = 5;
+ gdouble prop = 0.5;
+ gboolean flat = FALSE;
+ gdouble randomized = 0;
+ gdouble rounded = 0;
+
+ GtkWidget *fscb = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "flat_checkbox");
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(fscb), flat);
+ GtkWidget *sb2 = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "prop_widget");
+ gtk_widget_set_sensitive(GTK_WIDGET(sb2), !flat);
+
+ adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
+ gtk_adjustment_set_value(adj, mag);
+ gtk_adjustment_value_changed(adj);
+
+ adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
+ gtk_adjustment_set_value(adj, prop);
+ gtk_adjustment_value_changed(adj);
+
+ adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "rounded");
+ gtk_adjustment_set_value(adj, rounded);
+ gtk_adjustment_value_changed(adj);
+
+ adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "randomized");
+ gtk_adjustment_set_value(adj, randomized);
+ gtk_adjustment_value_changed(adj);
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+
+void
+sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
+{
+ GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
+ if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
+ GtkWidget *l = gtk_label_new(NULL);
+ gtk_label_set_markup(GTK_LABEL(l), title);
+ gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
+ gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
+}
+
+
+static GtkWidget *
+sp_star_toolbox_new(SPDesktop *desktop)
+{
+ GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
+
+ gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
+ gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
+
+ GtkTooltips *tt = gtk_tooltips_new();
+
+ sp_toolbox_add_label(tbl, _("<b>New:</b>"));
+
+ gchar const *flatsidedstr = NULL;
+
+ /* Flatsided checkbox */
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *fscb = gtk_check_button_new_with_label(_("Polygon"));
+ gtk_widget_set_sensitive(GTK_WIDGET(fscb), TRUE);
+ flatsidedstr = prefs_get_string_attribute("tools.shapes.star", "isflatsided");
+ if (!flatsidedstr || (flatsidedstr && !strcmp(flatsidedstr, "false")))
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(fscb), FALSE);
+ else
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(fscb), TRUE);
+ gtk_tooltips_set_tip(tt, fscb, _("Regular polygon (with one handle) instead of a star"), NULL);
+ gtk_widget_show(fscb);
+ gtk_object_set_data(GTK_OBJECT(tbl), "flat_checkbox", fscb);
+ gtk_container_add(GTK_CONTAINER(hb), fscb);
+ g_signal_connect(G_OBJECT(fscb), "toggled", GTK_SIGNAL_FUNC(sp_stb_sides_flat_state_changed ), tbl);
+ gtk_box_pack_start(GTK_BOX(tbl),hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
+
+ /* Magnitude */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Corners:"), _("Number of corners of a polygon or star"),
+ "tools.shapes.star", "magnitude", 3,
+ NULL, tbl, TRUE, "altx-star",
+ 3, 1024, 1, 1,
+ sp_stb_magnitude_value_changed, 1, 0);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ /* Spoke ratio */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Spoke ratio:"),
+ // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
+ // Base radius is the same for the closest handle.
+ _("Base radius to tip radius ratio"),
+ "tools.shapes.star", "proportion", 0.5,
+ NULL, tbl, FALSE, NULL,
+ 0.01, 1.0, 0.01, 0.1,
+ sp_stb_proportion_value_changed);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ g_object_set_data(G_OBJECT(tbl), "prop_widget", hb);
+ if (!flatsidedstr || (flatsidedstr && !strcmp(flatsidedstr, "false")))
+ gtk_widget_set_sensitive(GTK_WIDGET(hb), TRUE);
+ else
+ gtk_widget_set_sensitive(GTK_WIDGET(hb), FALSE);
+ }
+
+ /* Roundedness */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
+ "tools.shapes.star", "rounded", 0.0,
+ NULL, tbl, FALSE, NULL,
+ -100.0, 100.0, 0.01, 0.1,
+ sp_stb_rounded_value_changed);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ /* Randomization */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Randomized:"), _("Scatter randomly the corners and angles"),
+ "tools.shapes.star", "randomized", 0.0,
+ NULL, tbl, FALSE, NULL,
+ -10.0, 10.0, 0.001, 0.01,
+ sp_stb_randomized_value_changed, 0.1, 3);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ aux_toolbox_space(tbl, AUX_SPACING);
+
+ /* Reset */
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *b = gtk_button_new_with_label(_("Defaults"));
+ gtk_tooltips_set_tip(tt, b, _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"), NULL);
+ gtk_widget_show(b);
+ gtk_container_add(GTK_CONTAINER(hb), b);
+ gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_stb_defaults), tbl);
+ gtk_box_pack_start(GTK_BOX(tbl),hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ gtk_widget_show_all(tbl);
+ sp_set_font_size_smaller (tbl);
+
+ sigc::connection *connection = new sigc::connection(
+ SP_DT_SELECTION(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GtkObject *)tbl))
+ );
+ g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_connection), connection);
+
+ return tbl;
+}
+
+
+//########################
+//## Rect ##
+//########################
+
+static void
+sp_rtb_sensitivize (GtkWidget *tbl)
+{
+ GtkAdjustment *adj1 = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "rx"));
+ GtkAdjustment *adj2 = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "ry"));
+ GtkWidget *not_rounded = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "not_rounded");
+
+ if (adj1->value == 0 && adj2->value == 0 && gtk_object_get_data(GTK_OBJECT(tbl), "single")) { // only for a single selected rect (for now)
+ gtk_widget_set_sensitive(GTK_WIDGET(not_rounded), FALSE);
+ } else {
+ gtk_widget_set_sensitive(GTK_WIDGET(not_rounded), TRUE);
+ }
+}
+
+
+static void
+sp_rtb_value_changed(GtkAdjustment *adj, GtkWidget *tbl, gchar const *value_name,
+ void (*setter)(SPRect *, gdouble))
+{
+ SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
+
+ GtkWidget *us = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(tbl), "units");
+ SPUnit const *unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
+
+ if (sp_document_get_undo_sensitive(SP_DT_DOCUMENT(desktop))) {
+ prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
+ }
+
+ // quit if run by the attr_changed listener
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent listener from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ bool modmade = false;
+ Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+ for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
+ if (SP_IS_RECT(items->data)) {
+ if (adj->value != 0) {
+ setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
+ } else {
+ SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
+ }
+ modmade = true;
+ }
+ }
+
+ sp_rtb_sensitivize (tbl);
+
+ if (modmade) {
+ sp_document_done(SP_DT_DOCUMENT(desktop));
+ }
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+
+ // defocus spinbuttons by moving focus to the canvas, unless "stay" is on
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void
+sp_rtb_rx_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
+}
+
+static void
+sp_rtb_ry_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
+}
+
+static void
+sp_rtb_width_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
+}
+
+static void
+sp_rtb_height_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
+}
+
+
+
+static void
+sp_rtb_defaults( GtkWidget *widget, GtkObject *obj)
+{
+ GtkWidget *tbl = GTK_WIDGET(obj);
+
+ GtkAdjustment *adj;
+
+ adj = (GtkAdjustment*)gtk_object_get_data(obj, "rx");
+ gtk_adjustment_set_value(adj, 0.0);
+ // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
+ gtk_adjustment_value_changed(adj);
+
+ adj = (GtkAdjustment*)gtk_object_get_data(obj, "ry");
+ gtk_adjustment_set_value(adj, 0.0);
+ gtk_adjustment_value_changed(adj);
+
+ sp_rtb_sensitivize (tbl);
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void rect_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
+ gchar const *old_value, gchar const *new_value,
+ bool is_interactive, gpointer data)
+{
+ GtkWidget *tbl = GTK_WIDGET(data);
+
+ // quit if run by the _changed callbacks
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent callbacks from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ GtkWidget *us = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(tbl), "units");
+ SPUnit const *unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
+
+ SPItem *item = SP_ITEM(g_object_get_data(G_OBJECT(tbl), "item"));
+ if (SP_IS_RECT(item)) {
+ {
+ GtkAdjustment *adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "rx");
+ gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
+ gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
+ }
+
+ {
+ GtkAdjustment *adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "ry");
+ gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
+ gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
+ }
+
+ {
+ GtkAdjustment *adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "width");
+ gdouble width = sp_rect_get_visible_width (SP_RECT(item));
+ gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
+ }
+
+ {
+ GtkAdjustment *adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "height");
+ gdouble height = sp_rect_get_visible_height (SP_RECT(item));
+ gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
+ }
+ }
+
+ sp_rtb_sensitivize (tbl);
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+}
+
+
+static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
+ NULL, /* child_added */
+ NULL, /* child_removed */
+ rect_tb_event_attr_changed,
+ NULL, /* content_changed */
+ NULL /* order_changed */
+};
+
+/**
+ * \param selection should not be NULL.
+ */
+static void
+sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GtkObject *tbl)
+{
+ int n_selected = 0;
+ Inkscape::XML::Node *repr = NULL;
+ SPItem *item = NULL;
+ Inkscape::XML::Node *oldrepr = NULL;
+
+ for (GSList const *items = selection->itemList();
+ items != NULL;
+ items = items->next) {
+ if (SP_IS_RECT((SPItem *) items->data)) {
+ n_selected++;
+ item = (SPItem *) items->data;
+ repr = SP_OBJECT_REPR(item);
+ }
+ }
+
+ GtkWidget *l = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(tbl), "mode_label"));
+
+ g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(FALSE));
+
+ if (n_selected == 0) {
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>New:</b>"));
+
+ GtkWidget *w = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(tbl), "width_sb");
+ gtk_widget_set_sensitive(w, FALSE);
+ GtkWidget *h = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(tbl), "height_sb");
+ gtk_widget_set_sensitive(h, FALSE);
+
+ } else if (n_selected == 1) {
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
+ g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(TRUE));
+
+ GtkWidget *w = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(tbl), "width_sb");
+ gtk_widget_set_sensitive(w, TRUE);
+ GtkWidget *h = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(tbl), "height_sb");
+ gtk_widget_set_sensitive(h, TRUE);
+
+ oldrepr = (Inkscape::XML::Node *) gtk_object_get_data(GTK_OBJECT(tbl), "repr");
+ if (oldrepr) { // remove old listener
+ sp_repr_remove_listener_by_data(oldrepr, tbl);
+ Inkscape::GC::release(oldrepr);
+ oldrepr = 0;
+ g_object_set_data(G_OBJECT(tbl), "repr", NULL);
+ }
+ if (repr) {
+ g_object_set_data(G_OBJECT(tbl), "repr", repr);
+ g_object_set_data(G_OBJECT(tbl), "item", item);
+ Inkscape::GC::anchor(repr);
+ sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
+ sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
+ }
+ } else {
+ // FIXME: implement averaging of all parameters for multiple selected
+ //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
+ sp_rtb_sensitivize (GTK_WIDGET(tbl));
+ }
+}
+
+
+static GtkWidget *
+sp_rect_toolbox_new(SPDesktop *desktop)
+{
+ GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
+
+ gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
+ gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
+
+ GtkTooltips *tt = gtk_tooltips_new();
+
+ sp_toolbox_add_label(tbl, _("<b>New:</b>"));
+
+ // rx/ry units menu: create
+ GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
+ sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
+ sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
+ // fixme: add % meaning per cent of the width/height
+
+ /* W */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("W:"), _("Width of rectangle"),
+ "tools.shapes.rect", "width", 0,
+ us, tbl, TRUE, "altx-rect",
+ 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
+ sp_rtb_width_value_changed);
+ gtk_object_set_data(GTK_OBJECT(tbl), "width_sb", hb);
+ gtk_widget_set_sensitive(hb, FALSE);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ /* H */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("H:"), _("Height of rectangle"),
+ "tools.shapes.rect", "height", 0,
+ us, tbl, FALSE, NULL,
+ 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
+ sp_rtb_height_value_changed);
+ gtk_object_set_data(GTK_OBJECT(tbl), "height_sb", hb);
+ gtk_widget_set_sensitive(hb, FALSE);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ /* rx */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Rx:"), _("Horizontal radius of rounded corners"),
+ "tools.shapes.rect", "rx", 0,
+ us, tbl, FALSE, NULL,
+ 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
+ sp_rtb_rx_value_changed);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ /* ry */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Ry:"), _("Vertical radius of rounded corners"),
+ "tools.shapes.rect", "ry", 0,
+ us, tbl, FALSE, NULL,
+ 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
+ sp_rtb_ry_value_changed);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ // add the units menu
+ gtk_widget_show(us);
+ gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
+
+ /* Reset */
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *b = gtk_button_new_with_label(_("Not rounded"));
+ gtk_object_set_data(GTK_OBJECT(tbl), "not_rounded", b);
+ gtk_tooltips_set_tip(tt, b, _("Make corners sharp"), NULL);
+ gtk_widget_show(b);
+ gtk_container_add(GTK_CONTAINER(hb), b);
+ gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_rtb_defaults), tbl);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(TRUE));
+ sp_rtb_sensitivize (tbl);
+
+ gtk_widget_show_all(tbl);
+ sp_set_font_size_smaller (tbl);
+
+ sigc::connection *connection = new sigc::connection(
+ SP_DT_SELECTION(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GtkObject *)tbl))
+ );
+ g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_connection), connection);
+
+ return tbl;
+}
+
+//########################
+//## Spiral ##
+//########################
+
+static void
+sp_spl_tb_value_changed(GtkAdjustment *adj, GtkWidget *tbl, gchar const *value_name)
+{
+ SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
+
+ if (sp_document_get_undo_sensitive(SP_DT_DOCUMENT(desktop))) {
+ prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
+ }
+
+ // quit if run by the attr_changed listener
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent listener from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
+
+ bool modmade = FALSE;
+ for (GSList const *items = SP_DT_SELECTION(desktop)->itemList();
+ items != NULL;
+ items = items->next)
+ {
+ if (SP_IS_SPIRAL((SPItem *) items->data)) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
+ sp_repr_set_svg_double( repr, namespaced_name, adj->value );
+ SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
+ modmade = true;
+ }
+ }
+
+ g_free(namespaced_name);
+
+ if (modmade) {
+ sp_document_done(SP_DT_DOCUMENT(desktop));
+ }
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void
+sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ sp_spl_tb_value_changed(adj, tbl, "revolution");
+}
+
+static void
+sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ sp_spl_tb_value_changed(adj, tbl, "expansion");
+}
+
+static void
+sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ sp_spl_tb_value_changed(adj, tbl, "t0");
+}
+
+static void
+sp_spl_tb_defaults(GtkWidget *widget, GtkObject *obj)
+{
+ GtkWidget *tbl = GTK_WIDGET(obj);
+
+ GtkAdjustment *adj;
+
+ // fixme: make settable
+ gdouble rev = 5;
+ gdouble exp = 1.0;
+ gdouble t0 = 0.0;
+
+ adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
+ gtk_adjustment_set_value(adj, rev);
+ gtk_adjustment_value_changed(adj);
+
+ adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
+ gtk_adjustment_set_value(adj, exp);
+ gtk_adjustment_value_changed(adj);
+
+ adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
+ gtk_adjustment_set_value(adj, t0);
+ gtk_adjustment_value_changed(adj);
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+
+static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
+ gchar const *old_value, gchar const *new_value,
+ bool is_interactive, gpointer data)
+{
+ GtkWidget *tbl = GTK_WIDGET(data);
+
+ // quit if run by the _changed callbacks
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent callbacks from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ GtkAdjustment *adj;
+ adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
+ gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
+
+ adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
+ gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
+
+ adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
+ gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+}
+
+
+static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
+ NULL, /* child_added */
+ NULL, /* child_removed */
+ spiral_tb_event_attr_changed,
+ NULL, /* content_changed */
+ NULL /* order_changed */
+};
+
+static void
+sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GtkObject *tbl)
+{
+ int n_selected = 0;
+ Inkscape::XML::Node *repr = NULL;
+ Inkscape::XML::Node *oldrepr = NULL;
+
+ for (GSList const *items = selection->itemList();
+ items != NULL;
+ items = items->next)
+ {
+ if (SP_IS_SPIRAL((SPItem *) items->data)) {
+ n_selected++;
+ repr = SP_OBJECT_REPR((SPItem *) items->data);
+ }
+ }
+
+ GtkWidget *l = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(tbl), "mode_label"));
+
+ if (n_selected == 0) {
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>New:</b>"));
+ } else if (n_selected == 1) {
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
+
+ oldrepr = (Inkscape::XML::Node *) gtk_object_get_data(GTK_OBJECT(tbl), "repr");
+ if (oldrepr) { // remove old listener
+ sp_repr_remove_listener_by_data(oldrepr, tbl);
+ Inkscape::GC::release(oldrepr);
+ oldrepr = 0;
+ g_object_set_data(G_OBJECT(tbl), "repr", NULL);
+ }
+
+ if (repr) {
+ g_object_set_data(G_OBJECT(tbl), "repr", repr);
+ Inkscape::GC::anchor(repr);
+ sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
+ sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
+ }
+ } else {
+ // FIXME: implement averaging of all parameters for multiple selected
+ //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
+ }
+}
+
+
+static GtkWidget *
+sp_spiral_toolbox_new(SPDesktop *desktop)
+{
+ GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
+ gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
+ gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
+
+ GtkTooltips *tt = gtk_tooltips_new();
+
+ sp_toolbox_add_label(tbl, _("<b>New:</b>"));
+
+ /* Revolution */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Turns:"), _("Number of revolutions"),
+ "tools.shapes.spiral", "revolution", 3.0,
+ NULL, tbl, TRUE, "altx-spiral",
+ 0.01, 1024.0, 0.1, 1.0,
+ sp_spl_tb_revolution_value_changed, 1, 2);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ /* Expansion */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
+ "tools.shapes.spiral", "expansion", 1.0,
+ NULL, tbl, FALSE, NULL,
+ 0.0, 1000.0, 0.01, 1.0,
+ sp_spl_tb_expansion_value_changed);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ /* T0 */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
+ "tools.shapes.spiral", "t0", 0.0,
+ NULL, tbl, FALSE, NULL,
+ 0.0, 0.999, 0.01, 1.0,
+ sp_spl_tb_t0_value_changed);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ aux_toolbox_space(tbl, AUX_SPACING);
+
+ /* Reset */
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *b = gtk_button_new_with_label(_("Defaults"));
+ gtk_tooltips_set_tip(tt, b, _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"), NULL);
+ gtk_widget_show(b);
+ gtk_container_add(GTK_CONTAINER(hb), b);
+ gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_spl_tb_defaults), tbl);
+ gtk_box_pack_start(GTK_BOX(tbl),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ gtk_widget_show_all(tbl);
+ sp_set_font_size_smaller (tbl);
+
+ sigc::connection *connection = new sigc::connection(
+ SP_DT_SELECTION(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GtkObject *)tbl))
+ );
+ g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_connection), connection);
+
+ return tbl;
+}
+
+
+//########################
+//## Calligraphy ##
+//########################
+
+static void
+sp_ddc_mass_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ prefs_set_double_attribute("tools.calligraphic", "mass", adj->value);
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void
+sp_ddc_drag_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ prefs_set_double_attribute("tools.calligraphic", "drag", adj->value);
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void
+sp_ddc_angle_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ prefs_set_double_attribute("tools.calligraphic", "angle", adj->value);
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void
+sp_ddc_width_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ prefs_set_double_attribute("tools.calligraphic", "width", adj->value);
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void
+sp_ddc_velthin_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void
+sp_ddc_flatness_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ prefs_set_double_attribute("tools.calligraphic", "flatness", adj->value);
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void
+sp_ddc_pressure_state_changed(GtkWidget *button, gpointer data)
+{
+ prefs_set_int_attribute ("tools.calligraphic", "usepressure", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 1 : 0);
+}
+
+static void
+sp_ddc_tilt_state_changed(GtkWidget *button, GtkWidget *calligraphy_angle)
+{
+ prefs_set_int_attribute ("tools.calligraphic", "usetilt", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 1 : 0);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(calligraphy_angle), !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)));
+}
+
+static void sp_ddc_defaults(GtkWidget *, GtkWidget *tbl)
+{
+ // FIXME: make defaults settable via Inkscape Options
+ struct KeyValue {
+ char const *key;
+ double value;
+ } const key_values[] = {
+ {"mass", 0.02},
+ {"drag", 1.0},
+ {"angle", 30.0},
+ {"width", 0.15},
+ {"thinning", 0.1},
+ {"flatness", 0.9}
+ };
+
+ for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
+ KeyValue const &kv = key_values[i];
+ GtkAdjustment &adj = *static_cast<GtkAdjustment *>(gtk_object_get_data(GTK_OBJECT(tbl), kv.key));
+ gtk_adjustment_set_value(&adj, kv.value);
+ }
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static GtkWidget *
+sp_calligraphy_toolbox_new(SPDesktop *desktop)
+{
+ GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
+ gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
+ gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
+
+ GtkTooltips *tt = gtk_tooltips_new();
+ GtkWidget *calligraphy_angle;
+
+ // interval
+ gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ /* Width */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Width:"), _("The width of the calligraphic pen (relative to the visible canvas area)"),
+ "tools.calligraphic", "width", 0.15,
+ NULL, tbl, TRUE, "altx-calligraphy",
+ 0.01, 1.0, 0.01, 0.1,
+ sp_ddc_width_value_changed, 0.01, 2);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ /* Thinning */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Thinning:"), _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
+ "tools.calligraphic", "thinning", 0.1,
+ NULL, tbl, FALSE, NULL,
+ -1.0, 1.0, 0.01, 0.1,
+ sp_ddc_velthin_value_changed, 0.01, 2);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ // interval
+ gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ /* Angle */
+ {
+ calligraphy_angle = sp_tb_spinbutton(_("Angle:"), _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
+ "tools.calligraphic", "angle", 30,
+ NULL, tbl, TRUE, "calligraphy-angle",
+ -90.0, 90.0, 1.0, 10.0,
+ sp_ddc_angle_value_changed, 1, 0);
+ gtk_box_pack_start(GTK_BOX(tbl), calligraphy_angle, FALSE, FALSE, AUX_SPACING);
+ }
+
+ /* Fixation */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Fixation:"), _("How fixed is the pen angle (0 = always perpendicular to stroke direction, 1 = fixed)"),
+ "tools.calligraphic", "flatness", 0.9,
+ NULL, tbl, FALSE, NULL,
+ 0.0, 1.0, 0.01, 0.1,
+ sp_ddc_flatness_value_changed, 0.01, 2);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ // interval
+ gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ /* Mass */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Mass:"), _("How much inertia affects the movement of the pen"),
+ "tools.calligraphic", "mass", 0.02,
+ NULL, tbl, FALSE, NULL,
+ 0.0, 1.0, 0.01, 0.1,
+ sp_ddc_mass_value_changed, 0.01, 2);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ /* Drag */
+ {
+ // TRANSLATORS: "drag" means "resistance" here
+ GtkWidget *hb = sp_tb_spinbutton(_("Drag:"), _("How much resistance affects the movement of the pen"),
+ "tools.calligraphic", "drag", 1,
+ NULL, tbl, FALSE, NULL,
+ 0.0, 1.0, 0.01, 0.1,
+ sp_ddc_drag_value_changed, 0.01, 2);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
+ }
+
+ // interval
+ gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+
+ GtkWidget *cvbox = gtk_vbox_new (FALSE, 0);
+ GtkWidget *cbox = gtk_hbox_new (FALSE, 0);
+
+ /* Use Pressure button */
+ {
+ GtkWidget *button = sp_button_new_from_data( GTK_ICON_SIZE_SMALL_TOOLBAR,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "use_pressure",
+ _("Use the pressure of the input device to alter the width of the pen"),
+ tt);
+ g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (sp_ddc_pressure_state_changed), NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), prefs_get_int_attribute ("tools.calligraphic", "usepressure", 1));
+ gtk_box_pack_start(GTK_BOX(cbox), button, FALSE, FALSE, 0);
+ }
+
+ /* Use Tilt button */
+ {
+ GtkWidget *button = sp_button_new_from_data( GTK_ICON_SIZE_SMALL_TOOLBAR,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "use_tilt",
+ _("Use the tilt of the input device to alter the angle of the pen's nib"),
+ tt);
+ g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (sp_ddc_tilt_state_changed), calligraphy_angle);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), prefs_get_int_attribute ("tools.calligraphic", "usetilt", 1));
+ gtk_widget_set_sensitive(GTK_WIDGET(calligraphy_angle), !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)));
+ gtk_box_pack_start(GTK_BOX(cbox), button, FALSE, FALSE, 0);
+ }
+
+ gtk_box_pack_start(GTK_BOX(cvbox), cbox, TRUE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(tbl), cvbox, FALSE, FALSE, 0);
+
+ /* Reset */
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *b = gtk_button_new_with_label(_("Defaults"));
+ gtk_tooltips_set_tip(tt, b, _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"), NULL);
+ gtk_widget_show(b);
+ gtk_container_add(GTK_CONTAINER(hb), b);
+ gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_ddc_defaults), tbl);
+ gtk_box_pack_start(GTK_BOX(tbl),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+/*
+ // Tablet features
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *fscb = gtk_check_button_new_with_label(_("Tablet"));
+ gtk_widget_set_sensitive(GTK_WIDGET(fscb), TRUE);
+
+
+
+
+ gtk_tooltips_set_tip(tt, fscb, _("Enable/Disable drawing tablet features"), NULL);
+ gtk_widget_show(fscb);
+ gtk_object_set_data(GTK_OBJECT(tbl), "flat_checkbox", fscb);
+ gtk_container_add(GTK_CONTAINER(hb), fscb);
+
+ gtk_box_pack_start(GTK_BOX(tbl),hb, FALSE, FALSE, AUX_SPACING);
+ }
+*/
+
+ gtk_widget_show_all(tbl);
+ sp_set_font_size_smaller (tbl);
+
+ return tbl;
+}
+
+
+//########################
+//## Circle / Arc ##
+//########################
+
+static void
+sp_arctb_sensitivize (GtkWidget *tbl, double v1, double v2)
+{
+ GtkWidget *ocb = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "open_checkbox");
+ GtkWidget *make_whole = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "make_whole");
+
+ if (v1 == 0 && v2 == 0) {
+ if (gtk_object_get_data(GTK_OBJECT(tbl), "single")) { // only for a single selected ellipse (for now)
+ gtk_widget_set_sensitive(GTK_WIDGET(ocb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(make_whole), FALSE);
+ }
+ } else {
+ gtk_widget_set_sensitive(GTK_WIDGET(ocb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(make_whole), TRUE);
+ }
+}
+
+static void
+sp_arctb_startend_value_changed(GtkAdjustment *adj, GtkWidget *tbl, gchar const *value_name, gchar const *other_name)
+{
+ SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
+
+ if (sp_document_get_undo_sensitive(SP_DT_DOCUMENT(desktop))) {
+ prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
+ }
+
+ // quit if run by the attr_changed listener
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent listener from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
+
+ bool modmade = FALSE;
+ for (GSList const *items = SP_DT_SELECTION(desktop)->itemList();
+ items != NULL;
+ items = items->next)
+ {
+ SPItem *item = SP_ITEM(items->data);
+
+ if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
+
+ SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
+ SPArc *arc = SP_ARC(item);
+
+ if (!strcmp(value_name, "start"))
+ ge->start = (adj->value * M_PI)/ 180;
+ else
+ ge->end = (adj->value * M_PI)/ 180;
+
+ sp_genericellipse_normalize(ge);
+ ((SPObject *)arc)->updateRepr();
+ ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+
+ modmade = true;
+ }
+ }
+
+ g_free(namespaced_name);
+
+ GtkAdjustment *other = (GtkAdjustment *)gtk_object_get_data(GTK_OBJECT(tbl), other_name);
+
+ sp_arctb_sensitivize (tbl, adj->value, other->value);
+
+ if (modmade) {
+ sp_document_maybe_done(SP_DT_DOCUMENT(desktop), value_name);
+ }
+
+ // defocus spinbuttons by moving focus to the canvas, unless "stay" is on
+ spinbutton_defocus(GTK_OBJECT(tbl));
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+}
+
+
+static void
+sp_arctb_start_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ sp_arctb_startend_value_changed(adj, tbl, "start", "end");
+}
+
+static void
+sp_arctb_end_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ sp_arctb_startend_value_changed(adj, tbl, "end", "start");
+}
+
+static void
+sp_arctb_open_state_changed(GtkWidget *widget, GtkObject *tbl)
+{
+ SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
+
+ if (sp_document_get_undo_sensitive(SP_DT_DOCUMENT(desktop))) {
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
+ prefs_set_string_attribute("tools.shapes.arc", "open", "true");
+ } else {
+ prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
+ }
+ }
+
+ // quit if run by the attr_changed listener
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent listener from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ bool modmade = FALSE;
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
+ for (GSList const *items = SP_DT_SELECTION(desktop)->itemList();
+ items != NULL;
+ items = items->next)
+ {
+ if (SP_IS_ARC((SPItem *) items->data)) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
+ repr->setAttribute("sodipodi:open", "true");
+ SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
+ modmade = true;
+ }
+ }
+ } else {
+ for (GSList const *items = SP_DT_SELECTION(desktop)->itemList();
+ items != NULL;
+ items = items->next)
+ {
+ if (SP_IS_ARC((SPItem *) items->data)) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
+ repr->setAttribute("sodipodi:open", NULL);
+ SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
+ modmade = true;
+ }
+ }
+ }
+
+ if (modmade) {
+ sp_document_done(SP_DT_DOCUMENT(desktop));
+ }
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void sp_arctb_defaults(GtkWidget *, GtkObject *obj)
+{
+ GtkWidget *tbl = GTK_WIDGET(obj);
+
+ GtkAdjustment *adj;
+ adj = (GtkAdjustment*)gtk_object_get_data(obj, "start");
+ gtk_adjustment_set_value(adj, 0.0);
+ gtk_adjustment_value_changed(adj);
+
+ adj = (GtkAdjustment*)gtk_object_get_data(obj, "end");
+ gtk_adjustment_set_value(adj, 0.0);
+ gtk_adjustment_value_changed(adj);
+
+ spinbutton_defocus(GTK_OBJECT(tbl));
+}
+
+static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
+ gchar const *old_value, gchar const *new_value,
+ bool is_interactive, gpointer data)
+{
+ GtkWidget *tbl = GTK_WIDGET(data);
+
+ // quit if run by the _changed callbacks
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+
+ // in turn, prevent callbacks from responding
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
+
+ gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
+ gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
+
+ GtkAdjustment *adj1,*adj2;
+ adj1 = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "start");
+ gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
+ adj2 = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "end");
+ gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
+
+ sp_arctb_sensitivize (tbl, adj1->value, adj2->value);
+
+ char const *openstr = NULL;
+ openstr = repr->attribute("sodipodi:open");
+ GtkWidget *ocb = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "open_checkbox");
+
+ if (openstr) {
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ocb), TRUE);
+ } else {
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ocb), FALSE);
+ }
+
+ g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+}
+
+static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
+ NULL, /* child_added */
+ NULL, /* child_removed */
+ arc_tb_event_attr_changed,
+ NULL, /* content_changed */
+ NULL /* order_changed */
+};
+
+
+static void
+sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GtkObject *tbl)
+{
+ int n_selected = 0;
+ Inkscape::XML::Node *repr = NULL;
+ Inkscape::XML::Node *oldrepr = NULL;
+
+ for (GSList const *items = selection->itemList();
+ items != NULL;
+ items = items->next)
+ {
+ if (SP_IS_ARC((SPItem *) items->data)) {
+ n_selected++;
+ repr = SP_OBJECT_REPR((SPItem *) items->data);
+ }
+ }
+
+ GtkWidget *l = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(tbl), "mode_label"));
+
+ g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(FALSE));
+ if (n_selected == 0) {
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>New:</b>"));
+ } else if (n_selected == 1) {
+ g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(TRUE));
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
+
+ oldrepr = (Inkscape::XML::Node *) gtk_object_get_data(GTK_OBJECT(tbl), "repr");
+
+ if (oldrepr) { // remove old listener
+ sp_repr_remove_listener_by_data(oldrepr, tbl);
+ Inkscape::GC::release(oldrepr);
+ oldrepr = 0;
+ g_object_set_data(G_OBJECT(tbl), "repr", NULL);
+ }
+
+ if (repr) {
+ g_object_set_data(G_OBJECT(tbl), "repr", repr);
+ Inkscape::GC::anchor(repr);
+ sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
+ sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
+ }
+ } else {
+ // FIXME: implement averaging of all parameters for multiple selected
+ //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
+ gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
+ sp_arctb_sensitivize (GTK_WIDGET(tbl), 1, 0);
+ }
+}
+
+
+static GtkWidget *
+sp_arc_toolbox_new(SPDesktop *desktop)
+{
+ GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
+
+ gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
+ gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
+
+ GtkTooltips *tt = gtk_tooltips_new();
+
+ sp_toolbox_add_label(tbl, _("<b>New:</b>"));
+
+ /* Start */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("Start:"), _("The angle (in degrees) from the horizontal to the arc's start point"),
+ "tools.shapes.arc", "start", 0.0,
+ NULL, tbl, TRUE, "altx-arc",
+ -360.0, 360.0, 1.0, 10.0,
+ sp_arctb_start_value_changed);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ /* End */
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_("End:"), _("The angle (in degrees) from the horizontal to the arc's end point"),
+ "tools.shapes.arc", "end", 0.0,
+ NULL, tbl, FALSE, NULL,
+ -360.0, 360.0, 1.0, 10.0,
+ sp_arctb_end_value_changed);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ /* Segments / Pie checkbox */
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *fscb = gtk_check_button_new_with_label(_("Open arc"));
+ gtk_tooltips_set_tip(tt, fscb, _("Switch between arc (unclosed shape) and segment (closed shape with two radii)"), NULL);
+
+ gchar const *openstr = NULL;
+ openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
+ if (!openstr || (openstr && !strcmp(openstr, "false")))
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(fscb), FALSE);
+ else
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(fscb), TRUE);
+
+ gtk_widget_show(fscb);
+ gtk_object_set_data(GTK_OBJECT(tbl), "open_checkbox", fscb);
+ gtk_container_add(GTK_CONTAINER(hb), fscb);
+ g_signal_connect(G_OBJECT(fscb), "toggled", GTK_SIGNAL_FUNC(sp_arctb_open_state_changed ), tbl);
+ gtk_box_pack_start(GTK_BOX(tbl),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ /* Make Whole */
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *b = gtk_button_new_with_label(_("Make whole"));
+ gtk_object_set_data(GTK_OBJECT(tbl), "make_whole", b);
+ gtk_tooltips_set_tip(tt, b, _("Make the shape a whole ellipse, not arc or segment"), NULL);
+ gtk_widget_show(b);
+ gtk_container_add(GTK_CONTAINER(hb), b);
+ gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_arctb_defaults), tbl);
+ gtk_box_pack_start(GTK_BOX(tbl),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(TRUE));
+ // sensitivize make whole and open checkbox
+ {
+ GtkAdjustment *adj1 = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "start"));
+ GtkAdjustment *adj2 = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "end"));
+ sp_arctb_sensitivize (tbl, adj1->value, adj2->value);
+ }
+
+ sigc::connection *connection = new sigc::connection(
+ SP_DT_SELECTION(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GtkObject *)tbl))
+ );
+ g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_connection), connection);
+
+ gtk_widget_show_all(tbl);
+ sp_set_font_size_smaller (tbl);
+
+ return tbl;
+}
+
+
+
+
+// toggle button callbacks and updaters
+
+//########################
+//## Dropper ##
+//########################
+
+static void toggle_dropper_color_pick (GtkWidget *button, gpointer data) {
+ prefs_set_int_attribute ("tools.dropper", "pick",
+ // 0 and 1 are backwards here because of pref
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
+}
+
+
+/**
+ * Copy the current saved desktop color to the clipboard as full hex + alpha
+ * color representation. This is useful for passing values between various
+ * input boxes, or directly to xml.
+ */
+/* static void
+sp_dropper_copy( GtkWidget *widget, GtkObject *obj)
+{
+ GtkWidget *tbl = GTK_WIDGET(obj);
+
+ SPDesktop *desktop =
+ (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
+
+
+ sp_dropper_c32_color_copy( sp_desktop_get_color(desktop, true) );
+}*/
+
+
+/**
+ * Copies currently saved desktop color to the clipboard as a hex value. This
+ * is useful for editing webpages and needing a value quickly for web
+ * colors.
+ *
+ * TODO: When the toggle of the dropper is set to not mix color against
+ * page background, this still just gets the color of the page and
+ * doesn't get the actual mixed against background which is needed
+ * for the hex value ppl. want for web pages, etc.
+ */
+
+/* static void
+sp_dropper_copy_as_hex ( GtkWidget *widget, GtkObject *obj)
+{
+ GtkWidget *tbl = GTK_WIDGET(obj);
+
+ SPDesktop *desktop =
+ (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
+
+ sp_dropper_c32_color_copy_hex( sp_desktop_get_color(desktop, true) );
+}*/
+
+
+/**
+ * Sets the input boxes with the changed color and opacity. This is used as a
+ * callback for style changing.
+ */
+/* static bool
+sp_style_changed (const SPCSSAttr *css, gpointer data)
+{
+ // GrDrag *drag = (GrDrag *) data;
+
+ // set fill of text entry box
+ if (css->attribute("fill"))
+ gtk_entry_set_text((GtkEntry *)dropper_rgb_entry,
+ css->attribute("fill"));
+
+ // set opacity of text entry box
+ if (css->attribute("fill-opacity"))
+ gtk_entry_set_text((GtkEntry *)dropper_opacity_entry,
+ css->attribute("fill-opacity"));
+
+ // set fill of text entry box
+ if (css->attribute("stroke"))
+ gtk_entry_set_text((GtkEntry *)dropper_rgb_entry,
+ css->attribute("stroke"));
+
+ // set opacity of text entry box
+ if (css->attribute("stroke-opacity"))
+ gtk_entry_set_text((GtkEntry *)dropper_opacity_entry,
+ css->attribute("stroke-opacity"));
+ return false;
+
+}
+*/
+
+
+/**
+ * Dropper auxiliary toolbar construction and setup.
+ *
+ * TODO: Would like to add swatch of current color.
+ * TODO: Add queue of last 5 or so colors selected with new swatches so that
+ * can drag and drop places. Will provide a nice mixing palette.
+ */
+static GtkWidget *
+sp_dropper_toolbox_new(SPDesktop *desktop)
+{
+ GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
+
+ gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
+ gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
+
+ GtkTooltips *tt = gtk_tooltips_new();
+
+
+ gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE,
+ AUX_BETWEEN_BUTTON_GROUPS);
+ // sp_toolbox_add_label(tbl, _("<b>New:</b>"));
+
+
+
+ /* RGB Input Field */
+ /* {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *dropper_rgba_label = gtk_label_new ("Color:");
+ gtk_widget_show (dropper_rgba_label);
+ gtk_container_add(GTK_CONTAINER(hb), dropper_rgba_label);
+
+ dropper_rgb_entry = gtk_entry_new ();
+ sp_dialog_defocus_on_enter (dropper_rgb_entry);
+ gtk_entry_set_max_length (GTK_ENTRY (dropper_rgb_entry), 7);
+ gtk_entry_set_width_chars (GTK_ENTRY (dropper_rgb_entry), 7);
+ gtk_tooltips_set_tip(tt, dropper_rgb_entry,
+ _("Hexidecimal representation of last selected "
+ "color"),
+ NULL);
+ gtk_widget_show (dropper_rgb_entry);
+ gtk_container_add(GTK_CONTAINER(hb), dropper_rgb_entry);
+
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
+ AUX_BETWEEN_BUTTON_GROUPS);
+ } */
+
+ /* Opacity Input Field */
+/* {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *dropper_opacity_label = gtk_label_new ( _("Opacity:") );
+ gtk_widget_show (dropper_opacity_label);
+ gtk_container_add(GTK_CONTAINER(hb), dropper_opacity_label);
+
+ dropper_opacity_entry = gtk_entry_new ();
+ sp_dialog_defocus_on_enter (dropper_opacity_entry);
+ gtk_entry_set_max_length (GTK_ENTRY (dropper_opacity_entry), 11);
+ gtk_entry_set_width_chars (GTK_ENTRY (dropper_opacity_entry), 11);
+ gtk_tooltips_set_tip(tt, dropper_opacity_entry,
+ _("Opacity of last selected color"),
+ NULL);
+ gtk_widget_show (dropper_opacity_entry);
+ gtk_container_add(GTK_CONTAINER(hb), dropper_opacity_entry);
+
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
+ AUX_BETWEEN_BUTTON_GROUPS);
+ } */
+
+
+ /* Copy to Clipboard */
+/* {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *b = gtk_button_new_with_label(_("Copy as RGBA"));
+ gtk_tooltips_set_tip(tt, b, _("Copy last saved color as hexidecimal "
+ "RGB + Alpha (RGBA) to "
+ "clipboard"),
+ NULL);
+ gtk_widget_show(b);
+ gtk_container_add(GTK_CONTAINER(hb), b);
+ gtk_signal_connect(GTK_OBJECT(b), "clicked",
+ GTK_SIGNAL_FUNC(sp_dropper_copy), tbl);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
+ AUX_BETWEEN_BUTTON_GROUPS);
+ } */
+
+
+ /* Copy to Clipboard as HEX */
+/* {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ GtkWidget *b = gtk_button_new_with_label(_("Copy as HEX"));
+ gtk_tooltips_set_tip(tt, b, _("Copy last saved color as "
+ "hexidecimal RGB without alpha "
+ "to clipboard"), NULL);
+ gtk_widget_show(b);
+ gtk_container_add(GTK_CONTAINER(hb), b);
+ gtk_signal_connect(GTK_OBJECT(b), "clicked",
+ GTK_SIGNAL_FUNC(sp_dropper_copy_as_hex), tbl);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
+ AUX_BETWEEN_BUTTON_GROUPS);
+ } */
+
+ // aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
+
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+
+ GtkWidget *button =
+ sp_button_new_from_data( GTK_ICON_SIZE_SMALL_TOOLBAR,
+ SP_BUTTON_TYPE_TOGGLE,
+ NULL,
+ "pick_color",
+ _("When pressed, picks visible color "
+ "without alpha and when not pressed, "
+ "picks color including its "
+ "alpha"),
+ tt);
+
+ gtk_widget_show(button);
+ gtk_container_add (GTK_CONTAINER (hb), button);
+
+ g_signal_connect_after (G_OBJECT (button), "clicked",
+ G_CALLBACK (toggle_dropper_color_pick), NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ !prefs_get_int_attribute ("tools.dropper",
+ "pick", 0));
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
+ AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
+
+
+ // where new gtkmm stuff should go
+
+ gtk_widget_show_all(tbl);
+ sp_set_font_size_smaller (tbl);
+
+ /*
+ sigc::connection *connection = new sigc::connection(
+ desktop->connectSetStyle(
+ sigc::bind(sigc::ptr_fun(sp_style_changed),
+ desktop)) );
+
+ g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_connection),
+ connection); */
+
+ return tbl;
+}
+
+
+//########################
+//## Text Toolbox ##
+//########################
+/*
+static void
+sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ //Call back for letter sizing spinbutton
+}
+
+static void
+sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ //Call back for line height spinbutton
+}
+
+static void
+sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ //Call back for horizontal kerning spinbutton
+}
+
+static void
+sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ //Call back for vertical kerning spinbutton
+}
+
+static void
+sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
+{
+ //Call back for letter rotation spinbutton
+}*/
+static GtkWidget *
+sp_text_toolbox_new(SPDesktop *desktop)
+{
+ GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
+/* GtkWidget *us = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(tbl), "units");
+ GtkTooltips *tt = gtk_tooltips_new();
+ GtkWidget *group;
+
+ //Font Family
+ {
+ GtkWidget *c = gtk_combo_new ();
+ gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
+ gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
+ gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
+ gtk_widget_set_size_request (c, 144, -1);
+ aux_toolbox_space(tbl, 1);
+ gtk_box_pack_start (GTK_BOX (tbl), c, FALSE, FALSE, 0);
+ }
+
+ //Font Style
+ {
+ GtkWidget *c = gtk_combo_new ();
+ gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
+ gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
+ gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
+ gtk_widget_set_size_request (c, 88, -1);
+ aux_toolbox_space(tbl, 1);
+ gtk_box_pack_start (GTK_BOX (tbl), c, FALSE, FALSE, 0);
+ }
+
+ //Font Size
+ {
+ GtkWidget *c = gtk_combo_new ();
+ gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
+ gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
+ gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
+ gtk_widget_set_size_request (c, 64, -1);
+ aux_toolbox_space(tbl, 1);
+ gtk_box_pack_start (GTK_BOX (tbl), c, FALSE, FALSE, 0);
+ }
+
+ aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
+ //Bold
+ {
+ GtkWidget *px = gtk_image_new_from_stock(GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ GtkWidget *button = gtk_toggle_button_new ();
+ gtk_container_add (GTK_CONTAINER (button), px);
+ gtk_widget_show(button);
+ gtk_tooltips_set_tip (tt, button, _("Bold"), NULL);
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_widget_set_sensitive(button, TRUE);
+ gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
+ }
+
+
+ //Italic
+ {
+ GtkWidget *px = gtk_image_new_from_stock(GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ GtkWidget *button = gtk_toggle_button_new ();
+ gtk_container_add (GTK_CONTAINER (button), px);
+ gtk_widget_show(button);
+ gtk_tooltips_set_tip (tt, button, _("Italics"), NULL);
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_widget_set_sensitive(button, TRUE);
+ gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
+ }
+
+ //Underline
+ {
+ GtkWidget *px = gtk_image_new_from_stock(GTK_STOCK_UNDERLINE, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ GtkWidget *button = gtk_toggle_button_new ();
+ gtk_container_add (GTK_CONTAINER (button), px);
+ gtk_widget_show(button);
+ gtk_tooltips_set_tip (tt, button, _("Underline"), NULL);
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_widget_set_sensitive(button, FALSE);
+ gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
+ }
+
+ aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
+ // align left
+ {
+ GtkWidget *px = gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ GtkWidget *b = group = gtk_radio_button_new (NULL);
+ gtk_container_add (GTK_CONTAINER (b), px);
+ gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
+ gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
+ gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
+ }
+
+ // align center
+ {
+ GtkWidget *px = gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
+ gtk_container_add (GTK_CONTAINER (b), px);
+ // TRANSLATORS: `Center' here is a verb.
+ gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
+ gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
+ gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
+ }
+
+ // align right
+ {
+ GtkWidget *px = gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
+ gtk_container_add (GTK_CONTAINER (b), px);
+ gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
+ gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
+ gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
+ }
+
+ // full justification
+ {
+ GtkWidget *px = gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
+ gtk_container_add (GTK_CONTAINER (b), px);
+ gtk_tooltips_set_tip (tt, b, _("Full justification"), NULL);
+ gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
+ gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
+ }
+
+
+ aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
+
+ // horizontal
+ {
+ GtkWidget *px= sp_icon_new(GTK_ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
+ GtkWidget *b = group = gtk_radio_button_new (NULL);
+ gtk_container_add (GTK_CONTAINER (b), px);
+ gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
+ gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
+ gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
+ }
+
+ // vertical
+ {
+ GtkWidget *px = sp_icon_new (GTK_ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
+ GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
+ gtk_container_add (GTK_CONTAINER (b), px);
+ gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
+ gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
+ gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
+ }
+
+ aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
+
+ // letter spacing
+ {
+ {
+ GtkWidget *image = sp_icon_new (GTK_ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ gtk_container_add (GTK_CONTAINER (hb), image);
+ gtk_widget_show(image);
+ gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
+ }
+
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between letters"),
+ "tools.text", "letter_spacing", 0.0,
+ us, tbl, FALSE, NULL,
+ -1000.0, 1000.0, 0.1, 0.1,
+ sp_text_letter_changed, 0.1, 1);
+ gtk_widget_set_size_request (hb, 45, 6);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
+ }
+ }
+
+ // line spacing
+ {
+ {
+ GtkWidget *image = sp_icon_new (GTK_ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ gtk_container_add (GTK_CONTAINER (hb), image);
+ gtk_widget_show(image);
+ gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
+ }
+
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between lines"),
+ "tools.text", "line_spacing", 0,
+ us, tbl, FALSE, NULL,
+ -1000.0, 1000.0, 0.1, 0.1,
+ sp_text_line_changed, 0.1, 1);
+ gtk_widget_set_size_request (hb, 45, 0);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
+ }
+ }
+
+{
+ // horizontal kerning/vertical kerning units menu: create
+ GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
+ sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
+ sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
+
+ aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
+
+ // horizontal kerning
+ {
+ {
+ GtkWidget *image = sp_icon_new (GTK_ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ gtk_container_add (GTK_CONTAINER (hb), image);
+ gtk_widget_show(image);
+ gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
+ }
+
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_(""), _("Horizontal kerning"),
+ "tools.text", "horizontal_kerning", 0,
+ us, tbl, FALSE, NULL,
+ -100.00, 100.00, 0.01, 0.1,
+ sp_text_horiz_kern_changed);
+ gtk_widget_set_size_request (hb, 45, 0);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
+ }
+ }
+
+ // vertical kerning
+ {
+ {
+ GtkWidget *image = sp_icon_new (GTK_ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ gtk_container_add (GTK_CONTAINER (hb), image);
+ gtk_widget_show(image);
+ gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
+ }
+
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_(""), _("Vertical kerning"),
+ "tools.text", "vertical_kerning", 0,
+ us, tbl, FALSE, NULL,
+ -100.00, 100.00, 0.01, 0.1,
+ sp_text_vert_kern_changed);
+ gtk_widget_set_size_request (hb, 45, 0);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
+ }
+ }
+
+ // add the units menu
+ gtk_widget_show(us);
+ gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
+ gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
+ }
+
+ // letter rotation
+ aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
+ {
+ {
+ GtkWidget *image = sp_icon_new (GTK_ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
+ GtkWidget *hb = gtk_hbox_new(FALSE, 1);
+ gtk_container_add (GTK_CONTAINER (hb), image);
+ gtk_widget_show(image);
+ gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
+ }
+ {
+ GtkWidget *hb = sp_tb_spinbutton(_(""), _("Letter rotation"),
+ "tools.text", "letter_rotation", 0,
+ us, tbl, FALSE, NULL,
+ -180.0, 180.0, 0.1, 0.1,
+ sp_text_letter_rotation_changed, 0.1, 1);
+ gtk_widget_set_size_request (hb, 45, 0);
+ gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
+ }
+ // rotation degree label
+ {
+ GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
+ gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
+ }
+ }
+
+ // Remove Manual Kerns
+ {
+ GtkWidget *px = sp_icon_new (GTK_ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
+ GtkWidget *button = gtk_button_new ();
+ gtk_container_add (GTK_CONTAINER (button), px);
+ gtk_widget_show(button);
+ gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_widget_set_sensitive(button, TRUE);
+ gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
+ }
+
+ gtk_widget_show_all(tbl);
+ sp_set_font_size_smaller (tbl);
+
+*/ return tbl;
+
+} // end of sp_text_toolbox_new()
+
+
+//#########################
+//## Connector Toolbox ##
+//#########################
+
+static void sp_connector_path_set_avoid(void)
+{
+ cc_selection_set_avoid(true);
+}
+
+
+static void sp_connector_path_set_ignore(void)
+{
+ cc_selection_set_avoid(false);
+}
+
+
+static GtkWidget *
+sp_connector_toolbox_new(SPDesktop *desktop)
+{
+ GtkTooltips *tt = gtk_tooltips_new();
+ GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
+
+ gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE,
+ AUX_BETWEEN_BUTTON_GROUPS);
+
+ sp_toolbox_button_new(tbl, GTK_ICON_SIZE_SMALL_TOOLBAR,
+ "connector_avoid", GTK_SIGNAL_FUNC(sp_connector_path_set_avoid),
+ tt, _("Make connectors avoid selected objects"));
+
+ sp_toolbox_button_new(tbl, GTK_ICON_SIZE_SMALL_TOOLBAR,
+ "connector_ignore", GTK_SIGNAL_FUNC(sp_connector_path_set_ignore),
+ tt, _("Make connectors ignore selected objects"));
+
+ gtk_widget_show_all(tbl);
+
+ return tbl;
+
+} // end of sp_connector_toolbox_new()
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+
diff --git a/src/widgets/toolbox.h b/src/widgets/toolbox.h
new file mode 100644
index 000000000..c6ae2a6f6
--- /dev/null
+++ b/src/widgets/toolbox.h
@@ -0,0 +1,57 @@
+#ifndef SEEN_TOOLBOX_H
+#define SEEN_TOOLBOX_H
+
+/**
+ * \brief Main toolbox
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Frank Felfe <innerspace@iname.com>
+ *
+ * Copyright (C) 1999-2002 Authors
+ * Copyright (C) 2001-2002 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <gtk/gtkstyle.h>
+#include <gtk/gtktooltips.h>
+
+#include "forward.h"
+
+GtkWidget *sp_tool_toolbox_new(void);
+void sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop);
+
+GtkWidget *sp_aux_toolbox_new(void);
+void sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop);
+
+GtkWidget *sp_commands_toolbox_new(void);
+void sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop);
+
+void show_aux_toolbox(GtkWidget *toolbox);
+
+GtkWidget *sp_toolbox_button_normal_new_from_verb(GtkWidget *t,
+ GtkIconSize size,
+ Inkscape::Verb * verb,
+ Inkscape::UI::View::View *view,
+ GtkTooltips *tt);
+
+void aux_toolbox_space(GtkWidget *tb, gint space);
+
+// utility
+void sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide = true);
+
+
+
+#endif /* !SEEN_TOOLBOX_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/widget-sizes.h b/src/widgets/widget-sizes.h
new file mode 100644
index 000000000..7a5aab2fb
--- /dev/null
+++ b/src/widgets/widget-sizes.h
@@ -0,0 +1,52 @@
+// #define TOOL_BUTTON_SIZE 28
+
+// GTK uses 24 for icon sizes by default. Spacing adjust to keep the
+// toolbar the same as other GTK applications. If we want that, use
+// these defines instead:
+//#define AUX_BUTTON_SIZE 24
+//#define AUX_SPACING 2
+
+// #define AUX_BUTTON_SIZE 20
+#define AUX_SPACING 3
+
+#define AUX_BETWEEN_BUTTON_GROUPS 7
+#define AUX_BETWEEN_SPINBUTTONS 0
+#define AUX_SPINBUTTON_WIDTH 62
+#define AUX_SPINBUTTON_WIDTH_SMALL 56
+#define AUX_SPINBUTTON_HEIGHT 20
+#define AUX_OPTION_MENU_WIDTH 55
+#define AUX_OPTION_MENU_HEIGHT 20
+#define AUX_MENU_ITEM_WIDTH 32
+#define AUX_MENU_ITEM_HEIGHT 18
+
+#define SPIN_STEP 0.1
+#define SPIN_PAGE_STEP 5.0
+
+#define BOTTOM_BAR_HEIGHT 20
+#define BOTTOM_BUTTON_SIZE 14
+
+#define STATUS_BAR_FONT_SIZE 10000
+
+#define STATUS_ZOOM_WIDTH 57
+
+#define SELECTED_STYLE_SB_WIDTH 40
+#define SELECTED_STYLE_WIDTH 150
+#define STYLE_SWATCH_WIDTH 100
+
+#define STATUS_LAYER_FONT_SIZE 7700
+
+#define RULER_WIDTH 12
+#define RULER_HEIGHT 12
+#define RULER_FONT_SIZE 6500
+#define RULER_FONT_VERTICAL_SPACING 1.2
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :