summaryrefslogtreecommitdiffstats
path: root/src/widgets
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2009-02-21 01:59:56 +0000
committertweenk <tweenk@users.sourceforge.net>2009-02-21 01:59:56 +0000
commit154165799998cb2cb7491bbd97b0511943a0228e (patch)
tree878507e5e42b4651f1bb0805a42c0837528eb314 /src/widgets
parentOnly build static libraries for subdirs than actually contain libraries, (diff)
downloadinkscape-154165799998cb2cb7491bbd97b0511943a0228e.tar.gz
inkscape-154165799998cb2cb7491bbd97b0511943a0228e.zip
Move files from the src/dialogs/ directory to the places where they
should be. Build libinkscape.a - should reduce link time. (bzr r7337)
Diffstat (limited to 'src/widgets')
-rw-r--r--src/widgets/Makefile_insert14
-rw-r--r--src/widgets/calligraphic-profile-rename.cpp108
-rw-r--r--src/widgets/calligraphic-profile-rename.h75
-rw-r--r--src/widgets/desktop-widget.cpp46
-rw-r--r--src/widgets/eek-color-def.cpp147
-rw-r--r--src/widgets/eek-color-def.h102
-rw-r--r--src/widgets/eek-preview.cpp736
-rw-r--r--src/widgets/eek-preview.h152
-rw-r--r--src/widgets/fill-style.cpp552
-rw-r--r--src/widgets/fill-style.h33
-rw-r--r--src/widgets/layer-selector.cpp611
-rw-r--r--src/widgets/layer-selector.h110
-rw-r--r--src/widgets/sp-attribute-widget.cpp790
-rw-r--r--src/widgets/sp-attribute-widget.h130
-rw-r--r--src/widgets/stroke-style.cpp1838
-rw-r--r--src/widgets/stroke-style.h35
-rw-r--r--src/widgets/toolbox.cpp2
17 files changed, 4549 insertions, 932 deletions
diff --git a/src/widgets/Makefile_insert b/src/widgets/Makefile_insert
index 32a8a71e5..13f2b33eb 100644
--- a/src/widgets/Makefile_insert
+++ b/src/widgets/Makefile_insert
@@ -3,12 +3,16 @@
ink_common_sources += \
widgets/button.cpp \
widgets/button.h \
- widgets/calligraphic-profile-rename.cpp \
- widgets/calligraphic-profile-rename.h \
widgets/dash-selector.cpp \
widgets/dash-selector.h \
widgets/desktop-widget.cpp \
widgets/desktop-widget.h \
+ widgets/eek-color-def.cpp \
+ widgets/eek-color-def.h \
+ widgets/eek-preview.cpp \
+ widgets/eek-preview.h \
+ widgets/fill-style.cpp \
+ widgets/fill-style.h \
widgets/font-selector.cpp \
widgets/font-selector.h \
widgets/gradient-image.cpp \
@@ -21,8 +25,6 @@ ink_common_sources += \
widgets/gradient-vector.h \
widgets/icon.cpp \
widgets/icon.h \
- widgets/layer-selector.cpp \
- widgets/layer-selector.h \
widgets/paint-selector.cpp \
widgets/paint-selector.h \
widgets/ruler.cpp \
@@ -31,6 +33,8 @@ ink_common_sources += \
widgets/select-toolbar.h \
widgets/shrink-wrap-button.cpp \
widgets/shrink-wrap-button.h \
+ widgets/sp-attribute-widget.cpp \
+ widgets/sp-attribute-widget.h \
widgets/sp-color-gtkselector.cpp \
widgets/sp-color-gtkselector.h \
widgets/sp-color-icc-selector.cpp \
@@ -61,6 +65,8 @@ ink_common_sources += \
widgets/sp-xmlview-content.h \
widgets/sp-xmlview-tree.cpp \
widgets/sp-xmlview-tree.h \
+ widgets/stroke-style.cpp \
+ widgets/stroke-style.h \
widgets/toolbox.cpp \
widgets/toolbox.h \
widgets/widget-sizes.h
diff --git a/src/widgets/calligraphic-profile-rename.cpp b/src/widgets/calligraphic-profile-rename.cpp
deleted file mode 100644
index 888b327f4..000000000
--- a/src/widgets/calligraphic-profile-rename.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/** @file
- * @brief Dialog for naming calligraphic profiles
- *
- * @note This file is in the wrong directory because of link order issues -
- * it is required by widgets/toolbox.cpp, and libspwidgets.a comes after
- * libinkdialogs.a in the current link order.
- */
-/* Author:
- * Aubanel MONNIER
- *
- * Copyright (C) 2007 Authors
- * Released under GNU GPL. Read the file 'COPYING' for more information
- */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <glibmm/i18n.h>
-#include <gtkmm/stock.h>
-
-#include "desktop.h"
-#include "calligraphic-profile-rename.h"
-
-namespace Inkscape {
-namespace UI {
-namespace Dialog {
-
-CalligraphicProfileRename::CalligraphicProfileRename() :
- _applied(false)
-{
- Gtk::VBox *mainVBox = get_vbox();
- _layout_table.set_spacings(4);
- _layout_table.resize (1, 2);
-
- _profile_name_entry.set_activates_default(true);
-
- _profile_name_label.set_label(_("Profile name:"));
- _profile_name_label.set_alignment(1.0, 0.5);
-
- _layout_table.attach(_profile_name_label,
- 0, 1, 0, 1, Gtk::FILL, Gtk::FILL);
- _layout_table.attach(_profile_name_entry,
- 1, 2, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL);
- mainVBox->pack_start(_layout_table, false, false, 4);
- // Buttons
- _close_button.set_use_stock(true);
- _close_button.set_label(Gtk::Stock::CANCEL.id);
- _close_button.set_flags(Gtk::CAN_DEFAULT);
-
- _apply_button.set_use_underline(true);
- _apply_button.set_label(_("Save"));
- _apply_button.set_flags(Gtk::CAN_DEFAULT);
-
- _close_button.signal_clicked()
- .connect(sigc::mem_fun(*this, &CalligraphicProfileRename::_close));
- _apply_button.signal_clicked()
- .connect(sigc::mem_fun(*this, &CalligraphicProfileRename::_apply));
-
- signal_delete_event().connect( sigc::bind_return(
- sigc::hide(sigc::mem_fun(*this, &CalligraphicProfileRename::_close)), true ) );
-
- add_action_widget(_close_button, Gtk::RESPONSE_CLOSE);
- add_action_widget(_apply_button, Gtk::RESPONSE_APPLY);
-
- _apply_button.grab_default();
-
- show_all_children();
-}
-
-void CalligraphicProfileRename::_apply()
-{
- _profile_name = _profile_name_entry.get_text();
- _applied = true;
- _close();
-}
-
-void CalligraphicProfileRename::_close()
-{
- this->Gtk::Dialog::hide();
-}
-
-void CalligraphicProfileRename::show(SPDesktop *desktop)
-{
- CalligraphicProfileRename &dial = instance();
- dial._applied=false;
- dial.set_modal(true);
- desktop->setWindowTransient (dial.gobj());
- dial.property_destroy_with_parent() = true;
- // dial.Gtk::Dialog::show();
- //dial.present();
- dial.run();
-}
-
-} // namespace Dialog
-} // namespace UI
-} // namespace Inkscape
-
-/*
- 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/calligraphic-profile-rename.h b/src/widgets/calligraphic-profile-rename.h
deleted file mode 100644
index 53ce907ed..000000000
--- a/src/widgets/calligraphic-profile-rename.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/** @file
- * @brief Dialog for naming calligraphic profiles
- */
-/* Author:
- * Aubanel MONNIER
- *
- * Copyright (C) 2007 Authors
- * Released under GNU GPL. Read the file 'COPYING' for more information
- */
-
-#ifndef INKSCAPE_DIALOG_CALLIGRAPHIC_PROFILE_H
-#define INKSCAPE_DIALOG_CALLIGRAPHIC_PROFILE_H
-
-#include <gtkmm/dialog.h>
-#include <gtkmm/entry.h>
-#include <gtkmm/label.h>
-#include <gtkmm/table.h>
-struct SPDesktop;
-
-namespace Inkscape {
-namespace UI {
-namespace Dialog {
-
-class CalligraphicProfileRename : public Gtk::Dialog {
-public:
- CalligraphicProfileRename();
- virtual ~CalligraphicProfileRename() {}
- Glib::ustring getName() const {
- return "CalligraphicProfileRename";
- }
-
- static void show(SPDesktop *desktop);
- static bool applied() {
- return instance()._applied;
- }
- static Glib::ustring getProfileName() {
- return instance()._profile_name;
- }
-
-protected:
- void _close();
- void _apply();
-
- Gtk::Label _profile_name_label;
- Gtk::Entry _profile_name_entry;
- Gtk::Table _layout_table;
- Gtk::Button _close_button;
- Gtk::Button _apply_button;
- Glib::ustring _profile_name;
- bool _applied;
-private:
- static CalligraphicProfileRename &instance() {
- static CalligraphicProfileRename instance_;
- return instance_;
- }
- CalligraphicProfileRename(CalligraphicProfileRename const &); // no copy
- CalligraphicProfileRename &operator=(CalligraphicProfileRename const &); // no assign
-};
-
-} // namespace Dialog
-} // namespace UI
-} // namespace Inkscape
-
-#endif // INKSCAPE_DIALOG_CALLIGRAPHIC_PROFILE_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/desktop-widget.cpp b/src/widgets/desktop-widget.cpp
index 324e5b008..356a09418 100644
--- a/src/widgets/desktop-widget.cpp
+++ b/src/widgets/desktop-widget.cpp
@@ -27,39 +27,39 @@
#include <gtkmm/paned.h>
#include <gtk/gtk.h>
-#include "macros.h"
-#include "inkscape-private.h"
-#include "desktop-handles.h"
+#include "box3d-context.h"
+#include "color-profile-fns.h"
+#include "conn-avoid-ref.h"
#include "desktop-events.h"
-#include "document.h"
+#include "desktop-handles.h"
#include "desktop-widget.h"
-#include "sp-namedview.h"
-#include "interface.h"
-#include "toolbox.h"
-#include "preferences.h"
-#include "file.h"
#include "display/canvas-arena.h"
#include "display/nr-arena.h"
+#include "document.h"
+#include "ege-color-prof-tracker.h"
+#include "ege-select-one-action.h"
#include <extension/db.h>
+#include "file.h"
#include "helper/units.h"
+#include "inkscape-private.h"
+#include "interface.h"
+#include "macros.h"
+#include "preferences.h"
+#include "sp-image.h"
+#include "sp-item.h"
+#include "sp-namedview.h"
+#include "toolbox.h"
+#include "ui/dialog/dialog-manager.h"
+#include "ui/dialog/swatches.h"
+#include "ui/widget/dock.h"
+#include "ui/widget/layer-selector.h"
+#include "ui/widget/selected-style.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 "widgets/spw-utilities.h"
#include "widgets/toolbox.h"
-#include "ui/dialog/dialog-manager.h"
-#include "ui/widget/dock.h"
-#include "ui/widget/selected-style.h"
-#include "sp-item.h"
-#include "dialogs/swatches.h"
-#include "conn-avoid-ref.h"
-#include "ege-select-one-action.h"
-#include "ege-color-prof-tracker.h"
-#include "color-profile-fns.h"
-#include "box3d-context.h"
-#include "sp-image.h"
+#include "widgets/widget-sizes.h"
#if defined (SOLARIS) && (SOLARIS == 8)
#include "round.h"
diff --git a/src/widgets/eek-color-def.cpp b/src/widgets/eek-color-def.cpp
new file mode 100644
index 000000000..85b00b251
--- /dev/null
+++ b/src/widgets/eek-color-def.cpp
@@ -0,0 +1,147 @@
+/** @file
+ * @brief EEK color definition
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Eek Color Definition.
+ *
+ * The Initial Developer of the Original Code is
+ * Jon A. Cruz.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "config.h"
+
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#endif
+
+#if !defined(_)
+#define _(s) gettext(s)
+#endif // !defined(_)
+
+#include "eek-color-def.h"
+
+namespace eek
+{
+
+ColorDef::ColorDef() :
+ descr(_("none")),
+ r(0),
+ g(0),
+ b(0),
+ none(true),
+ editable(false)
+{
+}
+
+ColorDef::ColorDef( unsigned int r, unsigned int g, unsigned int b, const std::string& description ) :
+ descr(description),
+ r(r),
+ g(g),
+ b(b),
+ none(false),
+ editable(false)
+{
+}
+
+ColorDef::~ColorDef()
+{
+}
+
+ColorDef::ColorDef( ColorDef const &other )
+{
+ if ( this != &other ) {
+ *this = other;
+ }
+}
+
+ColorDef& ColorDef::operator=( ColorDef const &other )
+{
+ if ( this != & other )
+ {
+ r = other.r;
+ g = other.g;
+ b = other.b;
+ descr = other.descr;
+ none = other.none;
+ editable = other.editable;
+ }
+ return *this;
+}
+
+class ColorDef::HookData {
+public:
+ HookData( ColorCallback cb, void* data ) {_cb = cb; _data = data;}
+ ColorCallback _cb;
+ void* _data;
+};
+
+void ColorDef::setRGB( unsigned int r, unsigned int g, unsigned int b )
+{
+ if ( r != this->r || g != this->g || b != this->b ) {
+ this->r = r;
+ this->g = g;
+ this->b = b;
+
+ // beware of callbacks changing things
+ for ( std::vector<HookData*>::iterator it = _listeners.begin(); it != _listeners.end(); ++it )
+ {
+ if ( (*it)->_cb )
+ {
+ (*it)->_cb( (*it)->_data );
+ }
+ }
+ }
+}
+
+void ColorDef::addCallback( ColorCallback cb, void* data )
+{
+ _listeners.push_back( new HookData(cb, data) );
+}
+
+void ColorDef::removeCallback( ColorCallback cb, void* data )
+{
+ (void)cb;
+ (void)data;
+}
+
+} // namespace eek
+
+/*
+ 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/eek-color-def.h b/src/widgets/eek-color-def.h
new file mode 100644
index 000000000..63cd096be
--- /dev/null
+++ b/src/widgets/eek-color-def.h
@@ -0,0 +1,102 @@
+/** @file
+ * @brief EEK color definition
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Eek Color Definition.
+ *
+ * The Initial Developer of the Original Code is
+ * Jon A. Cruz.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef SEEN_EEK_COLOR_DEF_H
+#define SEEN_EEK_COLOR_DEF_H
+
+#include <string>
+#include <vector>
+
+namespace eek
+{
+
+typedef void (*ColorCallback)( void* data );
+
+
+class ColorDef
+{
+public:
+ ColorDef();
+ ColorDef( unsigned int r, unsigned int g, unsigned int b, const std::string& description );
+ virtual ~ColorDef();
+
+ ColorDef( ColorDef const &other );
+ virtual ColorDef& operator=( ColorDef const &other );
+
+ void setRGB( unsigned int r, unsigned int g, unsigned int b );
+ unsigned int getR() const { return r; }
+ unsigned int getG() const { return g; }
+ unsigned int getB() const { return b; }
+
+ void addCallback( ColorCallback cb, void* data );
+ void removeCallback( ColorCallback cb, void* data );
+
+ bool isEditable() const { return editable; }
+ void setEditable( bool edit ) { editable = edit; }
+
+ std::string descr;
+
+protected:
+ unsigned int r;
+ unsigned int g;
+ unsigned int b;
+ bool none;
+ bool editable;
+
+private:
+ class HookData;
+
+ std::vector<HookData*> _listeners;
+};
+
+
+} // namespace eek
+
+#endif // SEEN_EEK_COLOR_DEF_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/eek-preview.cpp b/src/widgets/eek-preview.cpp
new file mode 100644
index 000000000..1c1adf543
--- /dev/null
+++ b/src/widgets/eek-preview.cpp
@@ -0,0 +1,736 @@
+/** @file
+ * @brief EEK preview stuff
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Eek Preview Stuffs.
+ *
+ * The Initial Developer of the Original Code is
+ * Jon A. Cruz.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include "eek-preview.h"
+
+#define PRIME_BUTTON_MAGIC_NUMBER 1
+
+#define FOCUS_PROP_ID 1
+
+/* Keep in sycn with last value in eek-preview.h */
+#define PREVIEW_SIZE_LAST PREVIEW_SIZE_HUGE
+#define PREVIEW_SIZE_NEXTFREE (PREVIEW_SIZE_HUGE + 1)
+
+#define PREVIEW_MAX_RATIO 500
+
+static void eek_preview_class_init( EekPreviewClass *klass );
+static void eek_preview_init( EekPreview *preview );
+
+static GtkWidgetClass* parent_class = 0;
+
+void eek_preview_set_color( EekPreview* preview, int r, int g, int b )
+{
+ preview->_r = r;
+ preview->_g = g;
+ preview->_b = b;
+
+ gtk_widget_queue_draw(GTK_WIDGET(preview));
+}
+
+
+void eek_preview_set_pixbuf( EekPreview* preview, GdkPixbuf* pixbuf )
+{
+ preview->_previewPixbuf = pixbuf;
+
+ gtk_widget_queue_draw(GTK_WIDGET(preview));
+
+ if (preview->_scaled) {
+ g_object_unref(preview->_scaled);
+ preview->_scaled = 0;
+ }
+ preview->_scaledW = gdk_pixbuf_get_width(preview->_previewPixbuf);
+ preview->_scaledH = gdk_pixbuf_get_height(preview->_previewPixbuf);
+}
+
+
+GType eek_preview_get_type(void)
+{
+ static GType preview_type = 0;
+
+ if (!preview_type) {
+ static const GTypeInfo preview_info = {
+ sizeof( EekPreviewClass ),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc)eek_preview_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof( EekPreview ),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc)eek_preview_init,
+ NULL /* value_table */
+ };
+
+
+ preview_type = g_type_register_static( GTK_TYPE_DRAWING_AREA, "EekPreview", &preview_info, (GTypeFlags)0 );
+ }
+
+ return preview_type;
+}
+
+static gboolean setupDone = FALSE;
+static GtkRequisition sizeThings[PREVIEW_SIZE_NEXTFREE];
+
+void eek_preview_set_size_mappings( guint count, GtkIconSize const* sizes )
+{
+ gint width = 0;
+ gint height = 0;
+ gint smallest = 512;
+ gint largest = 0;
+ guint i = 0;
+ guint delta = 0;
+
+ for ( i = 0; i < count; ++i ) {
+ gboolean worked = gtk_icon_size_lookup( sizes[i], &width, &height );
+ if ( worked ) {
+ if ( width < smallest ) {
+ smallest = width;
+ }
+ if ( width > largest ) {
+ largest = width;
+ }
+ }
+ }
+
+ smallest = (smallest * 3) / 4;
+
+ delta = largest - smallest;
+
+ for ( i = 0; i < G_N_ELEMENTS(sizeThings); ++i ) {
+ guint val = smallest + ( (i * delta) / (G_N_ELEMENTS(sizeThings) - 1) );
+ sizeThings[i].width = val;
+ sizeThings[i].height = val;
+ }
+
+ setupDone = TRUE;
+}
+
+static void eek_preview_size_request( GtkWidget* widget, GtkRequisition* req )
+{
+ gint width = 0;
+ gint height = 0;
+ EekPreview* preview = EEK_PREVIEW(widget);
+
+ if ( !setupDone ) {
+ GtkIconSize sizes[] = {
+ GTK_ICON_SIZE_MENU,
+ GTK_ICON_SIZE_SMALL_TOOLBAR,
+ GTK_ICON_SIZE_LARGE_TOOLBAR,
+ GTK_ICON_SIZE_BUTTON,
+ GTK_ICON_SIZE_DIALOG
+ };
+ eek_preview_set_size_mappings( G_N_ELEMENTS(sizes), sizes );
+ }
+
+ width = sizeThings[preview->_size].width;
+ height = sizeThings[preview->_size].height;
+
+ if ( preview->_view == VIEW_TYPE_LIST ) {
+ width *= 3;
+ }
+
+ if ( preview->_ratio != 100 ) {
+ width = (width * preview->_ratio) / 100;
+ if ( width < 0 ) {
+ width = 1;
+ }
+ }
+
+ req->width = width;
+ req->height = height;
+}
+
+enum {
+ CLICKED_SIGNAL,
+ ALTCLICKED_SIGNAL,
+ LAST_SIGNAL
+};
+
+
+static guint eek_preview_signals[LAST_SIGNAL] = { 0 };
+
+
+gboolean eek_preview_expose_event( GtkWidget* widget, GdkEventExpose* event )
+{
+/* g_message("Exposed!!! %s", GTK_WIDGET_HAS_FOCUS(widget) ? "XXX" : "---" ); */
+ gint insetX = 0;
+ gint insetY = 0;
+
+ (void)event;
+/*
+ gint lower = widget->allocation.width;
+ lower = (widget->allocation.height < lower) ? widget->allocation.height : lower;
+ if ( lower > 16 ) {
+ insetX++;
+ if ( lower > 18 ) {
+ insetX++;
+ if ( lower > 22 ) {
+ insetX++;
+ if ( lower > 24 ) {
+ insetX++;
+ if ( lower > 32 ) {
+ insetX++;
+ }
+ }
+ }
+ }
+ insetY = insetX;
+ }
+*/
+
+ if ( GTK_WIDGET_DRAWABLE( widget ) ) {
+ GtkStyle* style = gtk_widget_get_style( widget );
+
+ if ( insetX > 0 || insetY > 0 ) {
+ gtk_paint_flat_box( style,
+ widget->window,
+ (GtkStateType)GTK_WIDGET_STATE(widget),
+ GTK_SHADOW_NONE,
+ NULL,
+ widget,
+ NULL,
+ 0, 0,
+ widget->allocation.width, widget->allocation.height);
+ }
+
+ GdkGC *gc = gdk_gc_new( widget->window );
+ EekPreview* preview = EEK_PREVIEW(widget);
+ GdkColor fg = {0, preview->_r, preview->_g, preview->_b};
+
+ gdk_colormap_alloc_color( gdk_colormap_get_system(), &fg, FALSE, TRUE );
+
+ gdk_gc_set_foreground( gc, &fg );
+
+ gdk_draw_rectangle( widget->window,
+ gc,
+ TRUE,
+ insetX, insetY,
+ widget->allocation.width - (insetX * 2), widget->allocation.height - (insetY * 2) );
+
+ if ( preview->_linked ) {
+ /* Draw arrow */
+ GdkRectangle possible = {insetX, insetY, (widget->allocation.width - (insetX * 2)), (widget->allocation.height - (insetY * 2)) };
+ GdkRectangle area = {possible.x, possible.y, possible.width / 2, possible.height / 2 };
+
+ /* Make it square */
+ if ( area.width > area.height )
+ area.width = area.height;
+ if ( area.height > area.width )
+ area.height = area.width;
+
+ /* Center it horizontally */
+ if ( area.width < possible.width ) {
+ int diff = (possible.width - area.width) / 2;
+ area.x += diff;
+ }
+
+
+ if ( preview->_linked & PREVIEW_LINK_IN ) {
+ gtk_paint_arrow( style,
+ widget->window,
+ (GtkStateType)widget->state,
+ GTK_SHADOW_ETCHED_IN,
+ NULL, /* clip area. &area, */
+ widget, /* may be NULL */
+ NULL, /* detail */
+ GTK_ARROW_DOWN,
+ FALSE,
+ area.x, area.y,
+ area.width, area.height
+ );
+ }
+
+ if ( preview->_linked & PREVIEW_LINK_OUT ) {
+ GdkRectangle otherArea = {area.x, area.y, area.width, area.height};
+ if ( otherArea.height < possible.height ) {
+ otherArea.y = possible.y + (possible.height - otherArea.height);
+ }
+
+ gtk_paint_arrow( style,
+ widget->window,
+ (GtkStateType)widget->state,
+ GTK_SHADOW_ETCHED_OUT,
+ NULL, /* clip area. &area, */
+ widget, /* may be NULL */
+ NULL, /* detail */
+ GTK_ARROW_UP,
+ FALSE,
+ otherArea.x, otherArea.y,
+ otherArea.width, otherArea.height
+ );
+ }
+
+ if ( preview->_linked & PREVIEW_LINK_OTHER ) {
+ GdkRectangle otherArea = {insetX, area.y, area.width, area.height};
+ if ( otherArea.height < possible.height ) {
+ otherArea.y = possible.y + (possible.height - otherArea.height) / 2;
+ }
+
+ gtk_paint_arrow( style,
+ widget->window,
+ (GtkStateType)widget->state,
+ GTK_SHADOW_ETCHED_OUT,
+ NULL, /* clip area. &area, */
+ widget, /* may be NULL */
+ NULL, /* detail */
+ GTK_ARROW_LEFT,
+ FALSE,
+ otherArea.x, otherArea.y,
+ otherArea.width, otherArea.height
+ );
+ }
+ }
+
+ if ( preview->_previewPixbuf ) {
+ GtkDrawingArea* da = &(preview->drawing);
+ GdkDrawable* drawable = (GdkDrawable*) (((GtkWidget*)da)->window);
+ gint w = 0;
+ gint h = 0;
+ gdk_drawable_get_size(drawable, &w, &h);
+ if ((w != preview->_scaledW) || (h != preview->_scaledH)) {
+ if (preview->_scaled) {
+ g_object_unref(preview->_scaled);
+ }
+ preview->_scaled = gdk_pixbuf_scale_simple(preview->_previewPixbuf, w, h, GDK_INTERP_BILINEAR);
+ preview->_scaledW = w;
+ preview->_scaledH = h;
+ }
+
+ GdkPixbuf* pix = (preview->_scaled) ? preview->_scaled : preview->_previewPixbuf;
+ gdk_draw_pixbuf( drawable, 0, pix, 0, 0, 0, 0, w, h, GDK_RGB_DITHER_NONE, 0, 0 );
+ }
+
+
+ if ( GTK_WIDGET_HAS_FOCUS(widget) ) {
+ gtk_paint_focus( style,
+ widget->window,
+ GTK_STATE_NORMAL,
+ NULL, /* GdkRectangle *area, */
+ widget,
+ NULL,
+ 0 + 1, 0 + 1,
+ widget->allocation.width - 2, widget->allocation.height - 2 );
+ }
+ }
+
+
+ return FALSE;
+}
+
+
+static gboolean eek_preview_enter_cb( GtkWidget* widget, GdkEventCrossing* event )
+{
+ if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
+ EekPreview* preview = EEK_PREVIEW(widget);
+ preview->_within = TRUE;
+ gtk_widget_set_state( widget, preview->_hot ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT );
+ }
+ return FALSE;
+}
+
+static gboolean eek_preview_leave_cb( GtkWidget* widget, GdkEventCrossing* event )
+{
+ if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
+ EekPreview* preview = EEK_PREVIEW(widget);
+ preview->_within = FALSE;
+ gtk_widget_set_state( widget, GTK_STATE_NORMAL );
+ }
+ return FALSE;
+}
+
+/*
+static gboolean eek_preview_focus_in_event( GtkWidget* widget, GdkEventFocus* event )
+{
+ g_message("focus IN");
+ gboolean blip = parent_class->focus_in_event ? parent_class->focus_in_event(widget, event) : FALSE;
+ return blip;
+}
+
+static gboolean eek_preview_focus_out_event( GtkWidget* widget, GdkEventFocus* event )
+{
+ g_message("focus OUT");
+ gboolean blip = parent_class->focus_out_event ? parent_class->focus_out_event(widget, event) : FALSE;
+ return blip;
+}
+*/
+
+static gboolean eek_preview_button_press_cb( GtkWidget* widget, GdkEventButton* event )
+{
+ if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
+ EekPreview* preview = EEK_PREVIEW(widget);
+
+ if ( preview->_takesFocus && !GTK_WIDGET_HAS_FOCUS(widget) ) {
+ gtk_widget_grab_focus(widget);
+ }
+
+ if ( event->button == PRIME_BUTTON_MAGIC_NUMBER ) {
+ preview->_hot = TRUE;
+ if ( preview->_within ) {
+ gtk_widget_set_state( widget, GTK_STATE_ACTIVE );
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean eek_preview_button_release_cb( GtkWidget* widget, GdkEventButton* event )
+{
+ if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
+ EekPreview* preview = EEK_PREVIEW(widget);
+ preview->_hot = FALSE;
+ gtk_widget_set_state( widget, GTK_STATE_NORMAL );
+ if ( preview->_within && event->button == PRIME_BUTTON_MAGIC_NUMBER ) {
+ gboolean isAlt = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK;
+
+ if ( isAlt ) {
+ g_signal_emit( widget, eek_preview_signals[ALTCLICKED_SIGNAL], 0, 2 );
+ } else {
+ g_signal_emit( widget, eek_preview_signals[CLICKED_SIGNAL], 0 );
+ }
+ }
+ }
+ return FALSE;
+}
+
+gboolean eek_preview_key_press_event( GtkWidget* widget, GdkEventKey* event)
+{
+ (void)widget;
+ (void)event;
+ g_message("TICK");
+ return FALSE;
+}
+
+gboolean eek_preview_key_release_event( GtkWidget* widget, GdkEventKey* event)
+{
+ (void)widget;
+ (void)event;
+ g_message("tock");
+ return FALSE;
+}
+
+static void eek_preview_get_property( GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GObjectClass* gobjClass = G_OBJECT_CLASS(parent_class);
+ switch ( property_id ) {
+ case FOCUS_PROP_ID:
+ {
+ EekPreview* preview = EEK_PREVIEW( object );
+ g_value_set_boolean( value, preview->_takesFocus );
+ }
+ break;
+ default:
+ {
+ if ( gobjClass->get_property ) {
+ gobjClass->get_property( object, property_id, value, pspec );
+ }
+ }
+ }
+}
+
+static void eek_preview_set_property( GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GObjectClass* gobjClass = G_OBJECT_CLASS(parent_class);
+ switch ( property_id ) {
+ case FOCUS_PROP_ID:
+ {
+ EekPreview* preview = EEK_PREVIEW( object );
+ gboolean val = g_value_get_boolean( value );
+ if ( val != preview->_takesFocus ) {
+ preview->_takesFocus = val;
+ }
+ }
+ break;
+ default:
+ {
+ if ( gobjClass->set_property ) {
+ gobjClass->set_property( object, property_id, value, pspec );
+ }
+ }
+ }
+}
+
+
+static gboolean eek_preview_popup_menu( GtkWidget* widget )
+{
+/* g_message("Do the popup!"); */
+ gboolean blip = parent_class->popup_menu ? parent_class->popup_menu(widget) : FALSE;
+ return blip;
+}
+
+
+static void eek_preview_class_init( EekPreviewClass *klass )
+{
+ GObjectClass* gobjClass = G_OBJECT_CLASS(klass);
+ /*GtkObjectClass* objectClass = (GtkObjectClass*)klass;*/
+ GtkWidgetClass* widgetClass = (GtkWidgetClass*)klass;
+
+ gobjClass->set_property = eek_preview_set_property;
+ gobjClass->get_property = eek_preview_get_property;
+
+ /*objectClass->destroy = eek_preview_destroy;*/
+
+ parent_class = (GtkWidgetClass*)g_type_class_peek_parent( klass );
+
+ /*widgetClass->map = ;*/
+ /*widgetClass->unmap = ;*/
+ /*widgetClass->realize = ;*/
+ /*widgetClass->unrealize = ;*/
+ widgetClass->size_request = eek_preview_size_request;
+ /*widgetClass->size_allocate = ;*/
+ /*widgetClass->state_changed = ;*/
+ /*widgetClass->style_set = ;*/
+ /*widgetClass->grab_notify = ;*/
+
+ widgetClass->button_press_event = eek_preview_button_press_cb;
+ widgetClass->button_release_event = eek_preview_button_release_cb;
+ /*widgetClass->delete_event = ;*/
+ /*widgetClass->destroy_event = ;*/
+ widgetClass->expose_event = eek_preview_expose_event;
+/* widgetClass->key_press_event = eek_preview_key_press_event; */
+/* widgetClass->key_release_event = eek_preview_key_release_event; */
+ widgetClass->enter_notify_event = eek_preview_enter_cb;
+ widgetClass->leave_notify_event = eek_preview_leave_cb;
+ /*widgetClass->configure_event = ;*/
+ /*widgetClass->focus_in_event = eek_preview_focus_in_event;*/
+ /*widgetClass->focus_out_event = eek_preview_focus_out_event;*/
+
+ /* selection */
+ /*widgetClass->selection_get = ;*/
+ /*widgetClass->selection_received = ;*/
+
+
+ /* drag source: */
+ /*widgetClass->drag_begin = ;*/
+ /*widgetClass->drag_end = ;*/
+ /*widgetClass->drag_data_get = ;*/
+ /*widgetClass->drag_data_delete = ;*/
+
+ /* drag target: */
+ /*widgetClass->drag_leave = ;*/
+ /*widgetClass->drag_motion = ;*/
+ /*widgetClass->drag_drop = ;*/
+ /*widgetClass->drag_data_received = ;*/
+
+ /* For keybindings: */
+ widgetClass->popup_menu = eek_preview_popup_menu;
+ /*widgetClass->show_help = ;*/
+
+ /* Accessibility support: */
+ /*widgetClass->get_accessible = ;*/
+ /*widgetClass->screen_changed = ;*/
+ /*widgetClass->can_activate_accel = ;*/
+
+
+ eek_preview_signals[CLICKED_SIGNAL] =
+ g_signal_new( "clicked",
+ G_TYPE_FROM_CLASS( klass ),
+ (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+ G_STRUCT_OFFSET( EekPreviewClass, clicked ),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 );
+
+ eek_preview_signals[ALTCLICKED_SIGNAL] =
+ g_signal_new( "alt-clicked",
+ G_TYPE_FROM_CLASS( klass ),
+ (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+ G_STRUCT_OFFSET( EekPreviewClass, clicked ),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT, G_TYPE_NONE,
+ 1, G_TYPE_INT );
+
+
+ g_object_class_install_property( gobjClass,
+ FOCUS_PROP_ID,
+ g_param_spec_boolean(
+ "focus-on-click",
+ NULL,
+ "flag to grab focus when clicked",
+ TRUE,
+ (GParamFlags)(G_PARAM_READWRITE | G_PARAM_CONSTRUCT)
+ )
+ );
+}
+
+void eek_preview_set_linked( EekPreview* splat, LinkType link )
+{
+ link = (LinkType)(link & PREVIEW_LINK_ALL);
+ if ( link != (LinkType)splat->_linked ) {
+ splat->_linked = link;
+
+ gtk_widget_queue_draw( GTK_WIDGET(splat) );
+ }
+}
+
+LinkType eek_preview_get_linked( EekPreview* splat )
+{
+ return (LinkType)splat->_linked;
+}
+
+gboolean eek_preview_get_focus_on_click( EekPreview* preview )
+{
+ return preview->_takesFocus;
+}
+
+void eek_preview_set_focus_on_click( EekPreview* preview, gboolean focus_on_click )
+{
+ if ( focus_on_click != preview->_takesFocus ) {
+ preview->_takesFocus = focus_on_click;
+ }
+}
+
+void eek_preview_set_details( EekPreview* preview, PreviewStyle prevstyle, ViewType view, PreviewSize size, guint ratio )
+{
+ preview->_prevstyle = prevstyle;
+ preview->_view = view;
+
+ if ( size > PREVIEW_SIZE_LAST ) {
+ size = PREVIEW_SIZE_LAST;
+ }
+ preview->_size = size;
+
+ if ( ratio > PREVIEW_MAX_RATIO ) {
+ ratio = PREVIEW_MAX_RATIO;
+ }
+ preview->_ratio = ratio;
+
+ gtk_widget_queue_draw(GTK_WIDGET(preview));
+}
+
+static void eek_preview_init( EekPreview *preview )
+{
+ GtkWidget* widg = GTK_WIDGET(preview);
+ GTK_WIDGET_SET_FLAGS( widg, GTK_CAN_FOCUS );
+ GTK_WIDGET_SET_FLAGS( widg, GTK_RECEIVES_DEFAULT );
+
+ gtk_widget_set_sensitive( widg, TRUE );
+
+ gtk_widget_add_events(widg, GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_KEY_RELEASE_MASK
+ | GDK_FOCUS_CHANGE_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK );
+
+/* gtk_widget_add_events( widg, GDK_ALL_EVENTS_MASK );*/
+
+ preview->_r = 0x80;
+ preview->_g = 0x80;
+ preview->_b = 0xcc;
+ preview->_scaledW = 0;
+ preview->_scaledH = 0;
+
+ preview->_hot = FALSE;
+ preview->_within = FALSE;
+ preview->_takesFocus = FALSE;
+
+ preview->_prevstyle = PREVIEW_STYLE_ICON;
+ preview->_view = VIEW_TYPE_LIST;
+ preview->_size = PREVIEW_SIZE_SMALL;
+ preview->_ratio = 100;
+
+ preview->_previewPixbuf = 0;
+ preview->_scaled = 0;
+
+/*
+ GdkColor color = {0};
+ color.red = (255 << 8) | 255;
+
+ GdkColor whack = {0};
+ whack.green = (255 << 8) | 255;
+
+ gtk_widget_modify_bg( widg, GTK_STATE_NORMAL, &color );
+ gtk_widget_modify_bg( widg, GTK_STATE_PRELIGHT, &whack );
+*/
+
+/* GTK_STATE_ACTIVE, */
+/* GTK_STATE_PRELIGHT, */
+/* GTK_STATE_SELECTED, */
+/* GTK_STATE_INSENSITIVE */
+
+ if ( 0 ) {
+ GdkColor color = {0,0,0,0};
+
+ color.red = 0xffff;
+ color.green = 0;
+ color.blue = 0xffff;
+ gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
+ gtk_widget_modify_bg(widg, GTK_STATE_ACTIVE, &color);
+
+ color.red = 0;
+ color.green = 0xffff;
+ color.blue = 0;
+ gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
+ gtk_widget_modify_bg(widg, GTK_STATE_SELECTED, &color);
+
+ color.red = 0xffff;
+ color.green = 0;
+ color.blue = 0;
+ gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
+ gtk_widget_modify_bg( widg, GTK_STATE_PRELIGHT, &color );
+ }
+}
+
+
+GtkWidget* eek_preview_new(void)
+{
+ return GTK_WIDGET( g_object_new( EEK_PREVIEW_TYPE, 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/eek-preview.h b/src/widgets/eek-preview.h
new file mode 100644
index 000000000..6eb5c8930
--- /dev/null
+++ b/src/widgets/eek-preview.h
@@ -0,0 +1,152 @@
+/** @file
+ * @brief EEK preview stuff
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Eek Preview Stuffs.
+ *
+ * The Initial Developer of the Original Code is
+ * Jon A. Cruz.
+ * Portions created by the Initial Developer are Copyright (C) 2005-2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef SEEN_EEK_PREVIEW_H
+#define SEEN_EEK_PREVIEW_H
+
+#include <gdk/gdkpixbuf.h>
+#include <gtk/gtkdrawingarea.h>
+
+G_BEGIN_DECLS
+
+
+#define EEK_PREVIEW_TYPE (eek_preview_get_type())
+#define EEK_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST( (obj), EEK_PREVIEW_TYPE, EekPreview))
+#define EEK_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST( (klass), EEK_PREVIEW_TYPE, EekPreviewClass))
+#define IS_EEK_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE( (obj), EEK_PREVIEW_TYPE))
+#define IS_EEK_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE( (klass), EEK_PREVIEW_TYPE))
+#define EEK_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS( (obj), EEK_PREVIEW_TYPE, EekPreviewClass))
+
+typedef enum {
+ PREVIEW_STYLE_ICON = 0,
+ PREVIEW_STYLE_PREVIEW,
+ PREVIEW_STYLE_NAME,
+ PREVIEW_STYLE_BLURB,
+ PREVIEW_STYLE_ICON_NAME,
+ PREVIEW_STYLE_ICON_BLURB,
+ PREVIEW_STYLE_PREVIEW_NAME,
+ PREVIEW_STYLE_PREVIEW_BLURB
+} PreviewStyle;
+
+typedef enum {
+ VIEW_TYPE_LIST = 0,
+ VIEW_TYPE_GRID
+} ViewType;
+
+typedef enum {
+ PREVIEW_SIZE_TINY = 0,
+ PREVIEW_SIZE_SMALL,
+ PREVIEW_SIZE_MEDIUM,
+ PREVIEW_SIZE_BIG,
+ PREVIEW_SIZE_BIGGER,
+ PREVIEW_SIZE_HUGE
+} PreviewSize;
+
+typedef enum {
+ PREVIEW_LINK_NONE = 0,
+ PREVIEW_LINK_IN = 1,
+ PREVIEW_LINK_OUT = 2,
+ PREVIEW_LINK_OTHER = 4,
+ PREVIEW_LINK_ALL = 7
+} LinkType;
+
+typedef struct _EekPreview EekPreview;
+typedef struct _EekPreviewClass EekPreviewClass;
+
+
+struct _EekPreview
+{
+ GtkDrawingArea drawing;
+
+ int _r;
+ int _g;
+ int _b;
+ int _scaledW;
+ int _scaledH;
+
+ gboolean _hot;
+ gboolean _within;
+ gboolean _takesFocus;
+
+ PreviewStyle _prevstyle;
+ ViewType _view;
+ PreviewSize _size;
+ guint _ratio;
+ guint _linked;
+ GdkPixbuf* _previewPixbuf;
+ GdkPixbuf* _scaled;
+};
+
+struct _EekPreviewClass
+{
+ GtkDrawingAreaClass parent_class;
+
+ void (*clicked) (EekPreview* splat);
+};
+
+
+GType eek_preview_get_type(void) G_GNUC_CONST;
+GtkWidget* eek_preview_new(void);
+
+void eek_preview_set_details( EekPreview* splat, PreviewStyle prevstyle, ViewType view, PreviewSize size, guint ratio );
+void eek_preview_set_color( EekPreview* splat, int r, int g, int b );
+void eek_preview_set_pixbuf( EekPreview* splat, GdkPixbuf* pixbuf );
+
+void eek_preview_set_linked( EekPreview* splat, LinkType link );
+LinkType eek_preview_get_linked( EekPreview* splat );
+
+gboolean eek_preview_get_focus_on_click( EekPreview* preview );
+void eek_preview_set_focus_on_click( EekPreview* preview, gboolean focus_on_click );
+
+void eek_preview_set_size_mappings( guint count, GtkIconSize const* sizes );
+
+G_END_DECLS
+
+#endif /* SEEN_EEK_PREVIEW_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/fill-style.cpp b/src/widgets/fill-style.cpp
new file mode 100644
index 000000000..5e9d30bcd
--- /dev/null
+++ b/src/widgets/fill-style.cpp
@@ -0,0 +1,552 @@
+/** @file
+ * @brief Fill style widget
+ */
+/* Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Frank Felfe <innerspace@iname.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 1999-2005 authors
+ * Copyright (C) 2001-2002 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#define noSP_FS_VERBOSE
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glibmm/i18n.h>
+
+#include "desktop-handles.h"
+#include "desktop-style.h"
+#include "display/sp-canvas.h"
+#include "document-private.h"
+#include "gradient-chemistry.h"
+#include "inkscape.h"
+#include "selection.h"
+#include "sp-linear-gradient.h"
+#include "sp-pattern.h"
+#include "sp-radial-gradient.h"
+#include "style.h"
+#include "widgets/paint-selector.h"
+#include "widgets/sp-widget.h"
+#include "xml/repr.h"
+
+#include "widgets/fill-style.h"
+
+
+// These can be deleted once we sort out the libart dependence.
+
+#define ART_WIND_RULE_NONZERO 0
+
+static void sp_fill_style_widget_construct ( SPWidget *spw,
+ SPPaintSelector *psel );
+
+static void sp_fill_style_widget_modify_selection ( SPWidget *spw,
+ Inkscape::Selection *selection,
+ guint flags,
+ SPPaintSelector *psel );
+
+static void sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape, SPDesktop *desktop, SPWidget *spw );
+
+static void sp_fill_style_widget_change_selection ( SPWidget *spw,
+ Inkscape::Selection *selection,
+ SPPaintSelector *psel );
+
+static void sp_fill_style_widget_update (SPWidget *spw);
+
+static void sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
+ SPPaintSelectorMode mode,
+ SPWidget *spw );
+static void sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel,
+ SPPaintSelectorFillRule mode,
+ SPWidget *spw );
+
+static void sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw );
+static void sp_fill_style_widget_paint_changed (SPPaintSelector *psel, SPWidget *spw );
+
+GtkWidget *
+sp_fill_style_widget_new (void)
+{
+ GtkWidget *spw = sp_widget_new_global (INKSCAPE);
+
+ GtkWidget *vb = gtk_vbox_new (FALSE, 0);
+ gtk_widget_show (vb);
+ gtk_container_add (GTK_CONTAINER (spw), vb);
+
+ GtkWidget *psel = sp_paint_selector_new (true); // with fillrule selector
+ gtk_widget_show (psel);
+ gtk_box_pack_start (GTK_BOX (vb), psel, TRUE, TRUE, 0);
+ g_object_set_data (G_OBJECT (spw), "paint-selector", psel);
+
+ g_signal_connect ( G_OBJECT (psel), "mode_changed",
+ G_CALLBACK (sp_fill_style_widget_paint_mode_changed),
+ spw );
+
+ g_signal_connect ( G_OBJECT (psel), "dragged",
+ G_CALLBACK (sp_fill_style_widget_paint_dragged),
+ spw );
+
+ g_signal_connect ( G_OBJECT (psel), "changed",
+ G_CALLBACK (sp_fill_style_widget_paint_changed),
+ spw );
+
+ g_signal_connect ( G_OBJECT (psel), "fillrule_changed",
+ G_CALLBACK (sp_fill_style_widget_fillrule_changed),
+ spw );
+
+
+ g_signal_connect ( G_OBJECT (spw), "construct",
+ G_CALLBACK (sp_fill_style_widget_construct), psel);
+
+//FIXME: switch these from spw signals to global inkscape object signals; spw just retranslates
+//those anyway; then eliminate spw
+ g_signal_connect ( G_OBJECT (spw), "modify_selection",
+ G_CALLBACK (sp_fill_style_widget_modify_selection), psel);
+
+ g_signal_connect ( G_OBJECT (spw), "change_selection",
+ G_CALLBACK (sp_fill_style_widget_change_selection), psel);
+
+ g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_fill_style_widget_change_subselection), spw);
+
+ sp_fill_style_widget_update (SP_WIDGET (spw));
+
+ return spw;
+
+} // end of sp_fill_style_widget_new()
+
+
+
+static void
+sp_fill_style_widget_construct( SPWidget *spw, SPPaintSelector */*psel*/ )
+{
+#ifdef SP_FS_VERBOSE
+ g_print ( "Fill style widget constructed: inkscape %p repr %p\n",
+ spw->inkscape, spw->repr );
+#endif
+ if (spw->inkscape) {
+ sp_fill_style_widget_update (spw);
+ }
+
+} // end of sp_fill_style_widget_construct()
+
+static void
+sp_fill_style_widget_modify_selection( SPWidget *spw,
+ Inkscape::Selection */*selection*/,
+ guint flags,
+ SPPaintSelector */*psel*/ )
+{
+ if (flags & ( SP_OBJECT_MODIFIED_FLAG |
+ SP_OBJECT_PARENT_MODIFIED_FLAG |
+ SP_OBJECT_STYLE_MODIFIED_FLAG) )
+ {
+ sp_fill_style_widget_update (spw);
+ }
+}
+
+static void
+sp_fill_style_widget_change_subselection( Inkscape::Application */*inkscape*/,
+ SPDesktop */*desktop*/,
+ SPWidget *spw )
+{
+ sp_fill_style_widget_update (spw);
+}
+
+static void
+sp_fill_style_widget_change_selection( SPWidget *spw,
+ Inkscape::Selection */*selection*/,
+ SPPaintSelector */*psel*/ )
+{
+ sp_fill_style_widget_update (spw);
+}
+
+/**
+* \param sel Selection to use, or NULL.
+*/
+static void
+sp_fill_style_widget_update (SPWidget *spw)
+{
+ if (g_object_get_data (G_OBJECT (spw), "update"))
+ return;
+
+ if (g_object_get_data (G_OBJECT (spw), "local")) {
+ g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (FALSE)); // local change; do nothing, but reset the flag
+ return;
+ }
+
+ g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
+
+ SPPaintSelector *psel = SP_PAINT_SELECTOR (g_object_get_data (G_OBJECT (spw), "paint-selector"));
+
+ // create temporary style
+ SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
+ // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
+ int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FILL);
+
+ switch (result) {
+ case QUERY_STYLE_NOTHING:
+ {
+ /* No paint at all */
+ sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY);
+ break;
+ }
+
+ case QUERY_STYLE_SINGLE:
+ case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector
+ case QUERY_STYLE_MULTIPLE_SAME:
+ {
+ SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, true);
+ sp_paint_selector_set_mode (psel, pselmode);
+
+ sp_paint_selector_set_fillrule (psel, query->fill_rule.computed == ART_WIND_RULE_NONZERO?
+ SP_PAINT_SELECTOR_FILLRULE_NONZERO : SP_PAINT_SELECTOR_FILLRULE_EVENODD);
+
+ if (query->fill.set && query->fill.isColor()) {
+ sp_paint_selector_set_color_alpha (psel, &query->fill.value.color, SP_SCALE24_TO_FLOAT (query->fill_opacity.value));
+ } else if (query->fill.set && query->fill.isPaintserver()) {
+
+ SPPaintServer *server = SP_STYLE_FILL_SERVER (query);
+
+ if (SP_IS_LINEARGRADIENT (server)) {
+ SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
+ sp_paint_selector_set_gradient_linear (psel, vector);
+
+ SPLinearGradient *lg = SP_LINEARGRADIENT (server);
+ sp_paint_selector_set_gradient_properties (psel,
+ SP_GRADIENT_UNITS (lg),
+ SP_GRADIENT_SPREAD (lg));
+ } else if (SP_IS_RADIALGRADIENT (server)) {
+ SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
+ sp_paint_selector_set_gradient_radial (psel, vector);
+
+ SPRadialGradient *rg = SP_RADIALGRADIENT (server);
+ sp_paint_selector_set_gradient_properties (psel,
+ SP_GRADIENT_UNITS (rg),
+ SP_GRADIENT_SPREAD (rg));
+ } else if (SP_IS_PATTERN (server)) {
+ SPPattern *pat = pattern_getroot (SP_PATTERN (server));
+ sp_update_pattern_list (psel, pat);
+ }
+ }
+ break;
+ }
+
+ case QUERY_STYLE_MULTIPLE_DIFFERENT:
+ {
+ sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE);
+ break;
+ }
+ }
+
+ sp_style_unref(query);
+
+ g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
+
+}
+
+
+static void
+sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
+ SPPaintSelectorMode /*mode*/,
+ SPWidget *spw )
+{
+ if (g_object_get_data (G_OBJECT (spw), "update"))
+ return;
+
+ /* TODO: Does this work? */
+ /* TODO: Not really, here we have to get old color back from object */
+ /* Instead of relying on paint widget having meaningful colors set */
+ sp_fill_style_widget_paint_changed (psel, spw);
+}
+
+static void
+sp_fill_style_widget_fillrule_changed ( SPPaintSelector */*psel*/,
+ SPPaintSelectorFillRule mode,
+ SPWidget *spw )
+{
+ if (g_object_get_data (G_OBJECT (spw), "update"))
+ return;
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ sp_repr_css_set_property (css, "fill-rule", mode == SP_PAINT_SELECTOR_FILLRULE_EVENODD? "evenodd":"nonzero");
+
+ sp_desktop_set_style (desktop, css);
+
+ sp_repr_css_attr_unref (css);
+
+ sp_document_done (SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_FILL_STROKE,
+ _("Change fill rule"));
+}
+
+static gchar const *undo_label_1 = "fill:flatcolor:1";
+static gchar const *undo_label_2 = "fill:flatcolor:2";
+static gchar const *undo_label = undo_label_1;
+
+/**
+This is called repeatedly while you are dragging a color slider, only for flat color
+modes. Previously it set the color in style but did not update the repr for efficiency, however
+this was flakey and didn't buy us almost anything. So now it does the same as _changed, except
+lumps all its changes for undo.
+ */
+static void
+sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw)
+{
+ if (!spw->inkscape) {
+ return;
+ }
+
+ if (g_object_get_data (G_OBJECT (spw), "update")) {
+ return;
+ }
+
+ if (g_object_get_data (G_OBJECT (spw), "local")) {
+ // previous local flag not cleared yet;
+ // this means dragged events come too fast, so we better skip this one to speed up display
+ // (it's safe to do this in any case)
+ return;
+ }
+
+ g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
+
+ switch (psel->mode) {
+
+ case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
+ case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
+ {
+ sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "fill", "fill-opacity");
+ sp_document_maybe_done (sp_desktop_document(SP_ACTIVE_DESKTOP), undo_label, SP_VERB_DIALOG_FILL_STROKE,
+ _("Set fill color"));
+ g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (TRUE)); // local change, do not update from selection
+ break;
+ }
+
+ default:
+ g_warning ( "file %s: line %d: Paint %d should not emit 'dragged'",
+ __FILE__, __LINE__, psel->mode );
+ break;
+
+ }
+ g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
+}
+
+
+/**
+This is called (at least) when:
+1 paint selector mode is switched (e.g. flat color -> gradient)
+2 you finished dragging a gradient node and released mouse
+3 you changed a gradient selector parameter (e.g. spread)
+Must update repr.
+ */
+static void
+sp_fill_style_widget_paint_changed ( SPPaintSelector *psel,
+ SPWidget *spw )
+{
+ if (g_object_get_data (G_OBJECT (spw), "update")) {
+ return;
+ }
+ g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (!desktop) {
+ return;
+ }
+ SPDocument *document = sp_desktop_document (desktop);
+ Inkscape::Selection *selection = sp_desktop_selection (desktop);
+
+ GSList const *items = selection->itemList();
+
+ switch (psel->mode) {
+
+ case SP_PAINT_SELECTOR_MODE_EMPTY:
+ // This should not happen.
+ g_warning ( "file %s: line %d: Paint %d should not emit 'changed'",
+ __FILE__, __LINE__, psel->mode);
+ break;
+ case SP_PAINT_SELECTOR_MODE_MULTIPLE:
+ // This happens when you switch multiple objects with different gradients to flat color;
+ // nothing to do here.
+ break;
+
+ case SP_PAINT_SELECTOR_MODE_NONE:
+ {
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ sp_repr_css_set_property (css, "fill", "none");
+
+ sp_desktop_set_style (desktop, css);
+
+ sp_repr_css_attr_unref (css);
+
+ sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
+ _("Remove fill"));
+ break;
+ }
+
+ case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
+ case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
+ {
+ // FIXME: fix for GTK breakage, see comment in SelectedStyle::on_opacity_changed; here it results in losing release events
+ sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(desktop), 0);
+
+ sp_paint_selector_set_flat_color (psel, desktop, "fill", "fill-opacity");
+ sp_document_maybe_done (sp_desktop_document(desktop), undo_label, SP_VERB_DIALOG_FILL_STROKE,
+ _("Set fill color"));
+ // resume interruptibility
+ sp_canvas_end_forced_full_redraws(sp_desktop_canvas(desktop));
+
+ // on release, toggle undo_label so that the next drag will not be lumped with this one
+ if (undo_label == undo_label_1)
+ undo_label = undo_label_2;
+ else
+ undo_label = undo_label_1;
+
+ break;
+ }
+
+ case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR:
+ case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL:
+ if (items) {
+ SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR
+ ? SP_GRADIENT_TYPE_LINEAR
+ : SP_GRADIENT_TYPE_RADIAL );
+
+ // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "fill-opacity", "1.0");
+
+ SPGradient *vector = sp_paint_selector_get_gradient_vector(psel);
+ if (!vector) {
+ /* No vector in paint selector should mean that we just changed mode */
+
+ SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
+ int result = objects_query_fillstroke ((GSList *) items, query, true);
+ guint32 common_rgb = 0;
+ if (result == QUERY_STYLE_MULTIPLE_SAME) {
+ if (!query->fill.isColor()) {
+ common_rgb = sp_desktop_get_color(desktop, true);
+ } else {
+ common_rgb = query->fill.value.color.toRGBA32( 0xff );
+ }
+ vector = sp_document_default_gradient_vector(document, common_rgb);
+ }
+ sp_style_unref(query);
+
+ for (GSList const *i = items; i != NULL; i = i->next) {
+ //FIXME: see above
+ sp_repr_css_change_recursive(SP_OBJECT_REPR(i->data), css, "style");
+
+ if (!vector) {
+ sp_item_set_gradient(SP_ITEM(i->data),
+ sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), true),
+ gradient_type, true);
+ } else {
+ sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
+ }
+ }
+ } else {
+ /* We have changed from another gradient type, or modified spread/units within
+ * this gradient type. */
+ vector = sp_gradient_ensure_vector_normalized (vector);
+ for (GSList const *i = items; i != NULL; i = i->next) {
+ //FIXME: see above
+ sp_repr_css_change_recursive (SP_OBJECT_REPR (i->data), css, "style");
+
+ SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
+ sp_gradient_selector_attrs_to_gradient (gr, psel);
+ }
+ }
+
+ sp_repr_css_attr_unref (css);
+
+ sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
+ _("Set gradient on fill"));
+ }
+ break;
+
+ case SP_PAINT_SELECTOR_MODE_PATTERN:
+
+ if (items) {
+
+ SPPattern *pattern = sp_paint_selector_get_pattern (psel);
+ if (!pattern) {
+
+ /* No Pattern in paint selector should mean that we just
+ * changed mode - dont do jack.
+ */
+
+ } else {
+ Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern);
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id"));
+ sp_repr_css_set_property (css, "fill", urltext);
+
+ // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
+ sp_repr_css_set_property(css, "fill-opacity", "1.0");
+
+ // cannot just call sp_desktop_set_style, because we don't want to touch those
+ // objects who already have the same root pattern but through a different href
+ // chain. FIXME: move this to a sp_item_set_pattern
+ for (GSList const *i = items; i != NULL; i = i->next) {
+ SPObject *selobj = SP_OBJECT (i->data);
+
+ SPStyle *style = SP_OBJECT_STYLE (selobj);
+ if (style && style->fill.isPaintserver()) {
+ SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (selobj);
+ if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern)
+ // only if this object's pattern is not rooted in our selected pattern, apply
+ continue;
+ }
+
+ sp_desktop_apply_css_recursive (selobj, css, true);
+ }
+
+ sp_repr_css_attr_unref (css);
+ g_free (urltext);
+
+ } // end if
+
+ sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
+ _("Set pattern on fill"));
+
+ } // end if
+
+ break;
+
+ case SP_PAINT_SELECTOR_MODE_UNSET:
+ if (items) {
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ sp_repr_css_unset_property (css, "fill");
+
+ sp_desktop_set_style (desktop, css);
+ sp_repr_css_attr_unref (css);
+
+ sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
+ _("Unset fill"));
+ }
+ break;
+
+ default:
+ g_warning ( "file %s: line %d: Paint selector should not be in "
+ "mode %d",
+ __FILE__, __LINE__, psel->mode );
+ break;
+ }
+
+ g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (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/fill-style.h b/src/widgets/fill-style.h
new file mode 100644
index 000000000..3924412ec
--- /dev/null
+++ b/src/widgets/fill-style.h
@@ -0,0 +1,33 @@
+/** @file
+ * @brief Fill style configuration
+ */
+/* Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_DIALOGS_SP_FILL_STYLE_H
+#define SEEN_DIALOGS_SP_FILL_STYLE_H
+
+#include <glib.h>
+#include <gtk/gtkwidget.h>
+#include "forward.h"
+
+
+GtkWidget *sp_fill_style_widget_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/layer-selector.cpp b/src/widgets/layer-selector.cpp
deleted file mode 100644
index d51b31e67..000000000
--- a/src/widgets/layer-selector.cpp
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
- * Inkscape::Widgets::LayerSelector - layer selector widget
- *
- * Authors:
- * MenTaLguY <mental@rydia.net>
- *
- * Copyright (C) 2004 MenTaLguY
- *
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <cstring>
-#include <string>
-#include <glibmm/i18n.h>
-
-#include "desktop-handles.h"
-
-#include "widgets/layer-selector.h"
-#include "widgets/shrink-wrap-button.h"
-#include "widgets/icon.h"
-
-#include "util/reverse-list.h"
-#include "util/filter-list.h"
-
-#include "sp-item.h"
-#include "desktop.h"
-#include "document.h"
-#include "dialogs/layer-properties.h"
-#include "layer-manager.h"
-#include "xml/node-event-vector.h"
-#include "verbs.h"
-
-namespace Inkscape {
-namespace Widgets {
-
-namespace {
-
-class AlternateIcons : public Gtk::HBox {
-public:
- AlternateIcons(Inkscape::IconSize 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(Inkscape::ICON_SIZE_DECORATION, "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(Inkscape::ICON_SIZE_DECORATION, "lock_unlocked", "width_height_lock"));
- _lock_toggle.add(*label);
- _lock_toggle.signal_toggled().connect(
- sigc::compose(
- sigc::mem_fun(*label, &AlternateIcons::setState),
- sigc::mem_fun(_lock_toggle, &Gtk::ToggleButton::get_active)
- )
- );
- _lock_toggled_connection = _lock_toggle.signal_toggled().connect(
- sigc::compose(
- sigc::mem_fun(*this, &LayerSelector::_lockLayer),
- sigc::mem_fun(_lock_toggle, &Gtk::ToggleButton::get_active)
- )
- );
-
- _lock_toggle.set_relief(Gtk::RELIEF_NONE);
- shrink_wrap_button(_lock_toggle);
- _tooltips.set_tip(_lock_toggle, _("Lock or unlock current layer"));
- pack_start(_lock_toggle, Gtk::PACK_EXPAND_PADDING);
-
- _tooltips.set_tip(_selector, _("Current layer"));
- pack_start(_selector, Gtk::PACK_EXPAND_WIDGET);
-
- _layer_model = Gtk::ListStore::create(_model_columns);
- _selector.set_model(_layer_model);
- _selector.pack_start(_label_renderer);
- _selector.set_cell_data_func(
- _label_renderer,
- sigc::mem_fun(*this, &LayerSelector::_prepareLabelRenderer)
- );
-
- _selection_changed_connection = _selector.signal_changed().connect(
- sigc::mem_fun(*this, &LayerSelector::_setDesktopLayer)
- );
- setDesktop(desktop);
-}
-
-/** Destructor - disconnects signal handler
- */
-LayerSelector::~LayerSelector() {
- setDesktop(NULL);
- _selection_changed_connection.disconnect();
-}
-
-namespace {
-
-/** Helper function - detaches desktop from selector
- */
-bool detach(LayerSelector *selector) {
- selector->setDesktop(NULL);
- return FALSE;
-}
-
-}
-
-/** Sets the desktop for the widget. First disconnects signals
- * for the current desktop, then stores the pointer to the
- * given \a desktop, and attaches its signals to this one.
- * Then it selects the current layer for the desktop.
- */
-void LayerSelector::setDesktop(SPDesktop *desktop) {
- if ( desktop == _desktop ) {
- return;
- }
-
- if (_desktop) {
-// _desktop_shutdown_connection.disconnect();
- _layer_changed_connection.disconnect();
-// g_signal_handlers_disconnect_by_func(_desktop, (gpointer)&detach, this);
- }
- _desktop = desktop;
- if (_desktop) {
- // TODO we need a different signal for this, really..s
-// _desktop_shutdown_connection = _desktop->connectShutdown(
-// sigc::bind (sigc::ptr_fun (detach), this));
-// g_signal_connect_after(_desktop, "shutdown", GCallback(detach), this);
-
- _layer_changed_connection = _desktop->connectCurrentLayerChanged(
- sigc::mem_fun(*this, &LayerSelector::_selectLayer)
- );
- _selectLayer(_desktop->currentLayer());
- }
-}
-
-namespace {
-
-class is_layer {
-public:
- is_layer(SPDesktop *desktop) : _desktop(desktop) {}
- bool operator()(SPObject &object) const {
- return _desktop->isLayer(&object);
- }
-private:
- SPDesktop *_desktop;
-};
-
-class column_matches_object {
-public:
- column_matches_object(Gtk::TreeModelColumn<SPObject *> const &column,
- SPObject &object)
- : _column(column), _object(object) {}
- bool operator()(Gtk::TreeModel::const_iterator const &iter) const {
- SPObject *current=(*iter)[_column];
- return current == &_object;
- }
-private:
- Gtk::TreeModelColumn<SPObject *> const &_column;
- SPObject &_object;
-};
-
-}
-
-/** Selects the given layer in the dropdown selector.
- */
-void LayerSelector::_selectLayer(SPObject *layer) {
- using Inkscape::Util::List;
- using Inkscape::Util::cons;
- using Inkscape::Util::reverse_list;
-
- _selection_changed_connection.block();
-
- while (!_layer_model->children().empty()) {
- Gtk::ListStore::iterator first_row(_layer_model->children().begin());
- _destroyEntry(first_row);
- _layer_model->erase(first_row);
- }
-
- SPObject *root=_desktop->currentRoot();
-
- if (_layer) {
- sp_object_unref(_layer, NULL);
- _layer = NULL;
- }
-
- if (layer) {
- List<SPObject &> hierarchy=reverse_list<SPObject::ParentIterator>(layer, root);
- if ( layer == root ) {
- _buildEntries(0, cons(*root, hierarchy));
- } else if (hierarchy) {
- _buildSiblingEntries(0, *root, hierarchy);
- }
-
- Gtk::TreeIter row(
- std::find_if(
- _layer_model->children().begin(),
- _layer_model->children().end(),
- column_matches_object(_model_columns.object, *layer)
- )
- );
- if ( row != _layer_model->children().end() ) {
- _selector.set_active(row);
- }
-
- _layer = layer;
- sp_object_ref(_layer, NULL);
- }
-
- if ( !layer || layer == root ) {
- _visibility_toggle.set_sensitive(false);
- _visibility_toggle.set_active(false);
- _lock_toggle.set_sensitive(false);
- _lock_toggle.set_active(false);
- } else {
- _visibility_toggle.set_sensitive(true);
- _visibility_toggle.set_active(( SP_IS_ITEM(layer) ? SP_ITEM(layer)->isHidden() : false ));
- _lock_toggle.set_sensitive(true);
- _lock_toggle.set_active(( SP_IS_ITEM(layer) ? SP_ITEM(layer)->isLocked() : false ));
- }
-
- _selection_changed_connection.unblock();
-}
-
-/** Sets the current desktop layer to the actively selected layer.
- */
-void LayerSelector::_setDesktopLayer() {
- Gtk::ListStore::iterator selected(_selector.get_active());
- SPObject *layer=_selector.get_active()->get_value(_model_columns.object);
- if ( _desktop && layer ) {
- _layer_changed_connection.block();
-
- _desktop->layer_manager->setCurrentLayer(layer);
-
- _layer_changed_connection.unblock();
-
- _selectLayer(_desktop->currentLayer());
- }
- if (_desktop && _desktop->canvas) {
- gtk_widget_grab_focus (GTK_WIDGET(_desktop->canvas));
- }
-}
-
-/** Creates rows in the _layer_model data structure for each item
- * in \a hierarchy, to a given \a depth.
- */
-void LayerSelector::_buildEntries(unsigned depth,
- Inkscape::Util::List<SPObject &> hierarchy)
-{
- using Inkscape::Util::List;
- using Inkscape::Util::rest;
-
- _buildEntry(depth, *hierarchy);
-
- List<SPObject &> remainder=rest(hierarchy);
- if (remainder) {
- _buildEntries(depth+1, remainder);
- } else {
- _buildSiblingEntries(depth+1, *hierarchy, remainder);
- }
-}
-
-/** Creates entries in the _layer_model data structure for
- * all siblings of the first child in \a parent.
- */
-void LayerSelector::_buildSiblingEntries(
- unsigned depth, SPObject &parent,
- Inkscape::Util::List<SPObject &> hierarchy
-) {
- using Inkscape::Util::List;
- using Inkscape::Util::rest;
- using Inkscape::Util::reverse_list_in_place;
- using Inkscape::Util::filter_list;
-
- Inkscape::Util::List<SPObject &> siblings(
- reverse_list_in_place(
- filter_list<SPObject::SiblingIterator>(
- is_layer(_desktop), parent.firstChild(), NULL
- )
- )
- );
-
- SPObject *layer( hierarchy ? &*hierarchy : NULL );
-
- while (siblings) {
- _buildEntry(depth, *siblings);
- if ( &*siblings == layer ) {
- _buildSiblingEntries(depth+1, *layer, rest(hierarchy));
- }
- ++siblings;
- }
-}
-
-namespace {
-
-struct Callbacks {
- sigc::slot<void> update_row;
- sigc::slot<void> update_list;
-};
-
-void attribute_changed(Inkscape::XML::Node */*repr*/, gchar const *name,
- gchar const */*old_value*/, gchar const */*new_value*/,
- bool /*is_interactive*/, void *data)
-{
- if ( !std::strcmp(name, "inkscape:groupmode") ) {
- reinterpret_cast<Callbacks *>(data)->update_list();
- } else {
- reinterpret_cast<Callbacks *>(data)->update_row();
- }
-}
-
-void node_added(Inkscape::XML::Node */*parent*/, Inkscape::XML::Node *child, Inkscape::XML::Node */*ref*/, void *data) {
- gchar const *mode=child->attribute("inkscape:groupmode");
- if ( mode && !std::strcmp(mode, "layer") ) {
- reinterpret_cast<Callbacks *>(data)->update_list();
- }
-}
-
-void node_removed(Inkscape::XML::Node */*parent*/, Inkscape::XML::Node *child, Inkscape::XML::Node */*ref*/, void *data) {
- gchar const *mode=child->attribute("inkscape:groupmode");
- if ( mode && !std::strcmp(mode, "layer") ) {
- reinterpret_cast<Callbacks *>(data)->update_list();
- }
-}
-
-void node_reordered(Inkscape::XML::Node */*parent*/, Inkscape::XML::Node *child,
- Inkscape::XML::Node */*old_ref*/, Inkscape::XML::Node */*new_ref*/,
- void *data)
-{
- gchar const *mode=child->attribute("inkscape:groupmode");
- if ( mode && !std::strcmp(mode, "layer") ) {
- reinterpret_cast<Callbacks *>(data)->update_list();
- }
-}
-
-void update_row_for_object(SPObject *object,
- Gtk::TreeModelColumn<SPObject *> const &column,
- Glib::RefPtr<Gtk::ListStore> const &model)
-{
- Gtk::TreeIter row(
- std::find_if(
- model->children().begin(),
- model->children().end(),
- column_matches_object(column, *object)
- )
- );
- if ( row != model->children().end() ) {
- model->row_changed(model->get_path(row), row);
- }
-}
-
-void rebuild_all_rows(sigc::slot<void, SPObject *> rebuild, SPDesktop *desktop)
-{
- rebuild(desktop->currentLayer());
-}
-
-}
-
-void LayerSelector::_protectUpdate(sigc::slot<void> slot) {
- bool visibility_blocked=_visibility_toggled_connection.blocked();
- bool lock_blocked=_lock_toggled_connection.blocked();
- _visibility_toggled_connection.block(true);
- _lock_toggled_connection.block(true);
- slot();
-
- SPObject *layer = _desktop ? _desktop->currentLayer() : 0;
- if ( layer ) {
- bool wantedValue = ( SP_IS_ITEM(layer) ? SP_ITEM(layer)->isLocked() : false );
- if ( _lock_toggle.get_active() != wantedValue ) {
- _lock_toggle.set_active( wantedValue );
- }
- wantedValue = ( SP_IS_ITEM(layer) ? SP_ITEM(layer)->isHidden() : false );
- if ( _visibility_toggle.get_active() != wantedValue ) {
- _visibility_toggle.set_active( wantedValue );
- }
- }
- _visibility_toggled_connection.block(visibility_blocked);
- _lock_toggled_connection.block(lock_blocked);
-}
-
-/** Builds and appends a row in the layer model object.
- */
-void LayerSelector::_buildEntry(unsigned depth, SPObject &object) {
- Inkscape::XML::NodeEventVector *vector;
-
- Callbacks *callbacks=new Callbacks();
-
- callbacks->update_row = sigc::bind(
- sigc::mem_fun(*this, &LayerSelector::_protectUpdate),
- sigc::bind(
- sigc::ptr_fun(&update_row_for_object),
- &object, _model_columns.object, _layer_model
- )
- );
-
- SPObject *layer=_desktop->currentLayer();
- if ( &object == layer || &object == SP_OBJECT_PARENT(layer) ) {
- callbacks->update_list = sigc::bind(
- sigc::mem_fun(*this, &LayerSelector::_protectUpdate),
- sigc::bind(
- sigc::ptr_fun(&rebuild_all_rows),
- sigc::mem_fun(*this, &LayerSelector::_selectLayer),
- _desktop
- )
- );
-
- Inkscape::XML::NodeEventVector events = {
- &node_added,
- &node_removed,
- &attribute_changed,
- NULL,
- &node_reordered
- };
-
- vector = new Inkscape::XML::NodeEventVector(events);
- } else {
- Inkscape::XML::NodeEventVector events = {
- NULL,
- NULL,
- &attribute_changed,
- NULL,
- NULL
- };
-
- vector = new Inkscape::XML::NodeEventVector(events);
- }
-
- Gtk::ListStore::iterator row(_layer_model->append());
-
- row->set_value(_model_columns.depth, depth);
-
- sp_object_ref(&object, NULL);
- row->set_value(_model_columns.object, &object);
-
- Inkscape::GC::anchor(SP_OBJECT_REPR(&object));
- row->set_value(_model_columns.repr, SP_OBJECT_REPR(&object));
-
- row->set_value(_model_columns.callbacks, reinterpret_cast<void *>(callbacks));
-
- sp_repr_add_listener(SP_OBJECT_REPR(&object), vector, callbacks);
-}
-
-/** Removes a row from the _model_columns object, disconnecting listeners
- * on the slot.
- */
-void LayerSelector::_destroyEntry(Gtk::ListStore::iterator const &row) {
- Callbacks *callbacks=reinterpret_cast<Callbacks *>(row->get_value(_model_columns.callbacks));
- SPObject *object=row->get_value(_model_columns.object);
- if (object) {
- sp_object_unref(object, NULL);
- }
- Inkscape::XML::Node *repr=row->get_value(_model_columns.repr);
- if (repr) {
- sp_repr_remove_listener_by_data(repr, callbacks);
- Inkscape::GC::release(repr);
- }
- delete callbacks;
-}
-
-/** Formats the label for a given layer row
- */
-void LayerSelector::_prepareLabelRenderer(
- Gtk::TreeModel::const_iterator const &row
-) {
- unsigned depth=(*row)[_model_columns.depth];
- SPObject *object=(*row)[_model_columns.object];
- bool label_defaulted(false);
-
- // TODO: when the currently selected row is removed,
- // (or before one has been selected) something appears to
- // "invent" an iterator with null data and try to render it;
- // where does it come from, and how can we avoid it?
- if ( object && SP_OBJECT_REPR(object) ) {
- SPObject *layer=( _desktop ? _desktop->currentLayer() : NULL );
- SPObject *root=( _desktop ? _desktop->currentRoot() : NULL );
-
- bool isancestor = !( (layer && (SP_OBJECT_PARENT(object) == SP_OBJECT_PARENT(layer))) || ((layer == root) && (SP_OBJECT_PARENT(object) == root)));
-
- bool iscurrent = ( object == layer && object != root );
-
- gchar *format = g_strdup_printf (
- "<span size=\"smaller\" %s><tt>%*s%s</tt>%s%s%s%%s%s%s%s</span>",
- ( _desktop && _desktop->itemIsHidden (SP_ITEM(object)) ? "foreground=\"gray50\"" : "" ),
- depth, "", ( iscurrent ? "&#8226;" : " " ),
- ( iscurrent ? "<b>" : "" ),
- ( SP_ITEM(object)->isLocked() ? "[" : "" ),
- ( isancestor ? "<small>" : "" ),
- ( isancestor ? "</small>" : "" ),
- ( SP_ITEM(object)->isLocked() ? "]" : "" ),
- ( iscurrent ? "</b>" : "" )
- );
-
- gchar const *label;
- if ( object != root ) {
- label = object->label();
- if (!label) {
- label = object->defaultLabel();
- label_defaulted = true;
- }
- } else {
- label = _("(root)");
- }
-
- gchar *text = g_markup_printf_escaped(format, label);
- _label_renderer.property_markup() = text;
- g_free(text);
- g_free(format);
- } else {
- _label_renderer.property_markup() = "<small> </small>";
- }
-
- _label_renderer.property_ypad() = 1;
- _label_renderer.property_style() = ( label_defaulted ?
- Pango::STYLE_ITALIC :
- Pango::STYLE_NORMAL );
-}
-
-void LayerSelector::_lockLayer(bool lock) {
- if ( _layer && SP_IS_ITEM(_layer) ) {
- SP_ITEM(_layer)->setLocked(lock);
- sp_document_done(sp_desktop_document(_desktop), SP_VERB_NONE,
- lock? _("Lock layer") : _("Unlock layer"));
- }
-}
-
-void LayerSelector::_hideLayer(bool hide) {
- if ( _layer && SP_IS_ITEM(_layer) ) {
- SP_ITEM(_layer)->setHidden(hide);
- sp_document_done(sp_desktop_document(_desktop), SP_VERB_NONE,
- hide? _("Hide layer") : _("Unhide layer"));
- }
-}
-
-}
-}
-
-/*
- 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
deleted file mode 100644
index 0b5300272..000000000
--- a/src/widgets/layer-selector.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Inkscape::Widgets::LayerSelector - layer selector widget
- *
- * Authors:
- * MenTaLguY <mental@rydia.net>
- *
- * Copyright (C) 2004 MenTaLguY
- *
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#ifndef SEEN_INKSCAPE_WIDGETS_LAYER_SELECTOR
-#define SEEN_INKSCAPE_WIDGETS_LAYER_SELECTOR
-
-#include <gtkmm/box.h>
-#include <gtkmm/combobox.h>
-#include <gtkmm/togglebutton.h>
-#include <gtkmm/tooltips.h>
-#include <gtkmm/cellrenderertext.h>
-#include <gtkmm/treemodel.h>
-#include <gtkmm/liststore.h>
-#include <sigc++/slot.h>
-#include "util/list.h"
-
-class SPDesktop;
-class SPDocument;
-class SPObject;
-namespace Inkscape {
-namespace XML {
-class Node;
-}
-}
-
-
-namespace Inkscape {
-namespace Widgets {
-
-class DocumentTreeModel;
-
-class LayerSelector : public Gtk::HBox {
-public:
- LayerSelector(SPDesktop *desktop = NULL);
- ~LayerSelector();
-
- SPDesktop *desktop() { return _desktop; }
- void setDesktop(SPDesktop *desktop);
-
-private:
- class LayerModelColumns : public Gtk::TreeModel::ColumnRecord {
- public:
- Gtk::TreeModelColumn<unsigned> depth;
- Gtk::TreeModelColumn<SPObject *> object;
- Gtk::TreeModelColumn<Inkscape::XML::Node *> repr;
- Gtk::TreeModelColumn<void *> callbacks;
-
- LayerModelColumns() {
- add(depth); add(object); add(repr); add(callbacks);
- }
- };
-
- SPDesktop *_desktop;
-
- Gtk::Tooltips _tooltips;
- Gtk::ComboBox _selector;
- Gtk::ToggleButton _visibility_toggle;
- Gtk::ToggleButton _lock_toggle;
-
- LayerModelColumns _model_columns;
- Gtk::CellRendererText _label_renderer;
- Glib::RefPtr<Gtk::ListStore> _layer_model;
-
-// sigc::connection _desktop_shutdown_connection;
- sigc::connection _layer_changed_connection;
- sigc::connection _selection_changed_connection;
- sigc::connection _visibility_toggled_connection;
- sigc::connection _lock_toggled_connection;
-
- SPObject *_layer;
-
- void _selectLayer(SPObject *layer);
- void _setDesktopLayer();
-
- void _buildEntry(unsigned depth, SPObject &object);
- void _buildEntries(unsigned depth,
- Inkscape::Util::List<SPObject &> hierarchy);
- void _buildSiblingEntries(unsigned depth,
- SPObject &parent,
- Inkscape::Util::List<SPObject &> hierarchy);
- void _protectUpdate(sigc::slot<void> slot);
- void _destroyEntry(Gtk::ListStore::iterator const &row);
- void _hideLayer(bool hide);
- void _lockLayer(bool lock);
-
- void _prepareLabelRenderer(Gtk::TreeModel::const_iterator const &row);
-};
-
-}
-}
-
-#endif
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/widgets/sp-attribute-widget.cpp b/src/widgets/sp-attribute-widget.cpp
new file mode 100644
index 000000000..de14fc173
--- /dev/null
+++ b/src/widgets/sp-attribute-widget.cpp
@@ -0,0 +1,790 @@
+/** @file
+ * @brief Widget that listens and modifies repr attributes
+ */
+/* Authors:
+ * Lauris Kaplinski <lauris@ximian.com>
+ *
+ * Copyright (C) 2001 Ximian, Inc.
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <gtk/gtktable.h>
+#include <gtk/gtklabel.h>
+#include "xml/repr.h"
+#include "macros.h"
+#include "document.h"
+#include "sp-object.h"
+#include <glibmm/i18n.h>
+
+#include <sigc++/functors/ptr_fun.h>
+#include <sigc++/adaptors/bind.h>
+
+#include "sp-attribute-widget.h"
+
+static void sp_attribute_widget_class_init (SPAttributeWidgetClass *klass);
+static void sp_attribute_widget_init (SPAttributeWidget *widget);
+static void sp_attribute_widget_destroy (GtkObject *object);
+
+static void sp_attribute_widget_changed (GtkEditable *editable);
+
+static void sp_attribute_widget_object_modified ( SPObject *object,
+ guint flags,
+ SPAttributeWidget *spaw );
+static void sp_attribute_widget_object_release ( SPObject *object,
+ SPAttributeWidget *spaw );
+
+static GtkEntryClass *parent_class;
+
+
+
+
+GType sp_attribute_widget_get_type(void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GTypeInfo info = {
+ sizeof(SPAttributeWidgetClass),
+ 0, // base_init
+ 0, // base_finalize
+ (GClassInitFunc)sp_attribute_widget_class_init,
+ 0, // class_finalize
+ 0, // class_data
+ sizeof(SPAttributeWidget),
+ 0, // n_preallocs
+ (GInstanceInitFunc)sp_attribute_widget_init,
+ 0 // value_table
+ };
+ type = g_type_register_static(GTK_TYPE_ENTRY, "SPAttributeWidget", &info, static_cast<GTypeFlags>(0));
+ }
+ return type;
+} // end of sp_attribute_widget_get_type()
+
+
+
+static void
+sp_attribute_widget_class_init (SPAttributeWidgetClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkEditableClass *editable_class;
+
+ object_class = GTK_OBJECT_CLASS (klass);
+ widget_class = GTK_WIDGET_CLASS (klass);
+ editable_class = GTK_EDITABLE_CLASS (klass);
+
+ parent_class = (GtkEntryClass*)gtk_type_class (GTK_TYPE_ENTRY);
+
+ object_class->destroy = sp_attribute_widget_destroy;
+
+ editable_class->changed = sp_attribute_widget_changed;
+
+} // end of sp_attribute_widget_class_init()
+
+
+
+static void
+sp_attribute_widget_init (SPAttributeWidget *spaw)
+{
+ spaw->blocked = FALSE;
+ spaw->hasobj = FALSE;
+
+ spaw->src.object = NULL;
+
+ spaw->attribute = NULL;
+
+ new (&spaw->modified_connection) sigc::connection();
+ new (&spaw->release_connection) sigc::connection();
+}
+
+
+
+static void
+sp_attribute_widget_destroy (GtkObject *object)
+{
+
+ SPAttributeWidget *spaw;
+
+ spaw = SP_ATTRIBUTE_WIDGET (object);
+
+ if (spaw->attribute) {
+ g_free (spaw->attribute);
+ spaw->attribute = NULL;
+ }
+
+
+ if (spaw->hasobj) {
+
+ if (spaw->src.object) {
+ spaw->modified_connection.disconnect();
+ spaw->release_connection.disconnect();
+ spaw->src.object = NULL;
+ }
+ } else {
+
+ if (spaw->src.repr) {
+ spaw->src.repr = Inkscape::GC::release(spaw->src.repr);
+ }
+ } // end of if()
+
+ spaw->modified_connection.~connection();
+ spaw->release_connection.~connection();
+
+ ((GtkObjectClass *) parent_class)->destroy (object);
+
+}
+
+
+
+static void
+sp_attribute_widget_changed (GtkEditable *editable)
+{
+
+ SPAttributeWidget *spaw;
+
+ spaw = SP_ATTRIBUTE_WIDGET (editable);
+
+ if (!spaw->blocked) {
+
+ const gchar *text;
+ spaw->blocked = TRUE;
+ text = gtk_entry_get_text (GTK_ENTRY (spaw));
+ if (!*text)
+ text = NULL;
+
+ if (spaw->hasobj && spaw->src.object) {
+
+ SP_OBJECT_REPR (spaw->src.object)->setAttribute(spaw->attribute, text, false);
+ sp_document_done (SP_OBJECT_DOCUMENT (spaw->src.object), SP_VERB_NONE,
+ _("Set attribute"));
+
+ } else if (spaw->src.repr) {
+
+ spaw->src.repr->setAttribute(spaw->attribute, text, false);
+ /* TODO: Warning! Undo will not be flushed in given case */
+ }
+ spaw->blocked = FALSE;
+ }
+
+} // end of sp_attribute_widget_changed()
+
+
+
+GtkWidget *
+sp_attribute_widget_new ( SPObject *object, const gchar *attribute )
+{
+ SPAttributeWidget *spaw;
+
+ g_return_val_if_fail (!object || SP_IS_OBJECT (object), NULL);
+ g_return_val_if_fail (!object || attribute, NULL);
+
+ spaw = (SPAttributeWidget*)gtk_type_new (SP_TYPE_ATTRIBUTE_WIDGET);
+
+ sp_attribute_widget_set_object (spaw, object, attribute);
+
+ return GTK_WIDGET (spaw);
+
+} // end of sp_attribute_widget_new()
+
+
+
+GtkWidget *
+sp_attribute_widget_new_repr ( Inkscape::XML::Node *repr, const gchar *attribute )
+{
+ SPAttributeWidget *spaw;
+
+ spaw = (SPAttributeWidget*)gtk_type_new (SP_TYPE_ATTRIBUTE_WIDGET);
+
+ sp_attribute_widget_set_repr (spaw, repr, attribute);
+
+ return GTK_WIDGET (spaw);
+}
+
+
+
+void
+sp_attribute_widget_set_object ( SPAttributeWidget *spaw,
+ SPObject *object,
+ const gchar *attribute )
+{
+
+ g_return_if_fail (spaw != NULL);
+ g_return_if_fail (SP_IS_ATTRIBUTE_WIDGET (spaw));
+ g_return_if_fail (!object || SP_IS_OBJECT (object));
+ g_return_if_fail (!object || attribute);
+ g_return_if_fail (attribute != NULL);
+
+ if (spaw->attribute) {
+ g_free (spaw->attribute);
+ spaw->attribute = NULL;
+ }
+
+ if (spaw->hasobj) {
+
+ if (spaw->src.object) {
+ spaw->modified_connection.disconnect();
+ spaw->release_connection.disconnect();
+ spaw->src.object = NULL;
+ }
+ } else {
+
+ if (spaw->src.repr) {
+ spaw->src.repr = Inkscape::GC::release(spaw->src.repr);
+ }
+ }
+
+ spaw->hasobj = TRUE;
+
+ if (object) {
+ const gchar *val;
+
+ spaw->blocked = TRUE;
+ spaw->src.object = object;
+
+ spaw->modified_connection = object->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_attribute_widget_object_modified), spaw));
+ spaw->release_connection = object->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_attribute_widget_object_release), spaw));
+
+ spaw->attribute = g_strdup (attribute);
+
+ val = SP_OBJECT_REPR (object)->attribute(attribute);
+ gtk_entry_set_text (GTK_ENTRY (spaw), val ? val : (const gchar *) "");
+ spaw->blocked = FALSE;
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (spaw), (spaw->src.object != NULL));
+
+} // end of sp_attribute_widget_set_object()
+
+
+
+void
+sp_attribute_widget_set_repr ( SPAttributeWidget *spaw,
+ Inkscape::XML::Node *repr,
+ const gchar *attribute )
+{
+
+ g_return_if_fail (spaw != NULL);
+ g_return_if_fail (SP_IS_ATTRIBUTE_WIDGET (spaw));
+ g_return_if_fail (attribute != NULL);
+
+ if (spaw->attribute) {
+ g_free (spaw->attribute);
+ spaw->attribute = NULL;
+ }
+
+ if (spaw->hasobj) {
+
+ if (spaw->src.object) {
+ spaw->modified_connection.disconnect();
+ spaw->release_connection.disconnect();
+ spaw->src.object = NULL;
+ }
+ } else {
+
+ if (spaw->src.repr) {
+ spaw->src.repr = Inkscape::GC::release(spaw->src.repr);
+ }
+ }
+
+ spaw->hasobj = FALSE;
+
+ if (repr) {
+ const gchar *val;
+
+ spaw->blocked = TRUE;
+ spaw->src.repr = Inkscape::GC::anchor(repr);
+ spaw->attribute = g_strdup (attribute);
+
+ val = repr->attribute(attribute);
+ gtk_entry_set_text (GTK_ENTRY (spaw), val ? val : (const gchar *) "");
+ spaw->blocked = FALSE;
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (spaw), (spaw->src.repr != NULL));
+
+} // end of sp_attribute_widget_set_repr()
+
+
+
+static void
+sp_attribute_widget_object_modified ( SPObject */*object*/,
+ guint flags,
+ SPAttributeWidget *spaw )
+{
+
+ if (flags && SP_OBJECT_MODIFIED_FLAG) {
+
+ const gchar *val, *text;
+ val = SP_OBJECT_REPR (spaw->src.object)->attribute(spaw->attribute);
+ text = gtk_entry_get_text (GTK_ENTRY (spaw));
+
+ if (val || text) {
+
+ if (!val || !text || strcmp (val, text)) {
+ /* We are different */
+ spaw->blocked = TRUE;
+ gtk_entry_set_text ( GTK_ENTRY (spaw),
+ val ? val : (const gchar *) "");
+ spaw->blocked = FALSE;
+ } // end of if()
+
+ } // end of if()
+
+ } //end of if()
+
+} // end of sp_attribute_widget_object_modified()
+
+
+
+static void
+sp_attribute_widget_object_release ( SPObject */*object*/,
+ SPAttributeWidget *spaw )
+{
+ sp_attribute_widget_set_object (spaw, NULL, NULL);
+}
+
+
+
+/* SPAttributeTable */
+
+static void sp_attribute_table_class_init (SPAttributeTableClass *klass);
+static void sp_attribute_table_init (SPAttributeTable *widget);
+static void sp_attribute_table_destroy (GtkObject *object);
+
+static void sp_attribute_table_object_modified (SPObject *object, guint flags, SPAttributeTable *spaw);
+static void sp_attribute_table_object_release (SPObject *object, SPAttributeTable *spaw);
+static void sp_attribute_table_entry_changed (GtkEditable *editable, SPAttributeTable *spat);
+
+static GtkVBoxClass *table_parent_class;
+
+
+
+
+GType sp_attribute_table_get_type(void)
+{
+ static GtkType type = 0;
+ if (!type) {
+ GTypeInfo info = {
+ sizeof(SPAttributeTableClass),
+ 0, // base_init
+ 0, // base_finalize
+ (GClassInitFunc)sp_attribute_table_class_init,
+ 0, // class_finalize
+ 0, // class_data
+ sizeof(SPAttributeTable),
+ 0, // n_preallocs
+ (GInstanceInitFunc)sp_attribute_table_init,
+ 0 // value_table
+ };
+ type = g_type_register_static(GTK_TYPE_VBOX, "SPAttributeTable", &info, static_cast<GTypeFlags>(0));
+ }
+ return type;
+} // end of sp_attribute_table_get_type()
+
+
+
+static void
+sp_attribute_table_class_init (SPAttributeTableClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = GTK_OBJECT_CLASS (klass);
+ widget_class = GTK_WIDGET_CLASS (klass);
+
+ table_parent_class = (GtkVBoxClass*)gtk_type_class (GTK_TYPE_VBOX);
+
+ object_class->destroy = sp_attribute_table_destroy;
+
+} // end of sp_attribute_table_class_init()
+
+
+
+static void
+sp_attribute_table_init ( SPAttributeTable *spat )
+{
+ spat->blocked = FALSE;
+ spat->hasobj = FALSE;
+ spat->table = NULL;
+ spat->src.object = NULL;
+ spat->num_attr = 0;
+ spat->attributes = NULL;
+ spat->entries = NULL;
+
+ new (&spat->modified_connection) sigc::connection();
+ new (&spat->release_connection) sigc::connection();
+}
+
+static void
+sp_attribute_table_destroy ( GtkObject *object )
+{
+ SPAttributeTable *spat;
+
+ spat = SP_ATTRIBUTE_TABLE (object);
+
+ if (spat->attributes) {
+ gint i;
+ for (i = 0; i < spat->num_attr; i++) {
+ g_free (spat->attributes[i]);
+ }
+ g_free (spat->attributes);
+ spat->attributes = NULL;
+ }
+
+ if (spat->hasobj) {
+
+ if (spat->src.object) {
+ spat->modified_connection.disconnect();
+ spat->release_connection.disconnect();
+ spat->src.object = NULL;
+ }
+ } else {
+ if (spat->src.repr) {
+ spat->src.repr = Inkscape::GC::release(spat->src.repr);
+ }
+ } // end of if()
+
+ spat->modified_connection.~connection();
+ spat->release_connection.~connection();
+
+ if (spat->entries) {
+ g_free (spat->entries);
+ spat->entries = NULL;
+ }
+
+ spat->table = NULL;
+
+ if (((GtkObjectClass *) table_parent_class)->destroy) {
+ (* ((GtkObjectClass *) table_parent_class)->destroy) (object);
+ }
+
+} // end of sp_attribute_table_destroy()
+
+
+GtkWidget *
+sp_attribute_table_new ( SPObject *object,
+ gint num_attr,
+ const gchar **labels,
+ const gchar **attributes )
+{
+ SPAttributeTable *spat;
+
+ g_return_val_if_fail (!object || SP_IS_OBJECT (object), NULL);
+ g_return_val_if_fail (!object || (num_attr > 0), NULL);
+ g_return_val_if_fail (!num_attr || (labels && attributes), NULL);
+
+ spat = (SPAttributeTable*)gtk_type_new (SP_TYPE_ATTRIBUTE_TABLE);
+
+ sp_attribute_table_set_object (spat, object, num_attr, labels, attributes);
+
+ return GTK_WIDGET (spat);
+
+} // end of sp_attribute_table_new()
+
+
+
+GtkWidget *
+sp_attribute_table_new_repr ( Inkscape::XML::Node *repr,
+ gint num_attr,
+ const gchar **labels,
+ const gchar **attributes )
+{
+ SPAttributeTable *spat;
+
+ g_return_val_if_fail (!num_attr || (labels && attributes), NULL);
+
+ spat = (SPAttributeTable*)gtk_type_new (SP_TYPE_ATTRIBUTE_TABLE);
+
+ sp_attribute_table_set_repr (spat, repr, num_attr, labels, attributes);
+
+ return GTK_WIDGET (spat);
+
+} // end of sp_attribute_table_new_repr()
+
+
+
+#define XPAD 4
+#define YPAD 0
+
+void
+sp_attribute_table_set_object ( SPAttributeTable *spat,
+ SPObject *object,
+ gint num_attr,
+ const gchar **labels,
+ const gchar **attributes )
+{
+
+ g_return_if_fail (spat != NULL);
+ g_return_if_fail (SP_IS_ATTRIBUTE_TABLE (spat));
+ g_return_if_fail (!object || SP_IS_OBJECT (object));
+ g_return_if_fail (!object || (num_attr > 0));
+ g_return_if_fail (!num_attr || (labels && attributes));
+
+ if (spat->table) {
+ gtk_widget_destroy (spat->table);
+ spat->table = NULL;
+ }
+
+ if (spat->attributes) {
+ gint i;
+ for (i = 0; i < spat->num_attr; i++) {
+ g_free (spat->attributes[i]);
+ }
+ g_free (spat->attributes);
+ spat->attributes = NULL;
+ }
+
+ if (spat->entries) {
+ g_free (spat->entries);
+ spat->entries = NULL;
+ }
+
+ if (spat->hasobj) {
+ if (spat->src.object) {
+ spat->modified_connection.disconnect();
+ spat->release_connection.disconnect();
+ spat->src.object = NULL;
+ }
+ } else {
+ if (spat->src.repr) {
+ spat->src.repr = Inkscape::GC::release(spat->src.repr);
+ }
+ }
+
+ spat->hasobj = TRUE;
+
+ if (object) {
+ gint i;
+
+ spat->blocked = TRUE;
+
+ /* Set up object */
+ spat->src.object = object;
+ spat->num_attr = num_attr;
+
+ spat->modified_connection = object->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_attribute_table_object_modified), spat));
+ spat->release_connection = object->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_attribute_table_object_release), spat));
+
+ /* Create table */
+ spat->table = gtk_table_new (num_attr, 2, FALSE);
+ gtk_container_add (GTK_CONTAINER (spat), spat->table);
+ /* Arrays */
+ spat->attributes = g_new0 (gchar *, num_attr);
+ spat->entries = g_new0 (GtkWidget *, num_attr);
+ /* Fill rows */
+ for (i = 0; i < num_attr; i++) {
+ GtkWidget *w;
+ const gchar *val;
+
+ spat->attributes[i] = g_strdup (attributes[i]);
+ w = gtk_label_new (_(labels[i]));
+ gtk_widget_show (w);
+ gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
+ gtk_table_attach ( GTK_TABLE (spat->table), w, 0, 1, i, i + 1,
+ GTK_FILL,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ XPAD, YPAD );
+ w = gtk_entry_new ();
+ gtk_widget_show (w);
+ val = SP_OBJECT_REPR (object)->attribute(attributes[i]);
+ gtk_entry_set_text (GTK_ENTRY (w), val ? val : (const gchar *) "");
+ gtk_table_attach ( GTK_TABLE (spat->table), w, 1, 2, i, i + 1,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ XPAD, YPAD );
+ spat->entries[i] = w;
+ g_signal_connect ( G_OBJECT (w), "changed",
+ G_CALLBACK (sp_attribute_table_entry_changed),
+ spat );
+ }
+ /* Show table */
+ gtk_widget_show (spat->table);
+
+ spat->blocked = FALSE;
+ }
+
+ gtk_widget_set_sensitive ( GTK_WIDGET (spat),
+ (spat->src.object != NULL) );
+
+} // end of sp_attribute_table_set_object()
+
+
+
+void
+sp_attribute_table_set_repr ( SPAttributeTable *spat,
+ Inkscape::XML::Node *repr,
+ gint num_attr,
+ const gchar **labels,
+ const gchar **attributes )
+{
+ g_return_if_fail (spat != NULL);
+ g_return_if_fail (SP_IS_ATTRIBUTE_TABLE (spat));
+ g_return_if_fail (!num_attr || (labels && attributes));
+
+ if (spat->table) {
+ gtk_widget_destroy (spat->table);
+ spat->table = NULL;
+ }
+
+ if (spat->attributes) {
+ gint i;
+ for (i = 0; i < spat->num_attr; i++) {
+ g_free (spat->attributes[i]);
+ }
+ g_free (spat->attributes);
+ spat->attributes = NULL;
+ }
+
+ if (spat->entries) {
+ g_free (spat->entries);
+ spat->entries = NULL;
+ }
+
+ if (spat->hasobj) {
+ if (spat->src.object) {
+ spat->modified_connection.disconnect();
+ spat->release_connection.disconnect();
+ spat->src.object = NULL;
+ }
+ } else {
+ if (spat->src.repr) {
+ spat->src.repr = Inkscape::GC::release(spat->src.repr);
+ }
+ }
+
+ spat->hasobj = FALSE;
+
+ if (repr) {
+ gint i;
+
+ spat->blocked = TRUE;
+
+ /* Set up repr */
+ spat->src.repr = Inkscape::GC::anchor(repr);
+ spat->num_attr = num_attr;
+ /* Create table */
+ spat->table = gtk_table_new (num_attr, 2, FALSE);
+ gtk_container_add (GTK_CONTAINER (spat), spat->table);
+ /* Arrays */
+ spat->attributes = g_new0 (gchar *, num_attr);
+ spat->entries = g_new0 (GtkWidget *, num_attr);
+
+ /* Fill rows */
+ for (i = 0; i < num_attr; i++) {
+ GtkWidget *w;
+ const gchar *val;
+
+ spat->attributes[i] = g_strdup (attributes[i]);
+ w = gtk_label_new (labels[i]);
+ gtk_widget_show (w);
+ gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
+ gtk_table_attach ( GTK_TABLE (spat->table), w, 0, 1, i, i + 1,
+ GTK_FILL,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ XPAD, YPAD );
+ w = gtk_entry_new ();
+ gtk_widget_show (w);
+ val = repr->attribute(attributes[i]);
+ gtk_entry_set_text (GTK_ENTRY (w), val ? val : (const gchar *) "");
+ gtk_table_attach ( GTK_TABLE (spat->table), w, 1, 2, i, i + 1,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ XPAD, YPAD );
+ spat->entries[i] = w;
+ g_signal_connect ( G_OBJECT (w), "changed",
+ G_CALLBACK (sp_attribute_table_entry_changed),
+ spat );
+ }
+ /* Show table */
+ gtk_widget_show (spat->table);
+
+ spat->blocked = FALSE;
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (spat), (spat->src.repr != NULL));
+
+} // end of sp_attribute_table_set_repr()
+
+
+
+static void
+sp_attribute_table_object_modified ( SPObject */*object*/,
+ guint flags,
+ SPAttributeTable *spat )
+{
+ if (flags && SP_OBJECT_MODIFIED_FLAG)
+ {
+ gint i;
+ for (i = 0; i < spat->num_attr; i++) {
+ const gchar *val, *text;
+ val = SP_OBJECT_REPR (spat->src.object)->attribute(spat->attributes[i]);
+ text = gtk_entry_get_text (GTK_ENTRY (spat->entries[i]));
+ if (val || text) {
+ if (!val || !text || strcmp (val, text)) {
+ /* We are different */
+ spat->blocked = TRUE;
+ gtk_entry_set_text ( GTK_ENTRY (spat->entries[i]),
+ val ? val : (const gchar *) "");
+ spat->blocked = FALSE;
+ }
+ }
+ }
+ } // end of if()
+
+} // end of sp_attribute_table_object_modified()
+
+
+
+static void
+sp_attribute_table_object_release (SPObject */*object*/, SPAttributeTable *spat)
+{
+ sp_attribute_table_set_object (spat, NULL, 0, NULL, NULL);
+}
+
+
+
+static void
+sp_attribute_table_entry_changed ( GtkEditable *editable,
+ SPAttributeTable *spat )
+{
+ if (!spat->blocked)
+ {
+ gint i;
+ for (i = 0; i < spat->num_attr; i++) {
+
+ if (GTK_WIDGET (editable) == spat->entries[i]) {
+ const gchar *text;
+ spat->blocked = TRUE;
+ text = gtk_entry_get_text (GTK_ENTRY (spat->entries[i]));
+
+ if (!*text)
+ text = NULL;
+
+ if (spat->hasobj && spat->src.object) {
+ SP_OBJECT_REPR (spat->src.object)->setAttribute(spat->attributes[i], text, false);
+ sp_document_done (SP_OBJECT_DOCUMENT (spat->src.object), SP_VERB_NONE,
+ _("Set attribute"));
+
+ } else if (spat->src.repr) {
+
+ spat->src.repr->setAttribute(spat->attributes[i], text, false);
+ /* TODO: Warning! Undo will not be flushed in given case */
+ }
+ spat->blocked = FALSE;
+ return;
+ }
+ }
+ g_warning ("file %s: line %d: Entry signalled change, but there is no such entry", __FILE__, __LINE__);
+ } // end of if()
+
+} // end of sp_attribute_table_entry_changed()
+
+/*
+ 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-attribute-widget.h b/src/widgets/sp-attribute-widget.h
new file mode 100644
index 000000000..01da29bed
--- /dev/null
+++ b/src/widgets/sp-attribute-widget.h
@@ -0,0 +1,130 @@
+/** @file
+ * @brief Widget that listens and modifies repr attributes
+ */
+/* Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2002 authors
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Licensed under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_DIALOGS_SP_ATTRIBUTE_WIDGET_H
+#define SEEN_DIALOGS_SP_ATTRIBUTE_WIDGET_H
+
+#include <glib.h>
+#include <sigc++/connection.h>
+
+#define SP_TYPE_ATTRIBUTE_WIDGET (sp_attribute_widget_get_type ())
+#define SP_ATTRIBUTE_WIDGET(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_ATTRIBUTE_WIDGET, SPAttributeWidget))
+#define SP_ATTRIBUTE_WIDGET_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_ATTRIBUTE_WIDGET, SPAttributeWidgetClass))
+#define SP_IS_ATTRIBUTE_WIDGET(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_ATTRIBUTE_WIDGET))
+#define SP_IS_ATTRIBUTE_WIDGET_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_ATTRIBUTE_WIDGET))
+
+#define SP_TYPE_ATTRIBUTE_TABLE (sp_attribute_table_get_type ())
+#define SP_ATTRIBUTE_TABLE(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_ATTRIBUTE_TABLE, SPAttributeTable))
+#define SP_ATTRIBUTE_TABLE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_ATTRIBUTE_TABLE, SPAttributeTableClass))
+#define SP_IS_ATTRIBUTE_TABLE(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_ATTRIBUTE_TABLE))
+#define SP_IS_ATTRIBUTE_TABLE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_ATTRIBUTE_TABLE))
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+}
+
+
+struct SPAttributeWidget;
+struct SPAttributeWidgetClass;
+
+struct SPAttributeTable;
+struct SPAttributeTableClass;
+
+#include <gtk/gtkentry.h>
+#include <gtk/gtkvbox.h>
+
+#include <forward.h>
+
+struct SPAttributeWidget {
+ GtkEntry entry;
+ guint blocked : 1;
+ guint hasobj : 1;
+ union {
+ SPObject *object;
+ Inkscape::XML::Node *repr;
+ } src;
+ gchar *attribute;
+
+ sigc::connection modified_connection;
+ sigc::connection release_connection;
+};
+
+struct SPAttributeWidgetClass {
+ GtkEntryClass entry_class;
+};
+
+GtkType sp_attribute_widget_get_type (void);
+
+GtkWidget *sp_attribute_widget_new (SPObject *object, const gchar *attribute);
+GtkWidget *sp_attribute_widget_new_repr (Inkscape::XML::Node *repr, const gchar *attribute);
+
+void sp_attribute_widget_set_object ( SPAttributeWidget *spw,
+ SPObject *object,
+ const gchar *attribute );
+void sp_attribute_widget_set_repr ( SPAttributeWidget *spw,
+ Inkscape::XML::Node *repr,
+ const gchar *attribute );
+
+/* SPAttributeTable */
+
+struct SPAttributeTable {
+ GtkVBox vbox;
+ guint blocked : 1;
+ guint hasobj : 1;
+ GtkWidget *table;
+ union {
+ SPObject *object;
+ Inkscape::XML::Node *repr;
+ } src;
+ gint num_attr;
+ gchar **attributes;
+ GtkWidget **entries;
+
+ sigc::connection modified_connection;
+ sigc::connection release_connection;
+};
+
+struct SPAttributeTableClass {
+ GtkEntryClass entry_class;
+};
+
+GtkType sp_attribute_table_get_type (void);
+
+GtkWidget *sp_attribute_table_new ( SPObject *object, gint num_attr,
+ const gchar **labels,
+ const gchar **attributes );
+GtkWidget *sp_attribute_table_new_repr ( Inkscape::XML::Node *repr, gint num_attr,
+ const gchar **labels,
+ const gchar **attributes );
+void sp_attribute_table_set_object ( SPAttributeTable *spw,
+ SPObject *object, gint num_attr,
+ const gchar **labels,
+ const gchar **attrs );
+void sp_attribute_table_set_repr ( SPAttributeTable *spw,
+ Inkscape::XML::Node *repr, gint num_attr,
+ const gchar **labels,
+ const gchar **attrs );
+
+#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/stroke-style.cpp b/src/widgets/stroke-style.cpp
new file mode 100644
index 000000000..b1678f266
--- /dev/null
+++ b/src/widgets/stroke-style.cpp
@@ -0,0 +1,1838 @@
+/** @file
+ * @brief Stroke style dialog
+ */
+/* Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Bryce Harrington <brycehar@bryceharrington.org>
+ * bulia byak <buliabyak@users.sf.net>
+ * Maximilian Albert <maximilian.albert@gmail.com>
+ * Josh Andler <scislac@users.sf.net>
+ *
+ * Copyright (C) 2001-2005 authors
+ * Copyright (C) 2001 Ximian, Inc.
+ * Copyright (C) 2004 John Cliff
+ * Copyright (C) 2008 Maximilian Albert (gtkmm-ification)
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#define noSP_SS_VERBOSE
+
+#include <glib/gmem.h>
+#include <gtk/gtk.h>
+#include <glibmm/i18n.h>
+
+#include "desktop-handles.h"
+#include "desktop-style.h"
+#include "dialogs/dialog-events.h"
+#include "display/nr-arena.h"
+#include "display/nr-arena-item.h"
+#include "document-private.h"
+#include "gradient-chemistry.h"
+#include "helper/stock-items.h"
+#include "helper/unit-menu.h"
+#include "helper/units.h"
+#include "inkscape.h"
+#include "io/sys.h"
+#include "marker.h"
+#include "path-prefix.h"
+#include "selection.h"
+#include "sp-linear-gradient.h"
+#include "sp-namedview.h"
+#include "sp-pattern.h"
+#include "sp-radial-gradient.h"
+#include "sp-rect.h"
+#include "sp-text.h"
+#include "style.h"
+#include "svg/css-ostringstream.h"
+#include "ui/cache/svg_preview_cache.h"
+#include "ui/icon-names.h"
+#include "widgets/dash-selector.h"
+#include "widgets/icon.h"
+#include "widgets/paint-selector.h"
+#include "widgets/sp-widget.h"
+#include "widgets/spw-utilities.h"
+#include "xml/repr.h"
+
+#include "widgets/stroke-style.h"
+
+
+/* Paint */
+
+static void sp_stroke_style_paint_selection_modified (SPWidget *spw, Inkscape::Selection *selection, guint flags, SPPaintSelector *psel);
+static void sp_stroke_style_paint_selection_changed (SPWidget *spw, Inkscape::Selection *selection, SPPaintSelector *psel);
+static void sp_stroke_style_paint_update(SPWidget *spw);
+
+static void sp_stroke_style_paint_mode_changed(SPPaintSelector *psel, SPPaintSelectorMode mode, SPWidget *spw);
+static void sp_stroke_style_paint_dragged(SPPaintSelector *psel, SPWidget *spw);
+static void sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw);
+
+static void sp_stroke_style_widget_change_subselection ( Inkscape::Application *inkscape, SPDesktop *desktop, SPWidget *spw );
+static void sp_stroke_style_widget_transientize_callback(Inkscape::Application *inkscape,
+ SPDesktop *desktop,
+ SPWidget *spw );
+
+/** Marker selection option menus */
+static Gtk::OptionMenu * marker_start_menu = NULL;
+static Gtk::OptionMenu * marker_mid_menu = NULL;
+static Gtk::OptionMenu * marker_end_menu = NULL;
+
+sigc::connection marker_start_menu_connection;
+sigc::connection marker_mid_menu_connection;
+sigc::connection marker_end_menu_connection;
+
+static SPObject *ink_extract_marker_name(gchar const *n, SPDocument *doc);
+static void ink_markers_menu_update(Gtk::Container* spw, SPMarkerLoc const which);
+
+static Inkscape::UI::Cache::SvgPreview svg_preview_cache;
+
+/**
+ * Create the stroke style widget, and hook up all the signals.
+ */
+GtkWidget *
+sp_stroke_style_paint_widget_new(void)
+{
+ GtkWidget *spw, *psel;
+
+ spw = sp_widget_new_global(INKSCAPE);
+
+ psel = sp_paint_selector_new(false); // without fillrule selector
+ gtk_widget_show(psel);
+ gtk_container_add(GTK_CONTAINER(spw), psel);
+ gtk_object_set_data(GTK_OBJECT(spw), "paint-selector", psel);
+
+ gtk_signal_connect(GTK_OBJECT(spw), "modify_selection",
+ GTK_SIGNAL_FUNC(sp_stroke_style_paint_selection_modified),
+ psel);
+ gtk_signal_connect(GTK_OBJECT(spw), "change_selection",
+ GTK_SIGNAL_FUNC(sp_stroke_style_paint_selection_changed),
+ psel);
+
+ g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_stroke_style_widget_change_subselection), spw);
+
+ g_signal_connect (G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK (sp_stroke_style_widget_transientize_callback), spw );
+
+ gtk_signal_connect(GTK_OBJECT(psel), "mode_changed",
+ GTK_SIGNAL_FUNC(sp_stroke_style_paint_mode_changed),
+ spw);
+ gtk_signal_connect(GTK_OBJECT(psel), "dragged",
+ GTK_SIGNAL_FUNC(sp_stroke_style_paint_dragged),
+ spw);
+ gtk_signal_connect(GTK_OBJECT(psel), "changed",
+ GTK_SIGNAL_FUNC(sp_stroke_style_paint_changed),
+ spw);
+
+ sp_stroke_style_paint_update (SP_WIDGET(spw));
+ return spw;
+}
+
+/**
+ * On signal modified, invokes an update of the stroke style paint object.
+ */
+static void
+sp_stroke_style_paint_selection_modified( SPWidget *spw,
+ Inkscape::Selection */*selection*/,
+ guint flags,
+ SPPaintSelector */*psel*/ )
+{
+ if (flags & ( SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG |
+ SP_OBJECT_STYLE_MODIFIED_FLAG) ) {
+ sp_stroke_style_paint_update(spw);
+ }
+}
+
+
+/**
+ * On signal selection changed, invokes an update of the stroke style paint object.
+ */
+static void
+sp_stroke_style_paint_selection_changed( SPWidget *spw,
+ Inkscape::Selection */*selection*/,
+ SPPaintSelector */*psel*/ )
+{
+ sp_stroke_style_paint_update (spw);
+}
+
+
+/**
+ * On signal change subselection, invoke an update of the stroke style widget.
+ */
+static void
+sp_stroke_style_widget_change_subselection( Inkscape::Application */*inkscape*/,
+ SPDesktop */*desktop*/,
+ SPWidget *spw )
+{
+ sp_stroke_style_paint_update (spw);
+}
+
+/**
+ * Gets the active stroke style property, then sets the appropriate color, alpha, gradient,
+ * pattern, etc. for the paint-selector.
+ */
+static void
+sp_stroke_style_paint_update (SPWidget *spw)
+{
+ if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
+ return;
+ }
+
+ gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE));
+
+ SPPaintSelector *psel = SP_PAINT_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "paint-selector"));
+
+ // create temporary style
+ SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
+ // query into it
+ int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKE);
+
+ switch (result) {
+ case QUERY_STYLE_NOTHING:
+ {
+ /* No paint at all */
+ sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY);
+ break;
+ }
+
+ case QUERY_STYLE_SINGLE:
+ case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector
+ case QUERY_STYLE_MULTIPLE_SAME:
+ {
+ SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, false);
+ sp_paint_selector_set_mode (psel, pselmode);
+
+ if (query->stroke.set && query->stroke.isPaintserver()) {
+
+ SPPaintServer *server = SP_STYLE_STROKE_SERVER (query);
+
+ if (SP_IS_LINEARGRADIENT (server)) {
+ SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
+ sp_paint_selector_set_gradient_linear (psel, vector);
+
+ SPLinearGradient *lg = SP_LINEARGRADIENT (server);
+ sp_paint_selector_set_gradient_properties (psel,
+ SP_GRADIENT_UNITS (lg),
+ SP_GRADIENT_SPREAD (lg));
+ } else if (SP_IS_RADIALGRADIENT (server)) {
+ SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
+ sp_paint_selector_set_gradient_radial (psel, vector);
+
+ SPRadialGradient *rg = SP_RADIALGRADIENT (server);
+ sp_paint_selector_set_gradient_properties (psel,
+ SP_GRADIENT_UNITS (rg),
+ SP_GRADIENT_SPREAD (rg));
+ } else if (SP_IS_PATTERN (server)) {
+ SPPattern *pat = pattern_getroot (SP_PATTERN (server));
+ sp_update_pattern_list (psel, pat);
+ }
+ } else if (query->stroke.set && query->stroke.isColor()) {
+ sp_paint_selector_set_color_alpha (psel, &query->stroke.value.color, SP_SCALE24_TO_FLOAT (query->stroke_opacity.value));
+
+ }
+ break;
+ }
+
+ case QUERY_STYLE_MULTIPLE_DIFFERENT:
+ {
+ sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE);
+ break;
+ }
+ }
+
+ sp_style_unref(query);
+
+ gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE));
+}
+
+/**
+ * When the mode is changed, invoke a regular changed handler.
+ */
+static void
+sp_stroke_style_paint_mode_changed( SPPaintSelector *psel,
+ SPPaintSelectorMode /*mode*/,
+ SPWidget *spw )
+{
+ if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
+ return;
+ }
+
+ /* TODO: Does this work?
+ * Not really, here we have to get old color back from object
+ * Instead of relying on paint widget having meaningful colors set
+ */
+ sp_stroke_style_paint_changed(psel, spw);
+}
+
+static gchar const *const undo_label_1 = "stroke:flatcolor:1";
+static gchar const *const undo_label_2 = "stroke:flatcolor:2";
+static gchar const *undo_label = undo_label_1;
+
+/**
+ * When a drag callback occurs on a paint selector object, if it is a RGB or CMYK
+ * color mode, then set the stroke opacity to psel's flat color.
+ */
+static void
+sp_stroke_style_paint_dragged(SPPaintSelector *psel, SPWidget *spw)
+{
+ if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
+ return;
+ }
+
+ switch (psel->mode) {
+ case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
+ case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
+ {
+ sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "stroke", "stroke-opacity");
+ sp_document_maybe_done (sp_desktop_document(SP_ACTIVE_DESKTOP), undo_label, SP_VERB_DIALOG_FILL_STROKE,
+ _("Set stroke color"));
+ break;
+ }
+
+ default:
+ g_warning( "file %s: line %d: Paint %d should not emit 'dragged'",
+ __FILE__, __LINE__, psel->mode);
+ break;
+ }
+}
+
+/**
+ * When the stroke style's paint settings change, this handler updates the
+ * repr's stroke css style and applies the style to relevant drawing items.
+ */
+static void
+sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw)
+{
+ if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
+ return;
+ }
+ g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ SPDocument *document = sp_desktop_document (desktop);
+ Inkscape::Selection *selection = sp_desktop_selection (desktop);
+
+ GSList const *items = selection->itemList();
+
+ switch (psel->mode) {
+ case SP_PAINT_SELECTOR_MODE_EMPTY:
+ // This should not happen.
+ g_warning ( "file %s: line %d: Paint %d should not emit 'changed'",
+ __FILE__, __LINE__, psel->mode);
+ break;
+ case SP_PAINT_SELECTOR_MODE_MULTIPLE:
+ // This happens when you switch multiple objects with different gradients to flat color;
+ // nothing to do here.
+ break;
+
+ case SP_PAINT_SELECTOR_MODE_NONE:
+ {
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "stroke", "none");
+
+ sp_desktop_set_style (desktop, css);
+
+ sp_repr_css_attr_unref(css);
+
+ sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE,
+ _("Remove stroke"));
+ break;
+ }
+
+ case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
+ case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
+ {
+ sp_paint_selector_set_flat_color (psel, desktop, "stroke", "stroke-opacity");
+ sp_document_maybe_done (sp_desktop_document(desktop), undo_label, SP_VERB_DIALOG_FILL_STROKE,
+ _("Set stroke color"));
+
+ // on release, toggle undo_label so that the next drag will not be lumped with this one
+ if (undo_label == undo_label_1)
+ undo_label = undo_label_2;
+ else
+ undo_label = undo_label_1;
+
+ break;
+ }
+
+ case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR:
+ case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL:
+ if (items) {
+ SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR
+ ? SP_GRADIENT_TYPE_LINEAR
+ : SP_GRADIENT_TYPE_RADIAL );
+ SPGradient *vector = sp_paint_selector_get_gradient_vector(psel);
+ if (!vector) {
+ /* No vector in paint selector should mean that we just changed mode */
+
+ SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
+ int result = objects_query_fillstroke ((GSList *) items, query, false);
+ guint32 common_rgb = 0;
+ if (result == QUERY_STYLE_MULTIPLE_SAME) {
+ if (!query->fill.isColor()) {
+ common_rgb = sp_desktop_get_color(desktop, false);
+ } else {
+ common_rgb = query->stroke.value.color.toRGBA32( 0xff );
+ }
+ vector = sp_document_default_gradient_vector(document, common_rgb);
+ }
+ sp_style_unref(query);
+
+ for (GSList const *i = items; i != NULL; i = i->next) {
+ if (!vector) {
+ sp_item_set_gradient(SP_ITEM(i->data),
+ sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), false),
+ gradient_type, false);
+ } else {
+ sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, false);
+ }
+ }
+ } else {
+ vector = sp_gradient_ensure_vector_normalized(vector);
+ for (GSList const *i = items; i != NULL; i = i->next) {
+ SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, false);
+ sp_gradient_selector_attrs_to_gradient(gr, psel);
+ }
+ }
+
+ sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE,
+ _("Set gradient on stroke"));
+ }
+ break;
+
+ case SP_PAINT_SELECTOR_MODE_PATTERN:
+
+ if (items) {
+
+ SPPattern *pattern = sp_paint_selector_get_pattern (psel);
+ if (!pattern) {
+
+ /* No Pattern in paint selector should mean that we just
+ * changed mode - dont do jack.
+ */
+
+ } else {
+ Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern);
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id"));
+ sp_repr_css_set_property (css, "stroke", urltext);
+
+ for (GSList const *i = items; i != NULL; i = i->next) {
+ Inkscape::XML::Node *selrepr = SP_OBJECT_REPR (i->data);
+ SPObject *selobj = SP_OBJECT (i->data);
+ if (!selrepr)
+ continue;
+
+ SPStyle *style = SP_OBJECT_STYLE (selobj);
+ if (style && style->stroke.isPaintserver()) {
+ SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (selobj);
+ if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern)
+ // only if this object's pattern is not rooted in our selected pattern, apply
+ continue;
+ }
+
+ sp_repr_css_change_recursive (selrepr, css, "style");
+ }
+
+ sp_repr_css_attr_unref (css);
+ g_free (urltext);
+
+ } // end if
+
+ sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
+ _("Set pattern on stroke"));
+ } // end if
+
+ break;
+
+ case SP_PAINT_SELECTOR_MODE_UNSET:
+ if (items) {
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ sp_repr_css_unset_property (css, "stroke");
+ sp_repr_css_unset_property (css, "stroke-opacity");
+ sp_repr_css_unset_property (css, "stroke-width");
+ sp_repr_css_unset_property (css, "stroke-miterlimit");
+ sp_repr_css_unset_property (css, "stroke-linejoin");
+ sp_repr_css_unset_property (css, "stroke-linecap");
+ sp_repr_css_unset_property (css, "stroke-dashoffset");
+ sp_repr_css_unset_property (css, "stroke-dasharray");
+
+ sp_desktop_set_style (desktop, css);
+ sp_repr_css_attr_unref (css);
+
+ sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
+ _("Unset stroke"));
+ }
+ break;
+
+ default:
+ g_warning( "file %s: line %d: Paint selector should not be in "
+ "mode %d",
+ __FILE__, __LINE__,
+ psel->mode );
+ break;
+ }
+
+ g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
+}
+
+
+
+
+
+/* Line */
+
+static void sp_stroke_style_line_selection_modified(SPWidget *spw, Inkscape::Selection *selection, guint flags, gpointer data);
+static void sp_stroke_style_line_selection_changed(SPWidget *spw, Inkscape::Selection *selection, gpointer data);
+
+static void sp_stroke_style_line_update(Gtk::Container *spw, Inkscape::Selection *sel);
+
+static void sp_stroke_style_set_join_buttons(Gtk::Container *spw, Gtk::ToggleButton *active);
+
+static void sp_stroke_style_set_cap_buttons(Gtk::Container *spw, Gtk::ToggleButton *active);
+
+static void sp_stroke_style_width_changed(Gtk::Container *spw);
+static void sp_stroke_style_miterlimit_changed(Gtk::Container *spw);
+static void sp_stroke_style_any_toggled(Gtk::ToggleButton *tb, Gtk::Container *spw);
+static void sp_stroke_style_line_dash_changed(Gtk::Container *spw);
+
+static void sp_stroke_style_update_marker_menus(Gtk::Container *spw, GSList const *objects);
+
+
+/**
+ * Helper function for creating radio buttons. This should probably be re-thought out
+ * when reimplementing this with Gtkmm.
+ */
+static Gtk::RadioButton *
+sp_stroke_radio_button(Gtk::RadioButton *tb, char const *icon,
+ Gtk::HBox *hb, Gtk::Container *spw,
+ gchar const *key, gchar const *data)
+{
+ g_assert(icon != NULL);
+ g_assert(hb != NULL);
+ g_assert(spw != NULL);
+
+ if (tb == NULL) {
+ tb = new Gtk::RadioButton();
+ } else {
+ Gtk::RadioButtonGroup grp = tb->get_group();
+ tb = new Gtk::RadioButton(grp);
+ }
+
+ tb->show();
+ tb->set_mode(false);
+ hb->pack_start(*tb, false, false, 0);
+ spw->set_data(icon, tb);
+ tb->set_data(key, (gpointer*)data);
+ tb->signal_toggled().connect(sigc::bind<Gtk::RadioButton *, Gtk::Container *>(
+ sigc::ptr_fun(&sp_stroke_style_any_toggled), tb, spw));
+ Gtk::Widget *px = manage(Glib::wrap(sp_icon_new(Inkscape::ICON_SIZE_LARGE_TOOLBAR, icon)));
+ g_assert(px != NULL);
+ px->show();
+ tb->add(*px);
+
+ return tb;
+
+}
+
+static void
+sp_stroke_style_widget_transientize_callback(Inkscape::Application */*inkscape*/,
+ SPDesktop */*desktop*/,
+ SPWidget */*spw*/ )
+{
+// TODO: Either of these will cause crashes sometimes
+// sp_stroke_style_line_update( SP_WIDGET(spw), desktop ? sp_desktop_selection(desktop) : NULL);
+// ink_markers_menu_update(spw);
+}
+
+/**
+ * Creates a copy of the marker named mname, determines its visible and renderable
+ * area in menu_id's bounding box, and then renders it. This allows us to fill in
+ * preview images of each marker in the marker menu.
+ */
+static Gtk::Image *
+sp_marker_prev_new(unsigned psize, gchar const *mname,
+ SPDocument *source, SPDocument *sandbox,
+ gchar const *menu_id, NRArena const */*arena*/, unsigned /*visionkey*/, NRArenaItem *root)
+{
+ // Retrieve the marker named 'mname' from the source SVG document
+ SPObject const *marker = source->getObjectById(mname);
+ if (marker == NULL)
+ return NULL;
+
+ // Create a copy repr of the marker with id="sample"
+ Inkscape::XML::Document *xml_doc = sp_document_repr_doc(sandbox);
+ Inkscape::XML::Node *mrepr = SP_OBJECT_REPR (marker)->duplicate(xml_doc);
+ mrepr->setAttribute("id", "sample");
+
+ // Replace the old sample in the sandbox by the new one
+ Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (sandbox->getObjectById("defs"));
+ SPObject *oldmarker = sandbox->getObjectById("sample");
+ if (oldmarker)
+ oldmarker->deleteObject(false);
+ defsrepr->appendChild(mrepr);
+ Inkscape::GC::release(mrepr);
+
+// Uncomment this to get the sandbox documents saved (useful for debugging)
+ //FILE *fp = fopen (g_strconcat(menu_id, mname, ".svg", NULL), "w");
+ //sp_repr_save_stream (sp_document_repr_doc (sandbox), fp);
+ //fclose (fp);
+
+ // object to render; note that the id is the same as that of the menu we're building
+ SPObject *object = sandbox->getObjectById(menu_id);
+ sp_document_root (sandbox)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ sp_document_ensure_up_to_date(sandbox);
+
+ if (object == NULL || !SP_IS_ITEM(object))
+ return NULL; // sandbox broken?
+
+ // Find object's bbox in document
+ Geom::Matrix const i2doc(sp_item_i2doc_affine(SP_ITEM(object)));
+ Geom::OptRect dbox = SP_ITEM(object)->getBounds(i2doc);
+
+ if (!dbox) {
+ return NULL;
+ }
+
+ /* Update to renderable state */
+ double sf = 0.8;
+
+ gchar *cache_name = g_strconcat(menu_id, mname, NULL);
+ Glib::ustring key = svg_preview_cache.cache_key(source->uri, cache_name, psize);
+ g_free (cache_name);
+ // TODO: is this correct?
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(svg_preview_cache.get_preview_from_cache(key));
+
+ if (!pixbuf) {
+ pixbuf = Glib::wrap(render_pixbuf(root, sf, *dbox, psize));
+ svg_preview_cache.set_preview_in_cache(key, pixbuf->gobj());
+ }
+
+ // Create widget
+ Gtk::Image *pb = new Gtk::Image(pixbuf);
+
+ return pb;
+}
+
+/**
+ * Returns a list of markers in the defs of the given source document as a GSList object
+ * Returns NULL if there are no markers in the document.
+ */
+GSList *
+ink_marker_list_get (SPDocument *source)
+{
+ if (source == NULL)
+ return NULL;
+
+ GSList *ml = NULL;
+ SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS (source);
+ for ( SPObject *child = sp_object_first_child(SP_OBJECT(defs));
+ child != NULL;
+ child = SP_OBJECT_NEXT (child) )
+ {
+ if (SP_IS_MARKER(child)) {
+ ml = g_slist_prepend (ml, child);
+ }
+ }
+ return ml;
+}
+
+#define MARKER_ITEM_MARGIN 0
+
+/**
+ * Adds previews of markers in marker_list to the given menu widget
+ */
+static void
+sp_marker_menu_build (Gtk::Menu *m, GSList *marker_list, SPDocument *source, SPDocument *sandbox, gchar const *menu_id)
+{
+ // Do this here, outside of loop, to speed up preview generation:
+ NRArena const *arena = NRArena::create();
+ unsigned const visionkey = sp_item_display_key_new(1);
+ NRArenaItem *root = sp_item_invoke_show(SP_ITEM(SP_DOCUMENT_ROOT (sandbox)), (NRArena *) arena, visionkey, SP_ITEM_SHOW_DISPLAY);
+
+ for (; marker_list != NULL; marker_list = marker_list->next) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) marker_list->data);
+ Gtk::MenuItem *i = new Gtk::MenuItem();
+ i->show();
+
+ if (repr->attribute("inkscape:stockid"))
+ i->set_data("stockid", (void *) "true");
+ else
+ i->set_data("stockid", (void *) "false");
+
+ gchar const *markid = repr->attribute("id");
+ i->set_data("marker", (void *) markid);
+
+ Gtk::HBox *hb = new Gtk::HBox(false, MARKER_ITEM_MARGIN);
+ hb->show();
+
+ // generate preview
+
+ Gtk::Image *prv = sp_marker_prev_new (22, markid, source, sandbox, menu_id, arena, visionkey, root);
+ prv->show();
+ hb->pack_start(*prv, false, false, 6);
+
+ // create label
+ Gtk::Label *l = new Gtk::Label(repr->attribute("id"));
+ l->show();
+ l->set_alignment(0.0, 0.5);
+
+ hb->pack_start(*l, true, true, 0);
+
+ hb->show();
+ i->add(*hb);
+
+ m->append(*i);
+ }
+
+ sp_item_invoke_hide(SP_ITEM(sp_document_root(sandbox)), visionkey);
+ nr_object_unref((NRObject *) arena);
+}
+
+/**
+ * sp_marker_list_from_doc()
+ *
+ * \brief Pick up all markers from source, except those that are in
+ * current_doc (if non-NULL), and add items to the m menu
+ *
+ */
+static void
+sp_marker_list_from_doc (Gtk::Menu *m, SPDocument */*current_doc*/, SPDocument *source, SPDocument */*markers_doc*/, SPDocument *sandbox, gchar const *menu_id)
+{
+ GSList *ml = ink_marker_list_get(source);
+ GSList *clean_ml = NULL;
+
+ for (; ml != NULL; ml = ml->next) {
+ if (!SP_IS_MARKER(ml->data))
+ continue;
+
+ // Add to the list of markers we really do wish to show
+ clean_ml = g_slist_prepend (clean_ml, ml->data);
+ }
+ sp_marker_menu_build(m, clean_ml, source, sandbox, menu_id);
+
+ g_slist_free (ml);
+ g_slist_free (clean_ml);
+}
+
+/**
+ * Returns a new document containing default start, mid, and end markers.
+ */
+SPDocument *
+ink_markers_preview_doc ()
+{
+gchar const *buffer = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">"
+" <defs id=\"defs\" />"
+
+" <g id=\"marker-start\">"
+" <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:url(#sample);marker-mid:none;marker-end:none\""
+" d=\"M 12.5,13 L 25,13\" id=\"path1\" />"
+" <rect style=\"fill:none;stroke:none\" id=\"rect2\""
+" width=\"25\" height=\"25\" x=\"0\" y=\"0\" />"
+" </g>"
+
+" <g id=\"marker-mid\">"
+" <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:none;marker-mid:url(#sample);marker-end:none\""
+" d=\"M 0,113 L 12.5,113 L 25,113\" id=\"path11\" />"
+" <rect style=\"fill:none;stroke:none\" id=\"rect22\""
+" width=\"25\" height=\"25\" x=\"0\" y=\"100\" />"
+" </g>"
+
+" <g id=\"marker-end\">"
+" <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:none;marker-mid:none;marker-end:url(#sample)\""
+" d=\"M 0,213 L 12.5,213\" id=\"path111\" />"
+" <rect style=\"fill:none;stroke:none\" id=\"rect222\""
+" width=\"25\" height=\"25\" x=\"0\" y=\"200\" />"
+" </g>"
+
+"</svg>";
+
+ return sp_document_new_from_mem (buffer, strlen(buffer), FALSE);
+}
+
+static void
+ink_marker_menu_create_menu(Gtk::Menu *m, gchar const *menu_id, SPDocument *doc, SPDocument *sandbox)
+{
+ static SPDocument *markers_doc = NULL;
+
+ // add "None"
+ Gtk::MenuItem *i = new Gtk::MenuItem();
+ i->show();
+
+ i->set_data("marker", (void *) "none");
+
+ Gtk::HBox *hb = new Gtk::HBox(false, MARKER_ITEM_MARGIN);
+ hb->show();
+
+ Gtk::Label *l = new Gtk::Label( _("None") );
+ l->show();
+ l->set_alignment(0.0, 0.5);
+
+ hb->pack_start(*l, true, true, 0);
+
+ hb->show();
+ i->add(*hb);
+ m->append(*i);
+
+ // find and load markers.svg
+ if (markers_doc == NULL) {
+ char *markers_source = g_build_filename(INKSCAPE_MARKERSDIR, "markers.svg", NULL);
+ if (Inkscape::IO::file_test(markers_source, G_FILE_TEST_IS_REGULAR)) {
+ markers_doc = sp_document_new(markers_source, FALSE);
+ }
+ g_free(markers_source);
+ }
+
+ // suck in from current doc
+ sp_marker_list_from_doc(m, NULL, doc, markers_doc, sandbox, menu_id);
+
+ // add separator
+ {
+ //Gtk::Separator *i = gtk_separator_menu_item_new();
+ Gtk::SeparatorMenuItem *i = new Gtk::SeparatorMenuItem();
+ i->show();
+ m->append(*i);
+ }
+
+ // suck in from markers.svg
+ if (markers_doc) {
+ sp_document_ensure_up_to_date(doc);
+ sp_marker_list_from_doc(m, doc, markers_doc, NULL, sandbox, menu_id);
+ }
+
+}
+
+/**
+ * Creates a menu widget to display markers from markers.svg
+ */
+static Gtk::OptionMenu *
+ink_marker_menu(Gtk::Widget */*tbl*/, gchar const *menu_id, SPDocument *sandbox)
+{
+ SPDesktop *desktop = inkscape_active_desktop();
+ SPDocument *doc = sp_desktop_document(desktop);
+ Gtk::OptionMenu *mnu = new Gtk::OptionMenu();
+
+ /* Create new menu widget */
+ Gtk::Menu *m = new Gtk::Menu();
+ m->show();
+
+ mnu->set_data("updating", (gpointer) FALSE);
+
+ if (!doc) {
+ Gtk::MenuItem *i = new Gtk::MenuItem(_("No document selected"));
+ i->show();
+ m->append(*i);
+ mnu->set_sensitive(false);
+
+ } else {
+ ink_marker_menu_create_menu(m, menu_id, doc, sandbox);
+
+ mnu->set_sensitive(true);
+ }
+
+ mnu->set_data("menu_id", const_cast<gchar *>(menu_id));
+ mnu->set_menu(*m);
+
+ /* Set history */
+ mnu->set_history(0);
+
+ return mnu;
+}
+
+/**
+ * Handles when user selects one of the markers from the marker menu.
+ * Defines a uri string to refer to it, then applies it to all selected
+ * items in the current desktop.
+ */
+static void
+sp_marker_select(Gtk::OptionMenu *mnu, Gtk::Container *spw, SPMarkerLoc const which)
+{
+ if (spw->get_data("update")) {
+ return;
+ }
+
+ SPDesktop *desktop = inkscape_active_desktop();
+ SPDocument *document = sp_desktop_document(desktop);
+ if (!document) {
+ return;
+ }
+
+ /* Get Marker */
+ if (!mnu->get_menu()->get_active()->get_data("marker"))
+ {
+ return;
+ }
+ gchar *markid = static_cast<gchar *>(mnu->get_menu()->get_active()->get_data("marker"));
+ gchar const *marker = "";
+ if (strcmp(markid, "none")) {
+ gchar *stockid = static_cast<gchar *>(mnu->get_menu()->get_active()->get_data("stockid"));
+
+ gchar *markurn = markid;
+ if (!strcmp(stockid,"true")) markurn = g_strconcat("urn:inkscape:marker:",markid,NULL);
+ SPObject *mark = get_stock_item(markurn);
+ if (mark) {
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR(mark);
+ marker = g_strconcat("url(#", repr->attribute("id"), ")", NULL);
+ }
+ } else {
+ marker = markid;
+ }
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ gchar const *menu_id = static_cast<gchar const *>(mnu->get_data("menu_id"));
+ sp_repr_css_set_property(css, menu_id, marker);
+
+ // Also update the marker dropdown menus, so the document's markers
+ // show up at the top of the menu
+// sp_stroke_style_line_update( SP_WIDGET(spw), desktop ? sp_desktop_selection(desktop) : NULL);
+ ink_markers_menu_update(spw, which);
+
+ Inkscape::Selection *selection = sp_desktop_selection(desktop);
+ GSList const *items = selection->itemList();
+ for (; items != NULL; items = items->next) {
+ SPItem *item = (SPItem *) items->data;
+ if (!SP_IS_SHAPE(item) || SP_IS_RECT(item)) // can't set marker to rect, until it's converted to using <path>
+ continue;
+ Inkscape::XML::Node *selrepr = SP_OBJECT_REPR((SPItem *) items->data);
+ if (selrepr) {
+ sp_repr_css_change_recursive(selrepr, css, "style");
+ }
+ SP_OBJECT(items->data)->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ SP_OBJECT(items->data)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
+ }
+
+ sp_repr_css_attr_unref(css);
+
+ sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE,
+ _("Set markers"));
+
+};
+
+static unsigned int
+ink_marker_menu_get_pos(Gtk::Menu *mnu, gchar const *markname)
+{
+ if (markname == NULL)
+ markname = static_cast<gchar const *>(mnu->get_active()->get_data("marker"));
+
+ if (markname == NULL)
+ return 0;
+
+ std::vector<Gtk::Widget *> kids = mnu->get_children();
+ unsigned int i = 0;
+ for (; i < kids.size();) {
+ gchar const *mark = static_cast<gchar const *>(kids[i]->get_data("marker"));
+ if (mark && strcmp(mark, markname) == 0) {
+ break;
+ }
+ ++i;
+ }
+
+ return i;
+}
+
+static void
+ink_markers_menu_update(Gtk::Container* /*spw*/, SPMarkerLoc const which) {
+ SPDesktop *desktop = inkscape_active_desktop();
+ SPDocument *document = sp_desktop_document(desktop);
+ SPDocument *sandbox = ink_markers_preview_doc ();
+ Gtk::Menu *m;
+ int pos;
+
+ // TODO: this code can be shortened by abstracting out marker_(start|mid|end)_...
+ switch (which) {
+ case SP_MARKER_LOC_START:
+ marker_start_menu_connection.block();
+ pos = ink_marker_menu_get_pos(marker_start_menu->get_menu(), NULL);
+ m = new Gtk::Menu();
+ m->show();
+ ink_marker_menu_create_menu(m, "marker-start", document, sandbox);
+ marker_start_menu->remove_menu();
+ marker_start_menu->set_menu(*m);
+ marker_start_menu->set_history(pos);
+ marker_start_menu_connection.unblock();
+ break;
+
+ case SP_MARKER_LOC_MID:
+ marker_mid_menu_connection.block();
+ pos = ink_marker_menu_get_pos(marker_mid_menu->get_menu(), NULL);
+ m = new Gtk::Menu();
+ m->show();
+ ink_marker_menu_create_menu(m, "marker-mid", document, sandbox);
+ marker_mid_menu->remove_menu();
+ marker_mid_menu->set_menu(*m);
+ marker_mid_menu->set_history(pos);
+ marker_mid_menu_connection.unblock();
+ break;
+
+ case SP_MARKER_LOC_END:
+ marker_end_menu_connection.block();
+ pos = ink_marker_menu_get_pos(marker_end_menu->get_menu(), NULL);
+ m = new Gtk::Menu();
+ m->show();
+ ink_marker_menu_create_menu(m, "marker-end", document, sandbox);
+ marker_end_menu->remove_menu();
+ marker_end_menu->set_menu(*m);
+ marker_end_menu->set_history(pos);
+ marker_end_menu_connection.unblock();
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/**
+ * Sets the stroke width units for all selected items.
+ * Also handles absolute and dimensionless units.
+ */
+static gboolean stroke_width_set_unit(SPUnitSelector *,
+ SPUnit const *old,
+ SPUnit const *new_units,
+ Gtk::Container *spw)
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+
+ if (!desktop) {
+ return FALSE;
+ }
+
+ Inkscape::Selection *selection = sp_desktop_selection (desktop);
+
+ if (selection->isEmpty())
+ return FALSE;
+
+ GSList const *objects = selection->itemList();
+
+ if ((old->base == SP_UNIT_ABSOLUTE || old->base == SP_UNIT_DEVICE) &&
+ (new_units->base == SP_UNIT_DIMENSIONLESS)) {
+
+ /* Absolute to percentage */
+ spw->set_data ("update", GUINT_TO_POINTER (TRUE));
+
+ Gtk::Adjustment *a = static_cast<Gtk::Adjustment *>(spw->get_data("width"));
+ float w = sp_units_get_pixels (a->get_value(), *old);
+
+ gdouble average = stroke_average_width (objects);
+
+ if (average == NR_HUGE || average == 0)
+ return FALSE;
+
+ a->set_value (100.0 * w / average);
+
+ spw->set_data ("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 */
+ spw->set_data ("update", GUINT_TO_POINTER (TRUE));
+
+ Gtk::Adjustment *a = static_cast<Gtk::Adjustment *>(spw->get_data ("width"));
+
+ gdouble average = stroke_average_width (objects);
+
+ a->set_value (sp_pixels_get_units (0.01 * a->get_value() * average, *new_units));
+
+ spw->set_data ("update", GUINT_TO_POINTER (FALSE));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ * \brief Creates a new widget for the line stroke style.
+ *
+ */
+Gtk::Container *
+sp_stroke_style_line_widget_new(void)
+{
+ Gtk::Widget *us;
+ SPDashSelector *ds;
+ GtkWidget *us_old, *spw_old;
+ Gtk::Container *spw;
+ Gtk::Table *t;
+ Gtk::Adjustment *a;
+ Gtk::SpinButton *sb;
+ Gtk::RadioButton *tb;
+ Gtk::HBox *f, *hb;
+
+ Gtk::Tooltips *tt = new Gtk::Tooltips();
+
+ spw_old = sp_widget_new_global(INKSCAPE);
+ spw = dynamic_cast<Gtk::Container *>(manage(Glib::wrap(spw_old)));
+
+ f = new Gtk::HBox(false, 0);
+ f->show();
+ spw->add(*f);
+
+ t = new Gtk::Table(3, 6, false);
+ t->show();
+ t->set_border_width(4);
+ t->set_row_spacings(4);
+ f->add(*t);
+ spw->set_data("stroke", t);
+
+ gint i = 0;
+
+ /* Stroke width */
+ spw_label(t, Q_("StrokeWidth|Width:"), 0, i);
+
+ hb = spw_hbox(t, 3, 1, i);
+
+// TODO: when this is gtkmmified, use an Inkscape::UI::Widget::ScalarUnit instead of the separate
+// spinbutton and unit selector for stroke width. In sp_stroke_style_line_update, use
+// setHundredPercent to remember the aeraged width corresponding to 100%. Then the
+// stroke_width_set_unit will be removed (because ScalarUnit takes care of conversions itself), and
+// with it, the two remaining calls of stroke_average_width, allowing us to get rid of that
+// function in desktop-style.
+
+ a = new Gtk::Adjustment(1.0, 0.0, 1000.0, 0.1, 10.0, 10.0);
+ spw->set_data("width", a);
+ sb = new Gtk::SpinButton(*a, 0.1, 3);
+ tt->set_tip(*sb, _("Stroke width"));
+ sb->show();
+
+ sp_dialog_defocus_on_enter_cpp(sb);
+
+ hb->pack_start(*sb, false, false, 0);
+ us_old = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
+ us = manage(Glib::wrap(us_old));
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop)
+ sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us_old), sp_desktop_namedview(desktop)->doc_units);
+ sp_unit_selector_add_unit(SP_UNIT_SELECTOR(us_old), &sp_unit_get_by_id(SP_UNIT_PERCENT), 0);
+ g_signal_connect ( G_OBJECT (us_old), "set_unit", G_CALLBACK (stroke_width_set_unit), spw );
+ us->show();
+ sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us_old), GTK_ADJUSTMENT(a->gobj()) );
+ hb->pack_start(*us, FALSE, FALSE, 0);
+ spw->set_data("units", us_old);
+
+ a->signal_value_changed().connect(sigc::bind(sigc::ptr_fun(&sp_stroke_style_width_changed), spw));
+ i++;
+
+ /* Join type */
+ // TRANSLATORS: The line join style specifies the shape to be used at the
+ // corners of paths. It can be "miter", "round" or "bevel".
+ spw_label(t, _("Join:"), 0, i);
+
+ hb = spw_hbox(t, 3, 1, i);
+
+ tb = NULL;
+
+ tb = sp_stroke_radio_button(tb, INKSCAPE_ICON_STROKE_JOIN_MITER,
+ hb, spw, "join", "miter");
+
+ // TRANSLATORS: Miter join: joining lines with a sharp (pointed) corner.
+ // For an example, draw a triangle with a large stroke width and modify the
+ // "Join" option (in the Fill and Stroke dialog).
+ tt->set_tip(*tb, _("Miter join"));
+
+ tb = sp_stroke_radio_button(tb, INKSCAPE_ICON_STROKE_JOIN_ROUND,
+ hb, spw, "join", "round");
+
+ // TRANSLATORS: Round join: joining lines with a rounded corner.
+ // For an example, draw a triangle with a large stroke width and modify the
+ // "Join" option (in the Fill and Stroke dialog).
+ tt->set_tip(*tb, _("Round join"));
+
+ tb = sp_stroke_radio_button(tb, INKSCAPE_ICON_STROKE_JOIN_BEVEL,
+ hb, spw, "join", "bevel");
+
+ // TRANSLATORS: Bevel join: joining lines with a blunted (flattened) corner.
+ // For an example, draw a triangle with a large stroke width and modify the
+ // "Join" option (in the Fill and Stroke dialog).
+ tt->set_tip(*tb, _("Bevel join"));
+
+ i++;
+
+ /* Miterlimit */
+ // TRANSLATORS: Miter limit: only for "miter join", this limits the length
+ // of the sharp "spike" when the lines connect at too sharp an angle.
+ // When two line segments meet at a sharp angle, a miter join results in a
+ // spike that extends well beyond the connection point. The purpose of the
+ // miter limit is to cut off such spikes (i.e. convert them into bevels)
+ // when they become too long.
+ spw_label(t, _("Miter limit:"), 0, i);
+
+ hb = spw_hbox(t, 3, 1, i);
+
+ a = new Gtk::Adjustment(4.0, 0.0, 100.0, 0.1, 10.0, 10.0);
+ spw->set_data("miterlimit", a);
+
+ sb = new Gtk::SpinButton(*a, 0.1, 2);
+ tt->set_tip(*sb, _("Maximum length of the miter (in units of stroke width)"));
+ sb->show();
+ spw->set_data("miterlimit_sb", sb);
+ sp_dialog_defocus_on_enter_cpp(sb);
+
+ hb->pack_start(*sb, false, false, 0);
+
+ a->signal_value_changed().connect(sigc::bind(sigc::ptr_fun(&sp_stroke_style_miterlimit_changed), spw));
+ i++;
+
+ /* Cap type */
+ // TRANSLATORS: cap type specifies the shape for the ends of lines
+ spw_label(t, _("Cap:"), 0, i);
+
+ hb = spw_hbox(t, 3, 1, i);
+
+ tb = NULL;
+
+ tb = sp_stroke_radio_button(tb, INKSCAPE_ICON_STROKE_CAP_BUTT,
+ hb, spw, "cap", "butt");
+
+ // TRANSLATORS: Butt cap: the line shape does not extend beyond the end point
+ // of the line; the ends of the line are square
+ tt->set_tip(*tb, _("Butt cap"));
+
+ tb = sp_stroke_radio_button(tb, INKSCAPE_ICON_STROKE_CAP_ROUND,
+ hb, spw, "cap", "round");
+
+ // TRANSLATORS: Round cap: the line shape extends beyond the end point of the
+ // line; the ends of the line are rounded
+ tt->set_tip(*tb, _("Round cap"));
+
+ tb = sp_stroke_radio_button(tb, INKSCAPE_ICON_STROKE_CAP_SQUARE,
+ hb, spw, "cap", "square");
+
+ // TRANSLATORS: Square cap: the line shape extends beyond the end point of the
+ // line; the ends of the line are square
+ tt->set_tip(*tb, _("Square cap"));
+
+ i++;
+
+
+ /* Dash */
+ spw_label(t, _("Dashes:"), 0, i);
+ ds = manage(new SPDashSelector);
+
+ ds->show();
+ t->attach(*ds, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0);
+ spw->set_data("dash", ds);
+ ds->changed_signal.connect(sigc::bind(sigc::ptr_fun(&sp_stroke_style_line_dash_changed), spw));
+ i++;
+
+ /* Drop down marker selectors*/
+ // TODO: this code can be shortened by iterating over the possible menus!
+
+ // doing this here once, instead of for each preview, to speed things up
+ SPDocument *sandbox = ink_markers_preview_doc ();
+
+ // TRANSLATORS: Path markers are an SVG feature that allows you to attach arbitrary shapes
+ // (arrowheads, bullets, faces, whatever) to the start, end, or middle nodes of a path.
+ spw_label(t, _("Start Markers:"), 0, i);
+ marker_start_menu = ink_marker_menu(spw ,"marker-start", sandbox);
+ tt->set_tip(*marker_start_menu, _("Start Markers are drawn on the first node of a path or shape"));
+ marker_start_menu_connection = marker_start_menu->signal_changed().connect(
+ sigc::bind<Gtk::OptionMenu *, Gtk::Container *, SPMarkerLoc>(
+ sigc::ptr_fun(&sp_marker_select), marker_start_menu, spw, SP_MARKER_LOC_START));
+ marker_start_menu->show();
+ t->attach(*marker_start_menu, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0);
+ spw->set_data("start_mark_menu", marker_start_menu);
+
+ i++;
+ spw_label(t, _("Mid Markers:"), 0, i);
+ marker_mid_menu = ink_marker_menu(spw ,"marker-mid", sandbox);
+ tt->set_tip(*marker_mid_menu, _("Mid Markers are drawn on every node of a path or shape except the first and last nodes"));
+ marker_mid_menu_connection = marker_mid_menu->signal_changed().connect(
+ sigc::bind<Gtk::OptionMenu *, Gtk::Container *, SPMarkerLoc>(
+ sigc::ptr_fun(&sp_marker_select), marker_mid_menu,spw, SP_MARKER_LOC_MID));
+ marker_mid_menu->show();
+ t->attach(*marker_mid_menu, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0);
+ spw->set_data("mid_mark_menu", marker_mid_menu);
+
+ i++;
+ spw_label(t, _("End Markers:"), 0, i);
+ marker_end_menu = ink_marker_menu(spw ,"marker-end", sandbox);
+ tt->set_tip(*marker_end_menu, _("End Markers are drawn on the last node of a path or shape"));
+ marker_end_menu_connection = marker_end_menu->signal_changed().connect(
+ sigc::bind<Gtk::OptionMenu *, Gtk::Container *, SPMarkerLoc>(
+ sigc::ptr_fun(&sp_marker_select), marker_end_menu, spw, SP_MARKER_LOC_END));
+ marker_end_menu->show();
+ t->attach(*marker_end_menu, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0);
+ spw->set_data("end_mark_menu", marker_end_menu);
+
+ i++;
+
+ // FIXME: we cheat and still use gtk+ signals
+
+ gtk_signal_connect(GTK_OBJECT(spw_old), "modify_selection",
+ GTK_SIGNAL_FUNC(sp_stroke_style_line_selection_modified),
+ spw);
+ gtk_signal_connect(GTK_OBJECT(spw_old), "change_selection",
+ GTK_SIGNAL_FUNC(sp_stroke_style_line_selection_changed),
+ spw);
+
+ sp_stroke_style_line_update(spw, desktop ? sp_desktop_selection(desktop) : NULL);
+
+ return spw;
+}
+
+/**
+ * Callback for when stroke style widget is modified.
+ * Triggers update action.
+ */
+static void
+sp_stroke_style_line_selection_modified(SPWidget *,
+ Inkscape::Selection *selection,
+ guint flags,
+ gpointer data)
+{
+ Gtk::Container *spw = static_cast<Gtk::Container *>(data);
+ if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
+ sp_stroke_style_line_update(spw, selection);
+ }
+
+}
+
+/**
+ * Callback for when stroke style widget is changed.
+ * Triggers update action.
+ */
+static void
+sp_stroke_style_line_selection_changed(SPWidget *,
+ Inkscape::Selection *selection,
+ gpointer data)
+{
+ Gtk::Container *spw = static_cast<Gtk::Container *>(data);
+ sp_stroke_style_line_update(spw, selection);
+}
+
+/**
+ * Sets selector widgets' dash style from an SPStyle object.
+ */
+static void
+sp_dash_selector_set_from_style(SPDashSelector *dsel, SPStyle *style)
+{
+ if (style->stroke_dash.n_dash > 0) {
+ double d[64];
+ int len = MIN(style->stroke_dash.n_dash, 64);
+ for (int i = 0; i < len; i++) {
+ if (style->stroke_width.computed != 0)
+ d[i] = style->stroke_dash.dash[i] / style->stroke_width.computed;
+ else
+ d[i] = style->stroke_dash.dash[i]; // is there a better thing to do for stroke_width==0?
+ }
+ dsel->set_dash(len, d, style->stroke_width.computed != 0 ?
+ style->stroke_dash.offset / style->stroke_width.computed :
+ style->stroke_dash.offset);
+ } else {
+ dsel->set_dash(0, NULL, 0.0);
+ }
+}
+
+/**
+ * Sets the join type for a line, and updates the stroke style widget's buttons
+ */
+static void
+sp_jointype_set (Gtk::Container *spw, unsigned const jointype)
+{
+ Gtk::RadioButton *tb = NULL;
+ switch (jointype) {
+ case SP_STROKE_LINEJOIN_MITER:
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_JOIN_MITER));
+ break;
+ case SP_STROKE_LINEJOIN_ROUND:
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_JOIN_ROUND));
+ break;
+ case SP_STROKE_LINEJOIN_BEVEL:
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_JOIN_BEVEL));
+ break;
+ default:
+ break;
+ }
+ sp_stroke_style_set_join_buttons(spw, tb);
+}
+
+/**
+ * Sets the cap type for a line, and updates the stroke style widget's buttons
+ */
+static void
+sp_captype_set (Gtk::Container *spw, unsigned const captype)
+{
+ Gtk::RadioButton *tb = NULL;
+ switch (captype) {
+ case SP_STROKE_LINECAP_BUTT:
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_CAP_BUTT));
+ break;
+ case SP_STROKE_LINECAP_ROUND:
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_CAP_ROUND));
+ break;
+ case SP_STROKE_LINECAP_SQUARE:
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_CAP_SQUARE));
+ break;
+ default:
+ break;
+ }
+ sp_stroke_style_set_cap_buttons(spw, tb);
+}
+
+/**
+ * Callback for when stroke style widget is updated, including markers, cap type,
+ * join type, etc.
+ */
+static void
+sp_stroke_style_line_update(Gtk::Container *spw, Inkscape::Selection *sel)
+{
+ if (spw->get_data("update")) {
+ return;
+ }
+
+ spw->set_data("update", GINT_TO_POINTER(TRUE));
+
+ Gtk::Table *sset = static_cast<Gtk::Table *>(spw->get_data("stroke"));
+ Gtk::Adjustment *width = static_cast<Gtk::Adjustment *>(spw->get_data("width"));
+ Gtk::Adjustment *ml = static_cast<Gtk::Adjustment *>(spw->get_data("miterlimit"));
+ SPUnitSelector *us = SP_UNIT_SELECTOR(spw->get_data("units"));
+ SPDashSelector *dsel = static_cast<SPDashSelector *>(spw->get_data("dash"));
+
+ // create temporary style
+ SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
+ // query into it
+ int result_sw = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
+ int result_ml = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEMITERLIMIT);
+ int result_cap = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKECAP);
+ int result_join = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEJOIN);
+
+ if (result_sw == QUERY_STYLE_NOTHING) {
+ /* No objects stroked, set insensitive */
+ sset->set_sensitive(false);
+
+ spw->set_data("update", GINT_TO_POINTER(FALSE));
+ return;
+ } else {
+ sset->set_sensitive(true);
+
+ SPUnit const *unit = sp_unit_selector_get_unit(us);
+
+ if (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED) {
+ sp_unit_selector_set_unit(us, &sp_unit_get_by_id(SP_UNIT_PERCENT));
+ } else {
+ // same width, or only one object; no sense to keep percent, switch to absolute
+ if (unit->base != SP_UNIT_ABSOLUTE && unit->base != SP_UNIT_DEVICE) {
+ sp_unit_selector_set_unit(us, sp_desktop_namedview(SP_ACTIVE_DESKTOP)->doc_units);
+ }
+ }
+
+ unit = sp_unit_selector_get_unit(us);
+
+ if (unit->base == SP_UNIT_ABSOLUTE || unit->base == SP_UNIT_DEVICE) {
+ double avgwidth = sp_pixels_get_units (query->stroke_width.computed, *unit);
+ width->set_value(avgwidth);
+ } else {
+ width->set_value(100);
+ }
+ }
+
+ if (result_ml != QUERY_STYLE_NOTHING)
+ ml->set_value(query->stroke_miterlimit.value); // TODO: reflect averagedness?
+
+ if (result_join != QUERY_STYLE_MULTIPLE_DIFFERENT) {
+ sp_jointype_set(spw, query->stroke_linejoin.value);
+ } else {
+ sp_stroke_style_set_join_buttons(spw, NULL);
+ }
+
+ if (result_cap != QUERY_STYLE_MULTIPLE_DIFFERENT) {
+ sp_captype_set (spw, query->stroke_linecap.value);
+ } else {
+ sp_stroke_style_set_cap_buttons(spw, NULL);
+ }
+
+ sp_style_unref(query);
+
+ if (!sel || sel->isEmpty())
+ return;
+
+ GSList const *objects = sel->itemList();
+ SPObject * const object = SP_OBJECT(objects->data);
+ SPStyle * const style = SP_OBJECT_STYLE(object);
+
+ /* Markers */
+ sp_stroke_style_update_marker_menus(spw, objects); // FIXME: make this desktop query too
+
+ /* Dash */
+ sp_dash_selector_set_from_style(dsel, style); // FIXME: make this desktop query too
+
+ sset->set_sensitive(true);
+
+ spw->set_data("update", GINT_TO_POINTER(FALSE));
+}
+
+/**
+ * Sets a line's dash properties in a CSS style object.
+ */
+static void
+sp_stroke_style_set_scaled_dash(SPCSSAttr *css,
+ int ndash, double *dash, double offset,
+ double scale)
+{
+ if (ndash > 0) {
+ Inkscape::CSSOStringStream osarray;
+ for (int i = 0; i < ndash; i++) {
+ osarray << dash[i] * scale;
+ if (i < (ndash - 1)) {
+ osarray << ",";
+ }
+ }
+ sp_repr_css_set_property(css, "stroke-dasharray", osarray.str().c_str());
+
+ Inkscape::CSSOStringStream osoffset;
+ osoffset << offset * scale;
+ sp_repr_css_set_property(css, "stroke-dashoffset", osoffset.str().c_str());
+ } else {
+ sp_repr_css_set_property(css, "stroke-dasharray", "none");
+ sp_repr_css_set_property(css, "stroke-dashoffset", NULL);
+ }
+}
+
+/**
+ * Sets line properties like width, dashes, markers, etc. on all currently selected items.
+ */
+static void
+sp_stroke_style_scale_line(Gtk::Container *spw)
+{
+ if (spw->get_data("update")) {
+ return;
+ }
+
+ spw->set_data("update", GINT_TO_POINTER(TRUE));
+
+ Gtk::Adjustment *wadj = static_cast<Gtk::Adjustment *>(spw->get_data("width"));
+ SPUnitSelector *us = SP_UNIT_SELECTOR(spw->get_data("units"));
+ SPDashSelector *dsel = static_cast<SPDashSelector *>(spw->get_data("dash"));
+ Gtk::Adjustment *ml = static_cast<Gtk::Adjustment *>(spw->get_data("miterlimit"));
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ SPDocument *document = sp_desktop_document (desktop);
+ Inkscape::Selection *selection = sp_desktop_selection (desktop);
+
+ GSList const *items = selection->itemList();
+
+ /* TODO: Create some standardized method */
+ SPCSSAttr *css = sp_repr_css_attr_new();
+
+ if (items) {
+
+ double width_typed = wadj->get_value();
+ double const miterlimit = ml->get_value();
+
+ SPUnit const *const unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
+
+ double *dash, offset;
+ int ndash;
+ dsel->get_dash(&ndash, &dash, &offset);
+
+ for (GSList const *i = items; i != NULL; i = i->next) {
+ /* Set stroke width */
+ double width;
+ if (unit->base == SP_UNIT_ABSOLUTE || unit->base == SP_UNIT_DEVICE) {
+ width = sp_units_get_pixels (width_typed, *unit);
+ } else { // percentage
+ gdouble old_w = SP_OBJECT_STYLE (i->data)->stroke_width.computed;
+ width = old_w * width_typed / 100;
+ }
+
+ {
+ Inkscape::CSSOStringStream os_width;
+ os_width << width;
+ sp_repr_css_set_property(css, "stroke-width", os_width.str().c_str());
+ }
+
+ {
+ Inkscape::CSSOStringStream os_ml;
+ os_ml << miterlimit;
+ sp_repr_css_set_property(css, "stroke-miterlimit", os_ml.str().c_str());
+ }
+
+ /* Set dash */
+ sp_stroke_style_set_scaled_dash(css, ndash, dash, offset, width);
+
+ sp_desktop_apply_css_recursive (SP_OBJECT(i->data), css, true);
+ }
+
+ g_free(dash);
+
+ if (unit->base != SP_UNIT_ABSOLUTE && unit->base != SP_UNIT_DEVICE) {
+ // reset to 100 percent
+ wadj->set_value(100.0);
+ }
+
+ }
+
+ // we have already changed the items, so set style without changing selection
+ // FIXME: move the above stroke-setting stuff, including percentages, to desktop-style
+ sp_desktop_set_style (desktop, css, false);
+
+ sp_repr_css_attr_unref(css);
+
+ sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE,
+ _("Set stroke style"));
+
+ spw->set_data("update", GINT_TO_POINTER(FALSE));
+}
+
+/**
+ * Callback for when the stroke style's width changes.
+ * Causes all line styles to be applied to all selected items.
+ */
+static void
+sp_stroke_style_width_changed(Gtk::Container *spw)
+{
+ if (spw->get_data("update")) {
+ return;
+ }
+
+ sp_stroke_style_scale_line(spw);
+}
+
+/**
+ * Callback for when the stroke style's miterlimit changes.
+ * Causes all line styles to be applied to all selected items.
+ */
+static void
+sp_stroke_style_miterlimit_changed(Gtk::Container *spw)
+{
+ if (spw->get_data("update")) {
+ return;
+ }
+
+ sp_stroke_style_scale_line(spw);
+}
+
+/**
+ * Callback for when the stroke style's dash changes.
+ * Causes all line styles to be applied to all selected items.
+ */
+
+static void
+sp_stroke_style_line_dash_changed(Gtk::Container *spw)
+{
+ if (spw->get_data("update")) {
+ return;
+ }
+
+ sp_stroke_style_scale_line(spw);
+}
+
+/**
+ * \brief This routine handles toggle events for buttons in the stroke style
+ * dialog.
+ * When activated, this routine gets the data for the various widgets, and then
+ * calls the respective routines to update css properties, etc.
+ *
+ */
+static void
+sp_stroke_style_any_toggled(Gtk::ToggleButton *tb, Gtk::Container *spw)
+{
+ if (spw->get_data("update")) {
+ return;
+ }
+
+ if (tb->get_active()) {
+
+ gchar const *join
+ = static_cast<gchar const *>(tb->get_data("join"));
+ gchar const *cap
+ = static_cast<gchar const *>(tb->get_data("cap"));
+
+ if (join) {
+ Gtk::SpinButton *ml = static_cast<Gtk::SpinButton *>(spw->get_data("miterlimit_sb"));
+ ml->set_sensitive(!strcmp(join, "miter"));
+ }
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+
+ /* TODO: Create some standardized method */
+ SPCSSAttr *css = sp_repr_css_attr_new();
+
+ if (join) {
+ sp_repr_css_set_property(css, "stroke-linejoin", join);
+
+ sp_desktop_set_style (desktop, css);
+
+ sp_stroke_style_set_join_buttons(spw, tb);
+ } else if (cap) {
+ sp_repr_css_set_property(css, "stroke-linecap", cap);
+
+ sp_desktop_set_style (desktop, css);
+
+ sp_stroke_style_set_cap_buttons(spw, tb);
+ }
+
+ sp_repr_css_attr_unref(css);
+
+ sp_document_done(sp_desktop_document(desktop), SP_VERB_DIALOG_FILL_STROKE,
+ _("Set stroke style"));
+ }
+}
+
+/**
+ * Updates the join style toggle buttons
+ */
+static void
+sp_stroke_style_set_join_buttons(Gtk::Container *spw, Gtk::ToggleButton *active)
+{
+ Gtk::RadioButton *tb;
+
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_JOIN_MITER));
+ tb->set_active(active == tb);
+
+ Gtk::SpinButton *ml = static_cast<Gtk::SpinButton *>(spw->get_data("miterlimit_sb"));
+ ml->set_sensitive(active == tb);
+
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_JOIN_ROUND));
+ tb->set_active(active == tb);
+
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_JOIN_BEVEL));
+ tb->set_active(active == tb);
+}
+
+/**
+ * Updates the cap style toggle buttons
+ */
+static void
+sp_stroke_style_set_cap_buttons(Gtk::Container *spw, Gtk::ToggleButton *active)
+{
+ Gtk::RadioButton *tb;
+
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_CAP_BUTT));
+ tb->set_active(active == tb);
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_CAP_ROUND));
+ tb->set_active(active == tb);
+ tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON_STROKE_CAP_SQUARE));
+ tb->set_active(active == tb);
+}
+
+/**
+ * Sets the current marker in the marker menu.
+ */
+static void
+ink_marker_menu_set_current(SPObject *marker, Gtk::OptionMenu *mnu)
+{
+ mnu->set_data("update", GINT_TO_POINTER(TRUE));
+
+ Gtk::Menu *m = mnu->get_menu();
+ if (marker != NULL) {
+ bool mark_is_stock = false;
+ if (SP_OBJECT_REPR(marker)->attribute("inkscape:stockid"))
+ mark_is_stock = true;
+
+ gchar *markname;
+ if (mark_is_stock)
+ markname = g_strdup(SP_OBJECT_REPR(marker)->attribute("inkscape:stockid"));
+ else
+ markname = g_strdup(SP_OBJECT_REPR(marker)->attribute("id"));
+
+ int markpos = ink_marker_menu_get_pos(m, markname);
+ mnu->set_history(markpos);
+
+ g_free (markname);
+ }
+ else {
+ mnu->set_history(0);
+ }
+ mnu->set_data("update", GINT_TO_POINTER(FALSE));
+}
+
+/**
+ * Updates the marker menus to highlight the appropriate marker and scroll to
+ * that marker.
+ */
+static void
+sp_stroke_style_update_marker_menus(Gtk::Container *spw, GSList const *objects)
+{
+ struct { char const *key; int loc; } const keyloc[] = {
+ { "start_mark_menu", SP_MARKER_LOC_START },
+ { "mid_mark_menu", SP_MARKER_LOC_MID },
+ { "end_mark_menu", SP_MARKER_LOC_END }
+ };
+
+ bool all_texts = true;
+ for (GSList *i = (GSList *) objects; i != NULL; i = i->next) {
+ if (!SP_IS_TEXT (i->data)) {
+ all_texts = false;
+ }
+ }
+
+ for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) {
+ Gtk::OptionMenu *mnu = static_cast<Gtk::OptionMenu *>(spw->get_data(keyloc[i].key));
+ // Per SVG spec, text objects cannot have markers; disable menus if only texts are selected
+ mnu->set_sensitive(!all_texts);
+ }
+
+ // We show markers of the first object in the list only
+ // FIXME: use the first in the list that has the marker of each type, if any
+ SPObject *object = SP_OBJECT(objects->data);
+
+ for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) {
+ // For all three marker types,
+
+ // find the corresponding menu
+ Gtk::OptionMenu *mnu = static_cast<Gtk::OptionMenu *>(spw->get_data(keyloc[i].key));
+
+ // Quit if we're in update state
+ if (mnu->get_data("update")) {
+ return;
+ }
+
+ if (object->style->marker[keyloc[i].loc].value != NULL && !all_texts) {
+ // If the object has this type of markers,
+
+ // Extract the name of the marker that the object uses
+ SPObject *marker = ink_extract_marker_name(object->style->marker[keyloc[i].loc].value, SP_OBJECT_DOCUMENT(object));
+ // Scroll the menu to that marker
+ ink_marker_menu_set_current(marker, mnu);
+
+ } else {
+ mnu->set_history(0);
+ }
+ }
+}
+
+
+/**
+ * Extract the actual name of the link
+ * e.g. get mTriangle from url(#mTriangle).
+ * \return Buffer containing the actual name, allocated from GLib;
+ * the caller should free the buffer when they no longer need it.
+ */
+static SPObject*
+ink_extract_marker_name(gchar const *n, SPDocument *doc)
+{
+ gchar const *p = n;
+ while (*p != '\0' && *p != '#') {
+ p++;
+ }
+
+ if (*p == '\0' || p[1] == '\0') {
+ return NULL;
+ }
+
+ p++;
+ int c = 0;
+ while (p[c] != '\0' && p[c] != ')') {
+ c++;
+ }
+
+ if (p[c] == '\0') {
+ return NULL;
+ }
+
+ gchar* b = g_strdup(p);
+ b[c] = '\0';
+
+ // FIXME: get the document from the object and let the caller pass it in
+ SPObject *marker = doc->getObjectById(b);
+ return marker;
+}
+
+/*
+ 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/stroke-style.h b/src/widgets/stroke-style.h
new file mode 100644
index 000000000..b947209e3
--- /dev/null
+++ b/src/widgets/stroke-style.h
@@ -0,0 +1,35 @@
+/** @file
+ * @brief Stroke style dialog
+ */
+/* Author:
+ * Lauris Kaplinski <lauris@ximian.com>
+ *
+ * Copyright (C) 2001 Ximian, Inc.
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_DIALOGS_STROKE_STYLE_H
+#define SEEN_DIALOGS_STROKE_STYLE_H
+
+#include <glib.h>
+
+#include <gtk/gtkwidget.h>
+
+#include "forward.h"
+#include "display/canvas-bpath.h"
+
+GtkWidget *sp_stroke_style_paint_widget_new (void);
+Gtk::Container *sp_stroke_style_line_widget_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/toolbox.cpp b/src/widgets/toolbox.cpp
index 14acb188a..e3dcbcc14 100644
--- a/src/widgets/toolbox.cpp
+++ b/src/widgets/toolbox.cpp
@@ -36,7 +36,6 @@
#include "../box3d-context.h"
#include "../box3d.h"
-#include "calligraphic-profile-rename.h"
#include "../conn-avoid-ref.h"
#include "../connection-pool.h"
#include "../connector-context.h"
@@ -87,6 +86,7 @@
#include "../svg/css-ostringstream.h"
#include "../tools-switch.h"
#include "../tweak-context.h"
+#include "../ui/dialog/calligraphic-profile-rename.h"
#include "../ui/icon-names.h"
#include "../ui/widget/style-swatch.h"
#include "../verbs.h"