From 179fa413b047bede6e32109e2ce82437c5fb8d34 Mon Sep 17 00:00:00 2001 From: MenTaLguY Date: Mon, 16 Jan 2006 02:36:01 +0000 Subject: moving trunk for module inkscape (bzr r1) --- src/widgets/.cvsignore | 5 + src/widgets/Makefile_insert | 75 + src/widgets/button.cpp | 324 ++++ src/widgets/button.h | 69 + src/widgets/dash-selector.cpp | 379 ++++ src/widgets/dash-selector.h | 48 + src/widgets/desktop-widget.cpp | 1270 +++++++++++++ src/widgets/desktop-widget.h | 201 ++ src/widgets/font-selector.cpp | 722 ++++++++ src/widgets/font-selector.h | 67 + src/widgets/gradient-image.cpp | 277 +++ src/widgets/gradient-image.h | 46 + src/widgets/gradient-selector.cpp | 342 ++++ src/widgets/gradient-selector.h | 79 + src/widgets/gradient-toolbar.cpp | 665 +++++++ src/widgets/gradient-toolbar.h | 21 + src/widgets/gradient-vector.cpp | 1115 ++++++++++++ src/widgets/gradient-vector.h | 61 + src/widgets/icon.cpp | 924 ++++++++++ src/widgets/icon.h | 51 + src/widgets/layer-selector.cpp | 588 ++++++ src/widgets/layer-selector.h | 110 ++ src/widgets/makefile.in | 17 + src/widgets/paint-selector.cpp | 963 ++++++++++ src/widgets/paint-selector.h | 110 ++ src/widgets/ruler.cpp | 663 +++++++ src/widgets/ruler.h | 84 + src/widgets/select-toolbar.cpp | 540 ++++++ src/widgets/select-toolbar.h | 34 + src/widgets/shrink-wrap-button.cpp | 55 + src/widgets/shrink-wrap-button.h | 35 + src/widgets/sp-color-gtkselector.cpp | 170 ++ src/widgets/sp-color-gtkselector.h | 55 + src/widgets/sp-color-notebook.cpp | 625 +++++++ src/widgets/sp-color-notebook.h | 105 ++ src/widgets/sp-color-preview.cpp | 196 ++ src/widgets/sp-color-preview.h | 47 + src/widgets/sp-color-scales.cpp | 741 ++++++++ src/widgets/sp-color-scales.h | 108 ++ src/widgets/sp-color-selector.cpp | 351 ++++ src/widgets/sp-color-selector.h | 95 + src/widgets/sp-color-slider.cpp | 687 +++++++ src/widgets/sp-color-slider.h | 67 + src/widgets/sp-color-wheel-selector.cpp | 295 +++ src/widgets/sp-color-wheel-selector.h | 90 + src/widgets/sp-color-wheel.cpp | 1163 ++++++++++++ src/widgets/sp-color-wheel.h | 80 + src/widgets/sp-widget.cpp | 260 +++ src/widgets/sp-widget.h | 52 + src/widgets/sp-xmlview-attr-list.cpp | 181 ++ src/widgets/sp-xmlview-attr-list.h | 54 + src/widgets/sp-xmlview-content.cpp | 165 ++ src/widgets/sp-xmlview-content.h | 53 + src/widgets/sp-xmlview-tree.cpp | 424 +++++ src/widgets/sp-xmlview-tree.h | 55 + src/widgets/spinbutton-events.cpp | 137 ++ src/widgets/spinbutton-events.h | 30 + src/widgets/spw-utilities.cpp | 255 +++ src/widgets/spw-utilities.h | 56 + src/widgets/toolbox.cpp | 3028 +++++++++++++++++++++++++++++++ src/widgets/toolbox.h | 57 + src/widgets/widget-sizes.h | 52 + 62 files changed, 19674 insertions(+) create mode 100644 src/widgets/.cvsignore create mode 100644 src/widgets/Makefile_insert create mode 100644 src/widgets/button.cpp create mode 100644 src/widgets/button.h create mode 100644 src/widgets/dash-selector.cpp create mode 100644 src/widgets/dash-selector.h create mode 100644 src/widgets/desktop-widget.cpp create mode 100644 src/widgets/desktop-widget.h create mode 100644 src/widgets/font-selector.cpp create mode 100644 src/widgets/font-selector.h create mode 100644 src/widgets/gradient-image.cpp create mode 100644 src/widgets/gradient-image.h create mode 100644 src/widgets/gradient-selector.cpp create mode 100644 src/widgets/gradient-selector.h create mode 100644 src/widgets/gradient-toolbar.cpp create mode 100644 src/widgets/gradient-toolbar.h create mode 100644 src/widgets/gradient-vector.cpp create mode 100644 src/widgets/gradient-vector.h create mode 100644 src/widgets/icon.cpp create mode 100644 src/widgets/icon.h create mode 100644 src/widgets/layer-selector.cpp create mode 100644 src/widgets/layer-selector.h create mode 100644 src/widgets/makefile.in create mode 100644 src/widgets/paint-selector.cpp create mode 100644 src/widgets/paint-selector.h create mode 100644 src/widgets/ruler.cpp create mode 100644 src/widgets/ruler.h create mode 100644 src/widgets/select-toolbar.cpp create mode 100644 src/widgets/select-toolbar.h create mode 100644 src/widgets/shrink-wrap-button.cpp create mode 100644 src/widgets/shrink-wrap-button.h create mode 100644 src/widgets/sp-color-gtkselector.cpp create mode 100644 src/widgets/sp-color-gtkselector.h create mode 100644 src/widgets/sp-color-notebook.cpp create mode 100644 src/widgets/sp-color-notebook.h create mode 100644 src/widgets/sp-color-preview.cpp create mode 100644 src/widgets/sp-color-preview.h create mode 100644 src/widgets/sp-color-scales.cpp create mode 100644 src/widgets/sp-color-scales.h create mode 100644 src/widgets/sp-color-selector.cpp create mode 100644 src/widgets/sp-color-selector.h create mode 100644 src/widgets/sp-color-slider.cpp create mode 100644 src/widgets/sp-color-slider.h create mode 100644 src/widgets/sp-color-wheel-selector.cpp create mode 100644 src/widgets/sp-color-wheel-selector.h create mode 100644 src/widgets/sp-color-wheel.cpp create mode 100644 src/widgets/sp-color-wheel.h create mode 100644 src/widgets/sp-widget.cpp create mode 100644 src/widgets/sp-widget.h create mode 100644 src/widgets/sp-xmlview-attr-list.cpp create mode 100644 src/widgets/sp-xmlview-attr-list.h create mode 100644 src/widgets/sp-xmlview-content.cpp create mode 100644 src/widgets/sp-xmlview-content.h create mode 100644 src/widgets/sp-xmlview-tree.cpp create mode 100644 src/widgets/sp-xmlview-tree.h create mode 100644 src/widgets/spinbutton-events.cpp create mode 100644 src/widgets/spinbutton-events.h create mode 100644 src/widgets/spw-utilities.cpp create mode 100644 src/widgets/spw-utilities.h create mode 100644 src/widgets/toolbox.cpp create mode 100644 src/widgets/toolbox.h create mode 100644 src/widgets/widget-sizes.h (limited to 'src/widgets') 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 +# + +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 + * Lauris Kaplinski + * bulia byak + * + * 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 + * + * 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 +#include +#include + +#include + +#include + + + +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 + * bulia byak + * + * 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 +#include + +#include +#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 + * + * Copyright (C) 2002 Lauris Kaplinski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +struct SPDashSelector; + +#include +#include "../xml/repr.h" + +#include + + + +#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 + * MenTaLguY + * bulia byak + * Ralf Stephan + * + * 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 +#include + +#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 +#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 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), _("Welcome to Inkscape! 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( + _("Save changes to document \"%s\" before closing?\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( + _("The file \"%s\" was saved with a format (%s) that may cause data loss!\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(data), 0.5); +} + +static void +sp_dtw_zoom_100 (GtkMenuItem *item, gpointer data) +{ + sp_dtw_zoom_menu_handler (static_cast(data), 1.0); +} + +static void +sp_dtw_zoom_200 (GtkMenuItem *item, gpointer data) +{ + sp_dtw_zoom_menu_handler (static_cast(data), 2.0); +} + +static void +sp_dtw_zoom_page (GtkMenuItem *item, gpointer data) +{ + static_cast(data)->zoom_page(); +} + +static void +sp_dtw_zoom_drawing (GtkMenuItem *item, gpointer data) +{ + static_cast(data)->zoom_drawing(); +} + +static void +sp_dtw_zoom_selection (GtkMenuItem *item, gpointer data) +{ + static_cast(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 (c) 2005, distrib. under GPL2 + * ? -2004 + */ + +#include +#include + +#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((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 + * Lauris Kaplinski + * bulia byak + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../display/nr-plain-stuff-gdk.h" +#include + +#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 (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 (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(fsel->styleidx) < fsel->styles.length + && static_cast(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 + * Lauris Kaplinski + * + * Copyright (C) 1999-2001 Ximian, Inc. + * Copyright (C) 2002 Lauris Kaplinski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +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 +#include + +/* 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 + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * Copyright (C) 2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#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 + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * Copyright (C) 2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include "../libnr/nr-matrix.h" +class SPGradient; + +#include + + +#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 + * bulia byak + * + * 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 +#include +#include +#include +#include + +#include "../document-private.h" +#include "../gradient-chemistry.h" + +#include +#include + +#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 + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * Copyright (C) 2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#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 +#include +#include "../forward.h" +#include + +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 + * + * Copyright (C) 2005 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#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 + +#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 ("#%s", id+15); + return g_strdup_printf ("%s", 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), _("No gradients")); + 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), _("Nothing selected")); + 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), _("No gradients in selection")); + 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), _("Multiple gradients")); + 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, _("New:")); + + 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, _("Change:")); + + 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 + * + * Copyright (C) 2005 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +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 + * bulia byak + * + * 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 +#include "macros.h" +#include +#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(g_object_get_data(G_OBJECT(widget), "preview")); + sp_gradient_image_set_gradient (SP_GRADIENT_IMAGE (w), gradient); + + GtkWidget *mnu = static_cast(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 + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * Copyright (C) 2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + + + +#include +#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 + * Jon A. Cruz + * + * 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 +#include +#include + +#include + +#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(icon->psize) ); + int const y1 = std::min(area->y + area->height, widget.allocation.y + pady + static_cast(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(gdk_pixbuf_get_width(pb)) != psize ) + || ( static_cast(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(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 doc_cache; +static std::map 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::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 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::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 + +static std::queue 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 + * + * Copyright (C) 2002 Lauris Kaplinski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + + + +#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 + +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 +#include + +// 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 + * + * Copyright (C) 2004 MenTaLguY + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#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 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 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 hierarchy=reverse_list(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 hierarchy) +{ + using Inkscape::Util::List; + using Inkscape::Util::rest; + + _buildEntry(depth, *hierarchy); + + List 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 hierarchy +) { + using Inkscape::Util::List; + using Inkscape::Util::rest; + using Inkscape::Util::reverse_list_in_place; + using Inkscape::Util::filter_list; + + Inkscape::Util::List siblings( + reverse_list_in_place( + filter_list( + 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 update_row; + sigc::slot 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(data)->update_list(); + } else { + reinterpret_cast(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(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(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(data)->update_list(); + } +} + +void update_row_for_object(SPObject *object, + Gtk::TreeModelColumn const &column, + Glib::RefPtr 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 rebuild, SPDesktop *desktop) +{ + rebuild(desktop->currentLayer()); +} + +} + +void LayerSelector::_protectUpdate(sigc::slot 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(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(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 ( + "%*s%s%s%s%s%%s%s%s%s", + ( _desktop && _desktop->itemIsHidden (SP_ITEM(object)) ? "foreground=\"gray50\"" : "" ), + depth, "", ( iscurrent ? "•" : " " ), + ( iscurrent ? "" : "" ), + ( SP_ITEM(object)->isLocked() ? "[" : "" ), + ( isancestor ? "" : "" ), + ( isancestor ? "" : "" ), + ( SP_ITEM(object)->isLocked() ? "]" : "" ), + ( iscurrent ? "" : "" ) + ); + + 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() = " "; + } + + _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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#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 depth; + Gtk::TreeModelColumn object; + Gtk::TreeModelColumn repr; + Gtk::TreeModelColumn 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 _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 hierarchy); + void _buildSiblingEntries(unsigned depth, + SPObject &parent, + Inkscape::Util::List hierarchy); + void _protectUpdate(sigc::slot 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 +*/ + +#define noSP_PS_VERBOSE + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + + +#include +#include +#include +#include +#include +#include +#include + +#include "../sp-pattern.h" +#include +#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 +#include +#include +#include +#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 Edit > Object(s) to Pattern 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 +#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 + +#include "../forward.h" +#include +#include + +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 + * Frank Felfe + * bulia byak + * + * Copyright (C) 1999-2005 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include +#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(&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 + * Frank Felfe + * + * Copyright (C) 1999-2002 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include "sp-metric.h" + +#include + + + +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 + * bulia byak + * + * 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 + +#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 +#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 stroke width is scaled when objects are scaled.")); + } else { + desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now stroke width is not scaled 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 rounded rectangle corners are scaled when rectangles are scaled.")); + } else { + desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now rounded rectangle corners are not scaled 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 gradients are transformed along with their objects when those are transformed (moved, scaled, rotated, or skewed).")); + } else { + desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now gradients remain fixed 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 patterns are transformed along with their objects when those are transformed (moved, scaled, rotated, or skewed).")); + } else { + desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now patterns remain fixed 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 + * bulia byak + * + * Copyright (C) 2003 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +#include + +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 + * + * Copyright (C) 2004 MenTaLguY + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include + +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 + * + * 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 +#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 +#include "../color.h" +#include "sp-color-selector.h" + +#include + + + +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 + * bulia byak + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * + * This code is in public domain + */ + +#undef SPCS_PREVIEW + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#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(GTK_EXPAND|GTK_FILL), + static_cast(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 + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * + * This code is in public domain + */ + +#include +#include +#include +#include "../color.h" +#include "sp-color-selector.h" + +#include + + + +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 + * + * 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 + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * Copyright (C) 2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +#include + + + +#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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#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(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 +#include +#include + +#include +#include +#include + + +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 +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#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 +#include "../color.h" + +#include + + + + +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 + * bulia byak + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * + * This code is in public domain + */ + +#include +#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 + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * + * This code is in public domain + */ + +#include + +#include + + + +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 +#include +#include +#include +#include +#include +#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 +#include +#include + +#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 + * Jon A. Cruz + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * Copyright (C) 2003-2004 Authors + * + * This code is in public domain + */ + +#include +#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(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(dx * wheel->_inner); + wheel->_triPoints[0].y = wheel->_center + static_cast(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(dx * wheel->_inner); + wheel->_triPoints[1].y = wheel->_center + static_cast(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(dx * wheel->_inner); + wheel->_triPoints[2].y = wheel->_center + static_cast(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(pointP.y) * rowStride); + dst += static_cast(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(pointP.y) * rowStride); + dst += static_cast(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(pointP.y) * rowStride); + dst += static_cast(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(pointP.y) * rowStride); + dst += static_cast(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(pointP.y) * rowStride); + dst += static_cast(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(dx * wheel->_inner); + gint iny = wheel->_center + static_cast(dy * wheel->_inner); + + + gdk_draw_line (widget->window, line_gc, + inx, iny, + wheel->_center + static_cast(dx * wheel->_center), wheel->_center + static_cast(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(dy), static_cast(-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 + * Jon A. Cruz + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * Copyright (C) 2001-2004 Authors + * + * This code is in public domain + */ + +#include + +#include +#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 + * bulia byak + * + * 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 + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +#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 + +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 + * + * Copyright (C) 2002 MenTaLguY + * + * Released under the GNU GPL; see COPYING for details + */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "helper/sp-marshal.h" +#include +#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 + * + * Copyright (C) 2002 MenTaLguY + * + * Released under the GNU GPL; see COPYING for details + */ + +#include +#include +#include "../xml/repr.h" + +#include + + + +#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 + * + * 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 + * + * Copyright (C) 2002 MenTaLguY + * + * Released under the GNU GPL; see COPYING for details + */ + +#include + +#include + +#include +#include "../xml/repr.h" + +#include + + + +#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 + * + * 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 ("", 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 + * + * Copyright (C) 2002 MenTaLguY + * + * Released under the GNU GPL; see COPYING for details + */ + +#include +#include "../xml/repr.h" + +#include + + + +#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 + * + * Copyright (C) 2003 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#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 + * + * Copyright (C) 2003 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include /* GtkWidget */ +#include /* 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 + * bulia byak + * + * 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 + +#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 + * + * 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 +#include /* 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 +* Lauris Kaplinski +* bulia byak +* Frank Felfe +* John Cliff +* David Turner +* Josh Andler +* +* 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 + +#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 +#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(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(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(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(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), _("New:")); + } else if (n_selected == 1) { + gtk_label_set_markup(GTK_LABEL(l), _("Change:")); + + 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), _("Average:")); + gtk_label_set_markup(GTK_LABEL(l), _("Change:")); + } +} + + +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, _("New:")); + + 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), _("New:")); + + 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), _("Change:")); + 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), _("Average:")); + gtk_label_set_markup(GTK_LABEL(l), _("Change:")); + 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, _("New:")); + + // 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), _("New:")); + } else if (n_selected == 1) { + gtk_label_set_markup(GTK_LABEL(l), _("Change:")); + + 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), _("Average:")); + gtk_label_set_markup(GTK_LABEL(l), _("Change:")); + } +} + + +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, _("New:")); + + /* 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(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), _("New:")); + } else if (n_selected == 1) { + g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(TRUE)); + gtk_label_set_markup(GTK_LABEL(l), _("Change:")); + + 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), _("Average:")); + gtk_label_set_markup(GTK_LABEL(l), _("Change:")); + 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, _("New:")); + + /* 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, _("New:")); + + + + /* 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 + * Frank Felfe + * + * Copyright (C) 1999-2002 Authors + * Copyright (C) 2001-2002 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include + +#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 : -- cgit v1.2.3