diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2009-02-21 01:59:56 +0000 |
|---|---|---|
| committer | tweenk <tweenk@users.sourceforge.net> | 2009-02-21 01:59:56 +0000 |
| commit | 154165799998cb2cb7491bbd97b0511943a0228e (patch) | |
| tree | 878507e5e42b4651f1bb0805a42c0837528eb314 /src/widgets | |
| parent | Only build static libraries for subdirs than actually contain libraries, (diff) | |
| download | inkscape-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_insert | 14 | ||||
| -rw-r--r-- | src/widgets/calligraphic-profile-rename.cpp | 108 | ||||
| -rw-r--r-- | src/widgets/calligraphic-profile-rename.h | 75 | ||||
| -rw-r--r-- | src/widgets/desktop-widget.cpp | 46 | ||||
| -rw-r--r-- | src/widgets/eek-color-def.cpp | 147 | ||||
| -rw-r--r-- | src/widgets/eek-color-def.h | 102 | ||||
| -rw-r--r-- | src/widgets/eek-preview.cpp | 736 | ||||
| -rw-r--r-- | src/widgets/eek-preview.h | 152 | ||||
| -rw-r--r-- | src/widgets/fill-style.cpp | 552 | ||||
| -rw-r--r-- | src/widgets/fill-style.h | 33 | ||||
| -rw-r--r-- | src/widgets/layer-selector.cpp | 611 | ||||
| -rw-r--r-- | src/widgets/layer-selector.h | 110 | ||||
| -rw-r--r-- | src/widgets/sp-attribute-widget.cpp | 790 | ||||
| -rw-r--r-- | src/widgets/sp-attribute-widget.h | 130 | ||||
| -rw-r--r-- | src/widgets/stroke-style.cpp | 1838 | ||||
| -rw-r--r-- | src/widgets/stroke-style.h | 35 | ||||
| -rw-r--r-- | src/widgets/toolbox.cpp | 2 |
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 ? "•" : " " ), - ( 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" |
