diff options
| author | Liam P. White <inkscapebrony@gmail.com> | 2014-10-29 22:40:05 +0000 |
|---|---|---|
| committer | Liam P. White <inkscapebrony@gmail.com> | 2014-10-29 22:40:05 +0000 |
| commit | e9943b70c7bf507b9639ecb0a421bcee7ce93e33 (patch) | |
| tree | 2d2fe7ee7a566e1ef1a5dcde18296d9f21188f35 /src/ui | |
| parent | i18n. Fixing untranslated strings. (diff) | |
| parent | Merge with trunk r13640 (diff) | |
| download | inkscape-e9943b70c7bf507b9639ecb0a421bcee7ce93e33.tar.gz inkscape-e9943b70c7bf507b9639ecb0a421bcee7ce93e33.zip | |
Merge experimental
(bzr r13641)
Diffstat (limited to 'src/ui')
169 files changed, 13205 insertions, 569 deletions
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 2b9dd6e45..e5c605889 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -2,7 +2,13 @@ set(ui_SRC clipboard.cpp control-manager.cpp + dialog-events.cpp + draw-anchor.cpp + interface.cpp + object-edit.cpp previewholder.cpp + shape-editor.cpp + tools-switch.cpp uxmanager.cpp cache/svg_preview_cache.cpp @@ -78,6 +84,7 @@ set(ui_SRC dialog/layers.cpp dialog/livepatheffect-add.cpp dialog/livepatheffect-editor.cpp + dialog/lpe-fillet-chamfer-properties.cpp dialog/memory.cpp dialog/messages.cpp dialog/new-from-template.cpp @@ -151,10 +158,17 @@ set(ui_SRC clipboard.h control-manager.h control-types.h + dialog-events.h + draw-anchor.h icon-names.h + interface.h + object-edit.h previewable.h previewfillable.h previewholder.h + shape-editor.h + tool-factory.h + tools-switch.h uxmanager.h cache/svg_preview_cache.h @@ -194,6 +208,7 @@ set(ui_SRC dialog/layers.h dialog/livepatheffect-add.h dialog/livepatheffect-editor.h + dialog/lpe-fillet-chamfer-properties.h dialog/memory.h dialog/messages.h dialog/new-from-template.h diff --git a/src/ui/Makefile_insert b/src/ui/Makefile_insert index 4081f86f8..7aeb4a83d 100644 --- a/src/ui/Makefile_insert +++ b/src/ui/Makefile_insert @@ -6,10 +6,23 @@ ink_common_sources += \ ui/control-manager.cpp \ ui/control-manager.h \ ui/control-types.h \ + ui/dialog-events.cpp \ + ui/dialog-events.h \ + ui/draw-anchor.cpp \ + ui/draw-anchor.h \ ui/icon-names.h \ + ui/interface.cpp \ + ui/interface.h \ + ui/object-edit.cpp \ + ui/object-edit.h \ ui/previewable.h \ ui/previewfillable.h \ ui/previewholder.cpp \ ui/previewholder.h \ + ui/shape-editor.cpp \ + ui/shape-editor.h \ + ui/tool-factory.h \ + ui/tools-switch.cpp \ + ui/tools-switch.h \ ui/uxmanager.cpp \ ui/uxmanager.h diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index 4b4b14c22..931a295d8 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -54,7 +54,7 @@ #include <2geom/transforms.h> #include "box3d.h" #include "gradient-drag.h" -#include "marker.h" +#include "sp-marker.h" #include "sp-item.h" #include "sp-item-transform.h" // for sp_item_scale_rel, used in _pasteSize #include "sp-path.h" @@ -77,7 +77,7 @@ #include "svg/css-ostringstream.h" // used in copy #include "ui/tools/text-tool.h" #include "text-editing.h" -#include "tools-switch.h" +#include "ui/tools-switch.h" #include "path-chemistry.h" #include "util/units.h" #include "helper/png-write.h" diff --git a/src/ui/dialog-events.cpp b/src/ui/dialog-events.cpp new file mode 100644 index 000000000..6bd93bbc3 --- /dev/null +++ b/src/ui/dialog-events.cpp @@ -0,0 +1,255 @@ +/** + * @file + * Event handler for dialog windows. + */ +/* Authors: + * bulia byak <bulia@dr.com> + * Johan Engelen <j.b.c.engelen@ewi.utwente.nl> + * + * Copyright (C) 2003-2007 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H +#include <glibmm/threads.h> +#endif + +#include <gtkmm/entry.h> +#include <gtkmm/window.h> +#include <gdk/gdkkeysyms.h> +#include "macros.h" +#include <gtk/gtk.h> +#include "desktop.h" +#include "inkscape-private.h" +#include "preferences.h" +#include "ui/tools/tool-base.h" + +#include "ui/dialog-events.h" + + +/** + * Remove focus from window to whoever it is transient for. + */ +void sp_dialog_defocus_cpp(Gtk::Window *win) +{ + //find out the document window we're transient for + Gtk::Window *w = win->get_transient_for(); + + //switch to it + if (w) { + w->present(); + } +} + +void +sp_dialog_defocus (GtkWindow *win) +{ + GtkWindow *w; + //find out the document window we're transient for + w = gtk_window_get_transient_for(GTK_WINDOW(win)); + //switch to it + + if (w) { + gtk_window_present (w); + } +} + + +/** + * Callback to defocus a widget's parent dialog. + */ +void sp_dialog_defocus_callback_cpp(Gtk::Entry *e) +{ + sp_dialog_defocus_cpp(dynamic_cast<Gtk::Window *>(e->get_toplevel())); +} + +void +sp_dialog_defocus_callback (GtkWindow * /*win*/, gpointer data) +{ + sp_dialog_defocus( GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(data))) ); +} + + + +void +sp_dialog_defocus_on_enter_cpp (Gtk::Entry *e) +{ + e->signal_activate().connect(sigc::bind(sigc::ptr_fun(&sp_dialog_defocus_callback_cpp), e)); +} + +void +sp_dialog_defocus_on_enter (GtkWidget *w) +{ + g_signal_connect ( G_OBJECT (w), "activate", + G_CALLBACK (sp_dialog_defocus_callback), w ); +} + + + +gboolean +sp_dialog_event_handler (GtkWindow *win, GdkEvent *event, gpointer data) +{ + +// if the focus is inside the Text and Font textview, do nothing + GObject *dlg = G_OBJECT(data); + if (g_object_get_data (dlg, "eatkeys")) { + return FALSE; + } + + gboolean ret = FALSE; + + switch (event->type) { + + case GDK_KEY_PRESS: + + switch (Inkscape::UI::Tools::get_group0_keyval (&event->key)) { + case GDK_KEY_Escape: + sp_dialog_defocus (win); + ret = TRUE; + break; + case GDK_KEY_F4: + case GDK_KEY_w: + case GDK_KEY_W: + // close dialog + if (MOD__CTRL_ONLY(event)) { + + /* this code sends a delete_event to the dialog, + * instead of just destroying it, so that the + * dialog can do some housekeeping, such as remember + * its position. + */ + GdkEventAny event; + GtkWidget *widget = GTK_WIDGET(win); + event.type = GDK_DELETE; + event.window = gtk_widget_get_window (widget); + event.send_event = TRUE; + g_object_ref (G_OBJECT (event.window)); + gtk_main_do_event(reinterpret_cast<GdkEvent*>(&event)); + g_object_unref (G_OBJECT (event.window)); + + ret = TRUE; + } + break; + default: // pass keypress to the canvas + break; + } + default: + ; + } + + return ret; + +} + + + +/** + * Make the argument dialog transient to the currently active document + * window. + */ +void sp_transientize(GtkWidget *dialog) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); +#ifndef WIN32 // FIXME: Temporary Win32 special code to enable transient dialogs + // _set_skip_taskbar_hint makes transient dialogs NON-transient! When dialogs + // are made transient (_set_transient_for), they are already removed from + // the taskbar in Win32. + if (prefs->getBool( "/options/dialogsskiptaskbar/value")) { + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE); + } +#endif + + gint transient_policy = prefs->getIntLimited("/options/transientpolicy/value", 1, 0, 2); + +#ifdef WIN32 // Win32 special code to enable transient dialogs + transient_policy = 2; +#endif + + if (transient_policy) { + + // if there's an active document window, attach dialog to it as a transient: + + if ( SP_ACTIVE_DESKTOP ) + { + SP_ACTIVE_DESKTOP->setWindowTransient (dialog, transient_policy); + } + } +} // end of sp_transientize() + +void on_transientize (SPDesktop *desktop, win_data *wd ) +{ + sp_transientize_callback (0, desktop, wd); +} + +void +sp_transientize_callback ( InkscapeApplication * /*inkscape*/, + SPDesktop *desktop, win_data *wd ) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gint transient_policy = prefs->getIntLimited( "/options/transientpolicy/value", 1, 0, 2); + +#ifdef WIN32 // Win32 special code to enable transient dialogs + transient_policy = 1; +#endif + + if (!transient_policy) + return; + + if (wd->win) + { + desktop->setWindowTransient (wd->win, transient_policy); + } +} + +void on_dialog_hide (GtkWidget *w) +{ + if (w) + gtk_widget_hide (w); +} + +void on_dialog_unhide (GtkWidget *w) +{ + if (w) + gtk_widget_show (w); +} + +gboolean +sp_dialog_hide(GObject * /*object*/, gpointer data) +{ + GtkWidget *dlg = GTK_WIDGET(data); + + if (dlg) + gtk_widget_hide (dlg); + + return TRUE; +} + + + +gboolean +sp_dialog_unhide(GObject * /*object*/, gpointer data) +{ + GtkWidget *dlg = GTK_WIDGET(data); + + if (dlg) + gtk_widget_show (dlg); + + return TRUE; +} + + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog-events.h b/src/ui/dialog-events.h new file mode 100644 index 000000000..b33eb3f38 --- /dev/null +++ b/src/ui/dialog-events.h @@ -0,0 +1,76 @@ +/** @file + * @brief Event handler for dialog windows + */ +/* Authors: + * bulia byak <bulia@dr.com> + * + * Copyright (C) 2003 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_DIALOG_EVENTS_H +#define SEEN_DIALOG_EVENTS_H + + +/* + * event callback can only accept one argument, but we need two, + * hence this struct. + * each dialog has a local static copy: + * win is the dialog window + * stop is the transientize semaphore: when 0, retransientizing this dialog + * is allowed + */ + +namespace Gtk { +class Window; +class Entry; +} + +class SPDesktop; + +struct InkscapeApplication; + +typedef struct { + GtkWidget *win; + guint stop; +} win_data; + + +gboolean sp_dialog_event_handler ( GtkWindow *win, + GdkEvent *event, + gpointer data ); + +void sp_dialog_defocus_cpp (Gtk::Window *win); +void sp_dialog_defocus_callback_cpp(Gtk::Entry *e); +void sp_dialog_defocus_on_enter_cpp(Gtk::Entry *e); + +void sp_dialog_defocus ( GtkWindow *win ); +void sp_dialog_defocus_callback ( GtkWindow *win, gpointer data ); +void sp_dialog_defocus_on_enter ( GtkWidget *w ); +void sp_transientize ( GtkWidget *win ); + +void on_transientize ( SPDesktop *desktop, + win_data *wd ); + +void sp_transientize_callback ( InkscapeApplication *inkscape, + SPDesktop *desktop, + win_data *wd ); + +void on_dialog_hide (GtkWidget *w); +void on_dialog_unhide (GtkWidget *w); +gboolean sp_dialog_hide (GObject *object, gpointer data); +gboolean sp_dialog_unhide (GObject *object, gpointer data); + +#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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert index daa8aea22..26a59ce2c 100644 --- a/src/ui/dialog/Makefile_insert +++ b/src/ui/dialog/Makefile_insert @@ -100,6 +100,8 @@ ink_common_sources += \ ui/dialog/template-load-tab.h \ ui/dialog/template-widget.cpp \ ui/dialog/template-widget.h \ + ui/dialog/tags.cpp \ + ui/dialpg/tags.h \ ui/dialog/text-edit.cpp \ ui/dialog/text-edit.h \ ui/dialog/tile.cpp \ @@ -114,4 +116,10 @@ ink_common_sources += \ ui/dialog/undo-history.h \ ui/dialog/xml-tree.cpp \ ui/dialog/xml-tree.h \ + ui/dialog/lpe-powerstroke-properties.cpp \ + ui/dialog/lpe-powerstroke-properties.h \ + ui/dialog/objects.cpp \ + ui/dialog/objects.h \ + ui/dialog/lpe-fillet-chamfer-properties.cpp \ + ui/dialog/lpe-fillet-chamfer-properties.h \ $(inkboard_dialogs) diff --git a/src/ui/dialog/aboutbox.cpp b/src/ui/dialog/aboutbox.cpp index fad10ae11..50b35ca2f 100644 --- a/src/ui/dialog/aboutbox.cpp +++ b/src/ui/dialog/aboutbox.cpp @@ -955,13 +955,13 @@ void AboutBox::initStrings() { } // namespace UI } // namespace Inkscape -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/aboutbox.h b/src/ui/dialog/aboutbox.h index 7b3308672..015e344a1 100644 --- a/src/ui/dialog/aboutbox.h +++ b/src/ui/dialog/aboutbox.h @@ -55,13 +55,13 @@ private: #endif // INKSCAPE_UI_DIALOG_ABOUTBOX_H -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index e02ccd3f1..431da7ad1 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -38,7 +38,7 @@ #include "sp-item-transform.h" #include "sp-text.h" #include "text-editing.h" -#include "tools-switch.h" +#include "ui/tools-switch.h" #include "ui/icon-names.h" #include "ui/tools/node-tool.h" #include "ui/tool/multi-path-manipulator.h" @@ -829,14 +829,14 @@ private : -static void on_tool_changed(Inkscape::Application */*inkscape*/, Inkscape::UI::Tools::ToolBase */*context*/, AlignAndDistribute *daad) +static void on_tool_changed(InkscapeApplication */*inkscape*/, Inkscape::UI::Tools::ToolBase */*context*/, AlignAndDistribute *daad) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (desktop && desktop->getEventContext()) daad->setMode(tools_active(desktop) == TOOLS_NODES); } -static void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, AlignAndDistribute *daad) +static void on_selection_changed(InkscapeApplication */*inkscape*/, Inkscape::Selection */*selection*/, AlignAndDistribute *daad) { daad->randomize_bbox = Geom::OptRect(); } diff --git a/src/ui/dialog/calligraphic-profile-rename.h b/src/ui/dialog/calligraphic-profile-rename.h index 3256338eb..fa13db196 100644 --- a/src/ui/dialog/calligraphic-profile-rename.h +++ b/src/ui/dialog/calligraphic-profile-rename.h @@ -16,7 +16,7 @@ #endif #if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> +# include <glibmm/threads.h> #endif #include <gtkmm/dialog.h> diff --git a/src/ui/dialog/clonetiler.cpp b/src/ui/dialog/clonetiler.cpp index 9b67132c0..5ef885ab2 100644 --- a/src/ui/dialog/clonetiler.cpp +++ b/src/ui/dialog/clonetiler.cpp @@ -38,7 +38,7 @@ #include "util/units.h" #include "helper/window.h" #include "inkscape.h" -#include "interface.h" +#include "ui/interface.h" #include "macros.h" #include "message-stack.h" #include "preferences.h" @@ -1349,7 +1349,7 @@ void CloneTiler::on_picker_color_changed(guint rgba) is_updating = false; } -void CloneTiler::clonetiler_change_selection(Inkscape::Application * /*inkscape*/, Inkscape::Selection *selection, GtkWidget *dlg) +void CloneTiler::clonetiler_change_selection(InkscapeApplication * /*inkscape*/, Inkscape::Selection *selection, GtkWidget *dlg) { GtkWidget *buttons = GTK_WIDGET(g_object_get_data (G_OBJECT(dlg), "buttons_on_tiles")); GtkWidget *status = GTK_WIDGET(g_object_get_data (G_OBJECT(dlg), "status")); @@ -1378,7 +1378,7 @@ void CloneTiler::clonetiler_change_selection(Inkscape::Application * /*inkscape* } } -void CloneTiler::clonetiler_external_change(Inkscape::Application * /*inkscape*/, GtkWidget *dlg) +void CloneTiler::clonetiler_external_change(InkscapeApplication * /*inkscape*/, GtkWidget *dlg) { clonetiler_change_selection (NULL, sp_desktop_selection(SP_ACTIVE_DESKTOP), dlg); } diff --git a/src/ui/dialog/clonetiler.h b/src/ui/dialog/clonetiler.h index e2a0240ee..70da86338 100644 --- a/src/ui/dialog/clonetiler.h +++ b/src/ui/dialog/clonetiler.h @@ -11,7 +11,6 @@ #define __SP_CLONE_TILER_H__ #include "ui/widget/panel.h" -#include <gtk/gtk.h> #include "ui/dialog/desktop-tracker.h" #include "ui/widget/color-picker.h" @@ -58,8 +57,8 @@ protected: static void clonetiler_keep_bbox_toggled(GtkToggleButton *tb, gpointer /*data*/); static void clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg); static void clonetiler_unclump(GtkWidget */*widget*/, void *); - static void clonetiler_change_selection(Inkscape::Application * /*inkscape*/, Inkscape::Selection *selection, GtkWidget *dlg); - static void clonetiler_external_change(Inkscape::Application * /*inkscape*/, GtkWidget *dlg); + static void clonetiler_change_selection(InkscapeApplication * /*inkscape*/, Inkscape::Selection *selection, GtkWidget *dlg); + static void clonetiler_external_change(InkscapeApplication * /*inkscape*/, GtkWidget *dlg); static void clonetiler_disconnect_gsignal(GObject *widget, gpointer source); static void clonetiler_reset(GtkWidget */*widget*/, GtkWidget *dlg); static guint clonetiler_number_of_clones(SPObject *obj); diff --git a/src/ui/dialog/color-item.cpp b/src/ui/dialog/color-item.cpp index 7940c28ae..bab7e18e1 100644 --- a/src/ui/dialog/color-item.cpp +++ b/src/ui/dialog/color-item.cpp @@ -684,6 +684,7 @@ void ColorItem::buttonClicked(bool secondary) descr = secondary? _("Set stroke color to none") : _("Set fill color to none"); break; } +//mark case ege::PaintDef::RGB: { Glib::ustring colorspec; if ( _grad ){ @@ -696,6 +697,7 @@ void ColorItem::buttonClicked(bool secondary) sp_svg_write_color(c, sizeof(c), rgba); colorspec = c; } +//end mark sp_repr_css_set_property( css, attrName, colorspec.c_str() ); descr = secondary? _("Set stroke color from swatch") : _("Set fill color from swatch"); break; diff --git a/src/ui/dialog/desktop-tracker.cpp b/src/ui/dialog/desktop-tracker.cpp index 8a359dd2d..3ed998252 100644 --- a/src/ui/dialog/desktop-tracker.cpp +++ b/src/ui/dialog/desktop-tracker.cpp @@ -94,7 +94,7 @@ sigc::connection DesktopTracker::connectDesktopChanged( const sigc::slot<void, S return desktopChangedSig.connect(slot); } -gboolean DesktopTracker::activateDesktopCB(Inkscape::Application */*inkscape*/, SPDesktop *desktop, DesktopTracker *self ) +gboolean DesktopTracker::activateDesktopCB(InkscapeApplication */*inkscape*/, SPDesktop *desktop, DesktopTracker *self ) { if (self && self->trackActive) { self->setDesktop(desktop); diff --git a/src/ui/dialog/desktop-tracker.h b/src/ui/dialog/desktop-tracker.h index 7da55cf2f..7b944ddfa 100644 --- a/src/ui/dialog/desktop-tracker.h +++ b/src/ui/dialog/desktop-tracker.h @@ -12,11 +12,10 @@ typedef struct _GtkWidget GtkWidget; class SPDesktop; +struct InkscapeApplication; namespace Inkscape { -struct Application; - namespace UI { namespace Dialog { @@ -37,7 +36,7 @@ public: sigc::connection connectDesktopChanged( const sigc::slot<void, SPDesktop*> & slot ); private: - static gboolean activateDesktopCB(Inkscape::Application *inkscape, SPDesktop *desktop, DesktopTracker *self ); + static gboolean activateDesktopCB(InkscapeApplication *inkscape, SPDesktop *desktop, DesktopTracker *self ); static bool hierarchyChangeCB(GtkWidget *widget, GtkWidget* prev, DesktopTracker *self); void handleHierarchyChange(); diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index 7e69e439a..7b1b36908 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -54,6 +54,8 @@ #include "ui/dialog/xml-tree.h" #include "ui/dialog/clonetiler.h" #include "ui/dialog/svg-fonts-dialog.h" +#include "ui/dialog/objects.h" +#include "ui/dialog/tags.h" namespace Inkscape { namespace UI { @@ -70,13 +72,13 @@ inline Dialog *create() { return PanelDialog<B>::template create<T>(); } /** * This class is provided as a container for Inkscape's various - * dialogs. This allows Inkscape::Application to treat the various + * dialogs. This allows InkscapeApplication to treat the various * dialogs it invokes, as abstractions. * * DialogManager is essentially a cache of dialogs. It lets us * initialize dialogs lazily - instead of constructing them during * application startup, they're constructed the first time they're - * actually invoked by Inkscape::Application. The constructed + * actually invoked by InkscapeApplication. The constructed * dialog is held here after that, so future invokations of the * dialog don't need to get re-constructed each time. The memory for * the dialogs are then reclaimed when the DialogManager is destroyed. @@ -110,6 +112,8 @@ DialogManager::DialogManager() { registerFactory("Glyphs", &create<GlyphsPanel, FloatingBehavior>); registerFactory("IconPreviewPanel", &create<IconPreviewPanel, FloatingBehavior>); registerFactory("LayersPanel", &create<LayersPanel, FloatingBehavior>); + registerFactory("ObjectsPanel", &create<ObjectsPanel, FloatingBehavior>); + registerFactory("TagsPanel", &create<TagsPanel, FloatingBehavior>); registerFactory("LivePathEffect", &create<LivePathEffectEditor, FloatingBehavior>); registerFactory("Memory", &create<Memory, FloatingBehavior>); registerFactory("Messages", &create<Messages, FloatingBehavior>); @@ -143,6 +147,8 @@ DialogManager::DialogManager() { registerFactory("Glyphs", &create<GlyphsPanel, DockBehavior>); registerFactory("IconPreviewPanel", &create<IconPreviewPanel, DockBehavior>); registerFactory("LayersPanel", &create<LayersPanel, DockBehavior>); + registerFactory("ObjectsPanel", &create<ObjectsPanel, DockBehavior>); + registerFactory("TagsPanel", &create<TagsPanel, DockBehavior>); registerFactory("LivePathEffect", &create<LivePathEffectEditor, DockBehavior>); registerFactory("Memory", &create<Memory, DockBehavior>); registerFactory("Messages", &create<Messages, DockBehavior>); diff --git a/src/ui/dialog/dialog.cpp b/src/ui/dialog/dialog.cpp index 645294bb5..213965a18 100644 --- a/src/ui/dialog/dialog.cpp +++ b/src/ui/dialog/dialog.cpp @@ -28,7 +28,7 @@ #include "desktop-handles.h" #include "shortcuts.h" #include "preferences.h" -#include "interface.h" +#include "ui/interface.h" #include "verbs.h" #include "ui/tool/event-utils.h" @@ -41,7 +41,7 @@ namespace Inkscape { namespace UI { namespace Dialog { -void sp_retransientize(Inkscape::Application */*inkscape*/, SPDesktop *desktop, gpointer dlgPtr) +void sp_retransientize(InkscapeApplication */*inkscape*/, SPDesktop *desktop, gpointer dlgPtr) { Dialog *dlg = static_cast<Dialog *>(dlgPtr); dlg->onDesktopActivated (desktop); @@ -317,20 +317,8 @@ void Dialog::_apply() void Dialog::_close() { - GtkWidget *dlg = GTK_WIDGET(_behavior->gobj()); - - GdkEventAny event; - event.type = GDK_DELETE; - event.window = gtk_widget_get_window(dlg); - event.send_event = TRUE; - - if (event.window) - g_object_ref(G_OBJECT(event.window)); - - gtk_main_do_event ((GdkEvent*)&event); - - if (event.window) - g_object_unref(G_OBJECT(event.window)); + _behavior->hide(); + _onDeleteEvent(NULL); } void Dialog::_defocus() diff --git a/src/ui/dialog/dialog.h b/src/ui/dialog/dialog.h index ec5d203bc..ccff43a56 100644 --- a/src/ui/dialog/dialog.h +++ b/src/ui/dialog/dialog.h @@ -18,10 +18,10 @@ #include "floating-behavior.h" class SPDesktop; +struct InkscapeApplication; namespace Inkscape { class Selection; -struct Application; } namespace Inkscape { @@ -30,7 +30,7 @@ namespace Dialog { enum BehaviorType { FLOATING, DOCK }; -void sp_retransientize(Inkscape::Application *inkscape, SPDesktop *desktop, gpointer dlgPtr); +void sp_retransientize(InkscapeApplication *inkscape, SPDesktop *desktop, gpointer dlgPtr); gboolean sp_retransientize_again(gpointer dlgPtr); void sp_dialog_shutdown(GObject *object, gpointer dlgPtr); diff --git a/src/ui/dialog/dock-behavior.cpp b/src/ui/dialog/dock-behavior.cpp index 9b216e841..50a6db208 100644 --- a/src/ui/dialog/dock-behavior.cpp +++ b/src/ui/dialog/dock-behavior.cpp @@ -18,13 +18,13 @@ #include "dock-behavior.h" #include "inkscape.h" #include "desktop.h" -#include "interface.h" +#include "ui/interface.h" #include "widgets/icon.h" #include "ui/widget/dock.h" #include "verbs.h" #include "dialog.h" #include "preferences.h" -#include "dialogs/dialog-events.h" +#include "ui/dialog-events.h" #include <gtkmm/invisible.h> #include <gtkmm/label.h> diff --git a/src/ui/dialog/document-metadata.cpp b/src/ui/dialog/document-metadata.cpp index 09c505860..77ea175d9 100644 --- a/src/ui/dialog/document-metadata.cpp +++ b/src/ui/dialog/document-metadata.cpp @@ -223,7 +223,7 @@ DocumentMetadata::_handleDocumentReplaced(SPDesktop* desktop, SPDocument *) } void -DocumentMetadata::_handleActivateDesktop(Inkscape::Application *, SPDesktop *desktop) +DocumentMetadata::_handleActivateDesktop(InkscapeApplication *, SPDesktop *desktop) { Inkscape::XML::Node *repr = sp_desktop_namedview(desktop)->getRepr(); repr->addListener(&_repr_events, this); @@ -231,7 +231,7 @@ DocumentMetadata::_handleActivateDesktop(Inkscape::Application *, SPDesktop *des } void -DocumentMetadata::_handleDeactivateDesktop(Inkscape::Application *, SPDesktop *desktop) +DocumentMetadata::_handleDeactivateDesktop(InkscapeApplication *, SPDesktop *desktop) { Inkscape::XML::Node *repr = sp_desktop_namedview(desktop)->getRepr(); repr->removeListenerByData(this); diff --git a/src/ui/dialog/document-metadata.h b/src/ui/dialog/document-metadata.h index 3b7ed1ec8..77084bc3d 100644 --- a/src/ui/dialog/document-metadata.h +++ b/src/ui/dialog/document-metadata.h @@ -56,8 +56,8 @@ protected: void init(); void _handleDocumentReplaced(SPDesktop* desktop, SPDocument *document); - void _handleActivateDesktop(Inkscape::Application *application, SPDesktop *desktop); - void _handleDeactivateDesktop(Inkscape::Application *application, SPDesktop *desktop); + void _handleActivateDesktop(InkscapeApplication *application, SPDesktop *desktop); + void _handleDeactivateDesktop(InkscapeApplication *application, SPDesktop *desktop); Gtk::Notebook _notebook; diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index 1f220b246..dc8a0fee2 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -31,13 +31,13 @@ #include "inkscape.h" #include "io/sys.h" #include "preferences.h" -#include "shape-editor.h" +#include "ui/shape-editor.h" #include "sp-namedview.h" #include "sp-root.h" #include "sp-script.h" #include "style.h" #include "svg/stringstream.h" -#include "tools-switch.h" +#include "ui/tools-switch.h" #include "ui/widget/color-picker.h" #include "ui/widget/scalar-unit.h" #include "ui/dialog/filedialog.h" @@ -1594,7 +1594,7 @@ void DocumentProperties::_handleDocumentReplaced(SPDesktop* desktop, SPDocument update(); } -void DocumentProperties::_handleActivateDesktop(Inkscape::Application *, SPDesktop *desktop) +void DocumentProperties::_handleActivateDesktop(InkscapeApplication *, SPDesktop *desktop) { Inkscape::XML::Node *repr = sp_desktop_namedview(desktop)->getRepr(); repr->addListener(&_repr_events, this); @@ -1603,7 +1603,7 @@ void DocumentProperties::_handleActivateDesktop(Inkscape::Application *, SPDeskt update(); } -void DocumentProperties::_handleDeactivateDesktop(Inkscape::Application *, SPDesktop *desktop) +void DocumentProperties::_handleDeactivateDesktop(InkscapeApplication *, SPDesktop *desktop) { Inkscape::XML::Node *repr = sp_desktop_namedview(desktop)->getRepr(); repr->removeListenerByData(this); diff --git a/src/ui/dialog/document-properties.h b/src/ui/dialog/document-properties.h index 495f3177d..ee7e88b18 100644 --- a/src/ui/dialog/document-properties.h +++ b/src/ui/dialog/document-properties.h @@ -94,8 +94,8 @@ protected: void save_default_metadata(); void _handleDocumentReplaced(SPDesktop* desktop, SPDocument *document); - void _handleActivateDesktop(Inkscape::Application *application, SPDesktop *desktop); - void _handleDeactivateDesktop(Inkscape::Application *application, SPDesktop *desktop); + void _handleActivateDesktop(InkscapeApplication *application, SPDesktop *desktop); + void _handleDeactivateDesktop(InkscapeApplication *application, SPDesktop *desktop); Inkscape::XML::SignalObserver _emb_profiles_observer, _scripts_observer; Gtk::Notebook _notebook; diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp index 913713e5c..a4d8801f7 100644 --- a/src/ui/dialog/export.cpp +++ b/src/ui/dialog/export.cpp @@ -62,10 +62,10 @@ #include "sp-namedview.h" #include "selection-chemistry.h" -#include "dialogs/dialog-events.h" +#include "ui/dialog-events.h" #include "preferences.h" #include "verbs.h" -#include "interface.h" +#include "ui/interface.h" #include "sp-root.h" #include "extension/output.h" diff --git a/src/ui/dialog/export.h b/src/ui/dialog/export.h index 6f3c0dfac..23af0109b 100644 --- a/src/ui/dialog/export.h +++ b/src/ui/dialog/export.h @@ -12,20 +12,11 @@ #ifndef SP_EXPORT_H #define SP_EXPORT_H -#include <gtk/gtk.h> -#include <glibmm/i18n.h> - -#include <gtkmm/comboboxtext.h> -#include <gtkmm/expander.h> -#include <gtkmm/frame.h> #include <gtkmm/progressbar.h> -#include <gtkmm/textview.h> -#include "desktop.h" #include "ui/dialog/desktop-tracker.h" #include "ui/widget/panel.h" #include "ui/widget/button.h" -#include "ui/widget/entry.h" namespace Gtk { class Dialog; diff --git a/src/ui/dialog/extension-editor.cpp b/src/ui/dialog/extension-editor.cpp index cd5491c24..9bdddc0e0 100644 --- a/src/ui/dialog/extension-editor.cpp +++ b/src/ui/dialog/extension-editor.cpp @@ -25,7 +25,7 @@ #include "verbs.h" #include "preferences.h" -#include "interface.h" +#include "ui/interface.h" #include "extension/extension.h" #include "extension/db.h" diff --git a/src/ui/dialog/filedialog.cpp b/src/ui/dialog/filedialog.cpp index e71605739..00ed09551 100644 --- a/src/ui/dialog/filedialog.cpp +++ b/src/ui/dialog/filedialog.cpp @@ -20,7 +20,7 @@ #include "filedialog.h" #include "gc-core.h" -#include <dialogs/dialog-events.h> +#include "ui/dialog-events.h" #include "extension/output.h" #include "preferences.h" diff --git a/src/ui/dialog/filedialog.h b/src/ui/dialog/filedialog.h index 8dfcf5dce..175031bcf 100644 --- a/src/ui/dialog/filedialog.h +++ b/src/ui/dialog/filedialog.h @@ -48,6 +48,7 @@ typedef enum { IMPORT_TYPES, EXPORT_TYPES, EXE_TYPES, + SWATCH_TYPES, CUSTOM_TYPE } FileDialogType; diff --git a/src/ui/dialog/filedialogimpl-gtkmm.cpp b/src/ui/dialog/filedialogimpl-gtkmm.cpp index 8ba3ad684..5d330f7f0 100644 --- a/src/ui/dialog/filedialogimpl-gtkmm.cpp +++ b/src/ui/dialog/filedialogimpl-gtkmm.cpp @@ -21,9 +21,11 @@ #include <config.h> #endif +#include <iostream> + #include "filedialogimpl-gtkmm.h" -#include "dialogs/dialog-events.h" -#include "interface.h" +#include "ui/dialog-events.h" +#include "ui/interface.h" #include "io/sys.h" #include "path-prefix.h" #include "preferences.h" @@ -40,6 +42,9 @@ #include <glibmm/i18n.h> #include <glibmm/miscutils.h> +#include <glibmm/regex.h> + +#include "document.h" #include "extension/input.h" #include "extension/output.h" #include "extension/db.h" @@ -190,6 +195,40 @@ void SVGPreview::showImage(Glib::ustring &theFileName) { Glib::ustring fileName = theFileName; + // Let's get real width and height from SVG file. These are template + // files so we assume they are well formed. + + // std::cout << "SVGPreview::showImage: " << theFileName << std::endl; + std::ifstream input(theFileName.c_str()); + + std::string width; + std::string height; + + if( !input ) { + std::cerr << "SVGPreview::showImage: Failed to open file: " << theFileName << std::endl; + } else { + + std::string token; + + Glib::MatchInfo match_info; + Glib::RefPtr<Glib::Regex> regex1 = Glib::Regex::create("width=\"(.*)\""); + Glib::RefPtr<Glib::Regex> regex2 = Glib::Regex::create("height=\"(.*)\""); + + while( !input.eof() && (height.empty() || width.empty()) ) { + + input >> token; + // std::cout << "|" << token << "|" << std::endl; + + if (regex1->match(token, match_info)) { + width = match_info.fetch(1).raw(); + } + + if (regex2->match(token, match_info)) { + height = match_info.fetch(1).raw(); + } + + } + } /*##################################### # LET'S HAVE SOME FUN WITH SVG! @@ -269,7 +308,7 @@ void SVGPreview::showImage(Glib::ustring &theFileName) " style=\"font-size:24.000000;font-style:normal;font-weight:normal;" " fill:#000000;fill-opacity:1.0000000;stroke:none;" " font-family:Sans\"\n" - " x=\"10\" y=\"26\">%d x %d</text>\n" //# VALUES HERE + " x=\"10\" y=\"26\">%s x %s</text>\n" //# VALUES HERE "</svg>\n\n"; // if (!Glib::get_charset()) //If we are not utf8 @@ -279,7 +318,7 @@ void SVGPreview::showImage(Glib::ustring &theFileName) /* FIXME: Do proper XML quoting for fileName. */ gchar *xmlBuffer = g_strdup_printf(xformat, previewWidth, previewHeight, imgX, imgY, scaledImgWidth, scaledImgHeight, - fileName.c_str(), rectX, rectY, rectWidth, rectHeight, imgWidth, imgHeight); + fileName.c_str(), rectX, rectY, rectWidth, rectHeight, width.c_str(), height.c_str() ); // g_message("%s\n", xmlBuffer); diff --git a/src/ui/dialog/filedialogimpl-win32.cpp b/src/ui/dialog/filedialogimpl-win32.cpp index 1eeee0592..ee6a0ef3a 100644 --- a/src/ui/dialog/filedialogimpl-win32.cpp +++ b/src/ui/dialog/filedialogimpl-win32.cpp @@ -31,7 +31,7 @@ //Inkscape includes #include "inkscape.h" -#include "dialogs/dialog-events.h" +#include "ui/dialog-events.h" #include "extension/input.h" #include "extension/output.h" #include "extension/db.h" @@ -1853,7 +1853,6 @@ void FileSaveDialogImplWin32::GetSaveFileName_thread() ofn.nFilterIndex = _filter_index; ofn.lpfnHook = GetSaveFileName_hookproc; ofn.lCustData = (LPARAM)this; - _result = GetSaveFileNameW(&ofn) != 0; g_assert(ofn.nFilterIndex >= 1 && ofn.nFilterIndex <= _filter_count); diff --git a/src/ui/dialog/filedialogimpl-win32.h b/src/ui/dialog/filedialogimpl-win32.h index c523f041d..f77249abd 100644 --- a/src/ui/dialog/filedialogimpl-win32.h +++ b/src/ui/dialog/filedialogimpl-win32.h @@ -22,13 +22,14 @@ #endif #endif +#include "filedialogimpl-gtkmm.h" + #include "gc-core.h" // define WINVER high enough so we get the correct OPENFILENAMEW size #ifndef WINVER #define WINVER 0x0500 #endif #include <windows.h> -#include "filedialogimpl-gtkmm.h" namespace Inkscape diff --git a/src/ui/dialog/fill-and-stroke.h b/src/ui/dialog/fill-and-stroke.h index 340cb860f..35c98ef9c 100644 --- a/src/ui/dialog/fill-and-stroke.h +++ b/src/ui/dialog/fill-and-stroke.h @@ -41,7 +41,7 @@ public: virtual void setDesktop(SPDesktop *desktop); - void selectionChanged(Inkscape::Application *inkscape, + void selectionChanged(InkscapeApplication *inkscape, Inkscape::Selection *selection); void showPageFill(); diff --git a/src/ui/dialog/find.cpp b/src/ui/dialog/find.cpp index 37f2761df..1a4823e4a 100644 --- a/src/ui/dialog/find.cpp +++ b/src/ui/dialog/find.cpp @@ -31,9 +31,9 @@ #include "selection.h" #include "desktop-handles.h" -#include "dialogs/dialog-events.h" +#include "ui/dialog-events.h" #include "verbs.h" -#include "interface.h" +#include "ui/interface.h" #include "preferences.h" #include "sp-text.h" #include "sp-flowtext.h" diff --git a/src/ui/dialog/floating-behavior.cpp b/src/ui/dialog/floating-behavior.cpp index dd07f009a..11db14801 100644 --- a/src/ui/dialog/floating-behavior.cpp +++ b/src/ui/dialog/floating-behavior.cpp @@ -28,8 +28,8 @@ #include "inkscape.h" #include "desktop.h" -#include "dialogs/dialog-events.h" -#include "interface.h" +#include "ui/dialog-events.h" +#include "ui/interface.h" #include "preferences.h" #include "verbs.h" diff --git a/src/ui/dialog/font-substitution.cpp b/src/ui/dialog/font-substitution.cpp index 6e9ecc3c8..db7bdf222 100644 --- a/src/ui/dialog/font-substitution.cpp +++ b/src/ui/dialog/font-substitution.cpp @@ -27,7 +27,7 @@ #include "document.h" #include "selection.h" -#include "dialogs/dialog-events.h" +#include "ui/dialog-events.h" #include "desktop-handles.h" #include "selection-chemistry.h" #include "preferences.h" diff --git a/src/ui/dialog/grid-arrange-tab.cpp b/src/ui/dialog/grid-arrange-tab.cpp index 72217c729..1417b39fa 100644 --- a/src/ui/dialog/grid-arrange-tab.cpp +++ b/src/ui/dialog/grid-arrange-tab.cpp @@ -572,7 +572,7 @@ void GridArrangeTab::updateSelection() ## Experimental ##########################*/ -static void updateSelectionCallback(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, GridArrangeTab *dlg) +static void updateSelectionCallback(InkscapeApplication */*inkscape*/, Inkscape::Selection */*selection*/, GridArrangeTab *dlg) { dlg->updateSelection(); } diff --git a/src/ui/dialog/grid-arrange-tab.h b/src/ui/dialog/grid-arrange-tab.h index 9d6700b39..a137d1694 100644 --- a/src/ui/dialog/grid-arrange-tab.h +++ b/src/ui/dialog/grid-arrange-tab.h @@ -17,14 +17,16 @@ #ifndef INKSCAPE_UI_DIALOG_GRID_ARRANGE_TAB_H #define INKSCAPE_UI_DIALOG_GRID_ARRANGE_TAB_H -#include <gtkmm.h> - +#include "ui/widget/scalar-unit.h" #include "ui/dialog/arrange-tab.h" #include "ui/widget/anchor-selector.h" -#include "ui/widget/scalar-unit.h" #include "ui/widget/spinbutton.h" +#include <gtkmm/checkbutton.h> +#include <gtkmm/radiobutton.h> +#include <gtkmm/radiobuttongroup.h> + namespace Inkscape { namespace UI { namespace Dialog { diff --git a/src/ui/dialog/guides.cpp b/src/ui/dialog/guides.cpp index 80740113c..4519a905f 100644 --- a/src/ui/dialog/guides.cpp +++ b/src/ui/dialog/guides.cpp @@ -28,7 +28,7 @@ #include "ui/tools/tool-base.h" #include "widgets/desktop-widget.h" #include <glibmm/i18n.h> -#include "dialogs/dialog-events.h" +#include "ui/dialog-events.h" #include "message-context.h" #include "xml/repr.h" #include "verbs.h" @@ -100,7 +100,7 @@ void GuidelinePropertiesDialog::_onOK() double rad_angle = Geom::deg_to_rad( deg_angle ); normal = Geom::rot90(Geom::Point::polar(rad_angle, 1.0)); } - sp_guide_set_normal(*_guide, normal, true); + _guide->set_normal(normal, true); double const points_x = _spin_button_x.getValue("px"); double const points_y = _spin_button_y.getValue("px"); @@ -108,11 +108,11 @@ void GuidelinePropertiesDialog::_onOK() if (!_mode) newpos += _oldpos; - sp_guide_moveto(*_guide, newpos, true); + _guide->moveto(newpos, true); const gchar* name = g_strdup( _label_entry.getEntry()->get_text().c_str() ); - sp_guide_set_label(*_guide, name, true); + _guide->set_label(name, true); g_free((gpointer) name); #if WITH_GTKMM_3_0 @@ -124,7 +124,7 @@ void GuidelinePropertiesDialog::_onOK() #endif //TODO: why 257? verify this! - sp_guide_set_color(*_guide, r, g, b, true); + _guide->set_color(r, g, b, true); DocumentUndo::done(_guide->document, SP_VERB_NONE, _("Set guide properties")); @@ -295,16 +295,17 @@ void GuidelinePropertiesDialog::_setup() { signal_response().connect(sigc::mem_fun(*this, &GuidelinePropertiesDialog::_response)); // initialize dialog - _oldpos = _guide->point_on_line; + _oldpos = _guide->getPoint(); if (_guide->isVertical()) { _oldangle = 90; } else if (_guide->isHorizontal()) { _oldangle = 0; } else { - _oldangle = Geom::rad_to_deg( std::atan2( - _guide->normal_to_line[Geom::X], _guide->normal_to_line[Geom::Y] ) ); + _oldangle = Geom::rad_to_deg( std::atan2( - _guide->getNormal()[Geom::X], _guide->getNormal()[Geom::Y] ) ); } { + // FIXME holy crap!!! Inkscape::XML::Node *repr = _guide->getRepr(); const gchar *guide_id = repr->attribute("id"); gchar *label = g_strdup_printf(_("Guideline ID: %s"), guide_id); @@ -312,7 +313,7 @@ void GuidelinePropertiesDialog::_setup() { g_free(label); } { - gchar *guide_description = sp_guide_description(_guide, false); + gchar *guide_description = _guide->description(false); gchar *label = g_strdup_printf(_("Current: %s"), guide_description); g_free(guide_description); _label_descr.set_markup(label); @@ -320,15 +321,15 @@ void GuidelinePropertiesDialog::_setup() { } // init name entry - _label_entry.getEntry()->set_text(_guide->label ? _guide->label : ""); + _label_entry.getEntry()->set_text(_guide->getLabel() ? _guide->getLabel() : ""); #if WITH_GTKMM_3_0 Gdk::RGBA c; - c.set_rgba(((_guide->color>>24)&0xff) / 255.0, ((_guide->color>>16)&0xff) / 255.0, ((_guide->color>>8)&0xff) / 255.0); + c.set_rgba(((_guide->getColor()>>24)&0xff) / 255.0, ((_guide->getColor()>>16)&0xff) / 255.0, ((_guide->getColor()>>8)&0xff) / 255.0); _color.set_rgba(c); #else Gdk::Color c; - c.set_rgb_p(((_guide->color>>24)&0xff) / 255.0, ((_guide->color>>16)&0xff) / 255.0, ((_guide->color>>8)&0xff) / 255.0); + c.set_rgb_p(((_guide->getColor()>>24)&0xff) / 255.0, ((_guide->getColor()>>16)&0xff) / 255.0, ((_guide->getColor()>>8)&0xff) / 255.0); _color.set_color(c); #endif diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 9f37626ed..dcea91741 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -545,9 +545,9 @@ private: Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp new file mode 100644 index 000000000..ad8b66b8c --- /dev/null +++ b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp @@ -0,0 +1,278 @@ +/** + * From the code of Liam P.White from his Power Stroke Knot dialog + * + * Released under GNU GPL. Read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if GLIBMM_DISABLE_DEPRECATED &&HAVE_GLIBMM_THREADS_H +#include <glibmm/threads.h> +#endif + +#include <gtkmm.h> +#include "lpe-fillet-chamfer-properties.h" +#include <boost/lexical_cast.hpp> +#include <glibmm/main.h> +#include <glibmm/i18n.h> +#include "inkscape.h" +#include "desktop.h" +#include "document.h" +#include "document-undo.h" +#include "layer-manager.h" +#include "message-stack.h" +#include "desktop-handles.h" +#include "sp-object.h" +#include "sp-item.h" +#include "verbs.h" +#include "selection.h" +#include "selection-chemistry.h" +#include "ui/icon-names.h" +#include "ui/widget/imagetoggler.h" +#include "util/units.h" +#include <cmath> + +//#include "event-context.h" + +namespace Inkscape { +namespace UI { +namespace Dialogs { + +FilletChamferPropertiesDialog::FilletChamferPropertiesDialog() + : _desktop(NULL), _knotpoint(NULL), _position_visible(false) +{ + Gtk::Box *mainVBox = get_vbox(); + mainVBox->set_homogeneous(false); + _layout_table.set_spacings(4); + _layout_table.resize(2, 2); + + // Layer name widgets + _fillet_chamfer_position_numeric.set_digits(4); + _fillet_chamfer_position_numeric.set_increments(1,1); + //todo: get tha max aloable infinity freeze the widget + _fillet_chamfer_position_numeric.set_range(0., 999999999999999999.); + + _fillet_chamfer_position_label.set_label(_("Radius (pixels):")); + _fillet_chamfer_position_label.set_alignment(1.0, 0.5); + + _layout_table.attach(_fillet_chamfer_position_label, 0, 1, 0, 1, Gtk::FILL, + Gtk::FILL); + _layout_table.attach(_fillet_chamfer_position_numeric, 1, 2, 0, 1, + Gtk::FILL | Gtk::EXPAND, Gtk::FILL); + _fillet_chamfer_type_fillet.set_label(_("Fillet")); + _fillet_chamfer_type_fillet.set_group(_fillet_chamfer_type_group); + _fillet_chamfer_type_inverse_fillet.set_label(_("Inverse fillet")); + _fillet_chamfer_type_inverse_fillet.set_group(_fillet_chamfer_type_group); + _fillet_chamfer_type_chamfer.set_label(_("Chamfer")); + _fillet_chamfer_type_chamfer.set_group(_fillet_chamfer_type_group); + _fillet_chamfer_type_double_chamfer.set_label(_("Double chamfer")); + _fillet_chamfer_type_double_chamfer.set_group(_fillet_chamfer_type_group); + + mainVBox->pack_start(_layout_table, true, true, 4); + mainVBox->pack_start(_fillet_chamfer_type_fillet, true, true, 4); + mainVBox->pack_start(_fillet_chamfer_type_inverse_fillet, true, true, 4); + mainVBox->pack_start(_fillet_chamfer_type_chamfer, true, true, 4); + mainVBox->pack_start(_fillet_chamfer_type_double_chamfer, true, true, 4); + + // Buttons + _close_button.set_use_stock(true); + _close_button.set_label(Gtk::Stock::CANCEL.id); + _close_button.set_can_default(); + + _apply_button.set_use_underline(true); + _apply_button.set_can_default(); + + _close_button.signal_clicked() + .connect(sigc::mem_fun(*this, &FilletChamferPropertiesDialog::_close)); + _apply_button.signal_clicked() + .connect(sigc::mem_fun(*this, &FilletChamferPropertiesDialog::_apply)); + + signal_delete_event().connect(sigc::bind_return( + sigc::hide(sigc::mem_fun(*this, &FilletChamferPropertiesDialog::_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(); + + set_focus(_fillet_chamfer_position_numeric); +} + +FilletChamferPropertiesDialog::~FilletChamferPropertiesDialog() +{ + + _setDesktop(NULL); +} + +void FilletChamferPropertiesDialog::showDialog( + SPDesktop *desktop, Geom::Point knotpoint, + const Inkscape::LivePathEffect:: + FilletChamferPointArrayParamKnotHolderEntity *pt, + const gchar *unit, + bool use_distance, + bool aprox_radius) +{ + FilletChamferPropertiesDialog *dialog = new FilletChamferPropertiesDialog(); + + dialog->_setDesktop(desktop); + dialog->_setUnit(unit); + dialog->_set_use_distance(use_distance); + dialog->_set_aprox(aprox_radius); + dialog->_setKnotPoint(knotpoint); + dialog->_setPt(pt); + + dialog->set_title(_("Modify Fillet-Chamfer")); + dialog->_apply_button.set_label(_("_Modify")); + + dialog->set_modal(true); + desktop->setWindowTransient(dialog->gobj()); + dialog->property_destroy_with_parent() = true; + + dialog->show(); + dialog->present(); +} + +void FilletChamferPropertiesDialog::_apply() +{ + double d_width; + double d_pos = _fillet_chamfer_position_numeric.get_value(); + if (d_pos) { + if (_fillet_chamfer_type_fillet.get_active() == true) { + d_width = 1; + } else if (_fillet_chamfer_type_inverse_fillet.get_active() == true) { + d_width = 2; + } else if (_fillet_chamfer_type_chamfer.get_active() == true) { + d_width = 3; + } else { + d_width = 4; + } + if (_flexible) { + if (d_pos > 99.99999 || d_pos < 0) { + d_pos = 0; + } + d_pos = _index + (d_pos / 100); + } else { + d_pos = Inkscape::Util::Quantity::convert(d_pos, unit, "px"); + d_pos = d_pos * -1; + } + _knotpoint->knot_set_offset(Geom::Point(d_pos, d_width)); + } + _close(); +} + +void FilletChamferPropertiesDialog::_close() +{ + _setDesktop(NULL); + destroy_(); + Glib::signal_idle().connect( + sigc::bind_return( + sigc::bind(sigc::ptr_fun(&::operator delete), this), + false + ) + ); +} + +bool FilletChamferPropertiesDialog::_handleKeyEvent(GdkEventKey *event) +{ + return false; +} + +void FilletChamferPropertiesDialog::_handleButtonEvent(GdkEventButton *event) +{ + if ((event->type == GDK_2BUTTON_PRESS) && (event->button == 1)) { + _apply(); + } +} + +void FilletChamferPropertiesDialog::_setKnotPoint(Geom::Point knotpoint) +{ + double position; + std::string distance_or_radius = std::string(_("Radius ")); + if(aprox){ + distance_or_radius = std::string(_("Radius aproximated ")); + } + if(use_distance){ + distance_or_radius = std::string(_("Knot distance ")); + } + if (knotpoint.x() > 0) { + double intpart; + position = modf(knotpoint[Geom::X], &intpart) * 100; + _flexible = true; + _index = intpart; + _fillet_chamfer_position_label.set_label(_("Position (%):")); + } else { + _flexible = false; + std::string posConcat = distance_or_radius + + std::string(_("(")) + std::string(unit) + std::string(")"); + _fillet_chamfer_position_label.set_label(_(posConcat.c_str())); + position = knotpoint[Geom::X] * -1; + position = Inkscape::Util::Quantity::convert(position, "px", unit); + } + _fillet_chamfer_position_numeric.set_value(position); + if (knotpoint.y() == 1) { + _fillet_chamfer_type_fillet.set_active(true); + } else if (knotpoint.y() == 2) { + _fillet_chamfer_type_inverse_fillet.set_active(true); + } else if (knotpoint.y() == 3) { + _fillet_chamfer_type_chamfer.set_active(true); + } else if (knotpoint.y() == 1) { + _fillet_chamfer_type_double_chamfer.set_active(true); + } +} + +void FilletChamferPropertiesDialog::_setPt( + const Inkscape::LivePathEffect:: + FilletChamferPointArrayParamKnotHolderEntity *pt) +{ + _knotpoint = const_cast< + Inkscape::LivePathEffect::FilletChamferPointArrayParamKnotHolderEntity *>( + pt); +} + +void FilletChamferPropertiesDialog::_setUnit(const gchar *abbr) +{ + unit = abbr; +} + +void FilletChamferPropertiesDialog::_set_use_distance(bool use_knot_distance) +{ + use_distance = use_knot_distance; +} + +void FilletChamferPropertiesDialog::_set_aprox(bool aprox_radius) +{ + aprox = aprox_radius; +} + +void FilletChamferPropertiesDialog::_setDesktop(SPDesktop *desktop) +{ + if (desktop) { + Inkscape::GC::anchor(desktop); + } + if (_desktop) { + Inkscape::GC::release(_desktop); + } + _desktop = desktop; +} + +} // namespace +} // namespace +} // namespace + +/* + 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:fileencoding=utf-8:textwidth=99 +// : diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.h b/src/ui/dialog/lpe-fillet-chamfer-properties.h new file mode 100644 index 000000000..47ff97b00 --- /dev/null +++ b/src/ui/dialog/lpe-fillet-chamfer-properties.h @@ -0,0 +1,110 @@ +/** + * + * From the code of Liam P.White from his Power Stroke Knot dialog + * + * Released under GNU GPL. Read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_DIALOG_FILLET_CHAMFER_PROPERTIES_H +#define INKSCAPE_DIALOG_FILLET_CHAMFER_PROPERTIES_H + +#include <2geom/point.h> +#include <gtkmm.h> +#include "live_effects/parameter/filletchamferpointarray.h" + +class SPDesktop; + +namespace Inkscape { +namespace UI { +namespace Dialogs { + +class FilletChamferPropertiesDialog : public Gtk::Dialog { +public: + FilletChamferPropertiesDialog(); + virtual ~FilletChamferPropertiesDialog(); + + Glib::ustring getName() const { + return "LayerPropertiesDialog"; + } + + static void showDialog(SPDesktop *desktop, Geom::Point knotpoint, + const Inkscape::LivePathEffect:: + FilletChamferPointArrayParamKnotHolderEntity *pt, + const gchar *unit, + bool use_distance, + bool aprox_radius); + +protected: + + SPDesktop *_desktop; + Inkscape::LivePathEffect::FilletChamferPointArrayParamKnotHolderEntity * + _knotpoint; + + Gtk::Label _fillet_chamfer_position_label; + Gtk::SpinButton _fillet_chamfer_position_numeric; + Gtk::RadioButton::Group _fillet_chamfer_type_group; + Gtk::RadioButton _fillet_chamfer_type_fillet; + Gtk::RadioButton _fillet_chamfer_type_inverse_fillet; + Gtk::RadioButton _fillet_chamfer_type_chamfer; + Gtk::RadioButton _fillet_chamfer_type_double_chamfer; + + Gtk::Table _layout_table; + bool _position_visible; + double _index; + + Gtk::Button _close_button; + Gtk::Button _apply_button; + + sigc::connection _destroy_connection; + + static FilletChamferPropertiesDialog &_instance() { + static FilletChamferPropertiesDialog instance; + return instance; + } + + void _setDesktop(SPDesktop *desktop); + void _setPt(const Inkscape::LivePathEffect:: + FilletChamferPointArrayParamKnotHolderEntity *pt); + void _setUnit(const gchar *abbr); + void _set_use_distance(bool use_knot_distance); + void _set_aprox(bool aprox_radius); + void _apply(); + void _close(); + bool _flexible; + const gchar *unit; + bool use_distance; + bool aprox; + void _setKnotPoint(Geom::Point knotpoint); + void _prepareLabelRenderer(Gtk::TreeModel::const_iterator const &row); + + bool _handleKeyEvent(GdkEventKey *event); + void _handleButtonEvent(GdkEventButton *event); + + friend class Inkscape::LivePathEffect:: + FilletChamferPointArrayParamKnotHolderEntity; + +private: + FilletChamferPropertiesDialog( + FilletChamferPropertiesDialog const &); // no copy + FilletChamferPropertiesDialog &operator=( + FilletChamferPropertiesDialog const &); // no assign +}; + +} // namespace +} // namespace +} // namespace + +#endif //INKSCAPE_DIALOG_LAYER_PROPERTIES_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:fileencoding=utf-8:textwidth=99 +// : diff --git a/src/ui/dialog/lpe-powerstroke-properties.cpp b/src/ui/dialog/lpe-powerstroke-properties.cpp new file mode 100644 index 000000000..c34351511 --- /dev/null +++ b/src/ui/dialog/lpe-powerstroke-properties.cpp @@ -0,0 +1,211 @@ +/** + * @file + * Dialog for renaming layers. + */ +/* Author: + * Bryce W. Harrington <bryce@bryceharrington.com> + * Andrius R. <knutux@gmail.com> + * Abhishek Sharma + * + * Copyright (C) 2004 Bryce Harrington + * Copyright (C) 2006 Andrius R. + * + * Released under GNU GPL. Read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H +#include <glibmm/threads.h> +#endif + +#include "lpe-powerstroke-properties.h" +#include <boost/lexical_cast.hpp> +#include <gtkmm/stock.h> +#include <glibmm/main.h> +#include <glibmm/i18n.h> +#include "inkscape.h" +#include "desktop.h" +#include "document.h" +#include "document-undo.h" +#include "layer-manager.h" +#include "message-stack.h" +#include "desktop-handles.h" +#include "sp-object.h" +#include "sp-item.h" +#include "verbs.h" +#include "selection.h" +#include "selection-chemistry.h" +#include "ui/icon-names.h" +#include "ui/widget/imagetoggler.h" +//#include "event-context.h" + +namespace Inkscape { +namespace UI { +namespace Dialogs { + +PowerstrokePropertiesDialog::PowerstrokePropertiesDialog() +: _desktop(NULL), _knotpoint(NULL), _position_visible(false) +{ + Gtk::Box *mainVBox = get_vbox(); + + _layout_table.set_spacings(4); + _layout_table.resize (2, 2); + + // Layer name widgets + _powerstroke_position_entry.set_activates_default(true); + _powerstroke_position_label.set_label(_("Position:")); + _powerstroke_position_label.set_alignment(1.0, 0.5); + + _powerstroke_width_entry.set_activates_default(true); + _powerstroke_width_label.set_label(_("Width:")); + _powerstroke_width_label.set_alignment(1.0, 0.5); + + _layout_table.attach(_powerstroke_position_label, + 0, 1, 0, 1, Gtk::FILL, Gtk::FILL); + _layout_table.attach(_powerstroke_position_entry, + 1, 2, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL); + + _layout_table.attach(_powerstroke_width_label, 0, 1, 1, 2, Gtk::FILL, Gtk::FILL); + _layout_table.attach(_powerstroke_width_entry, 1, 2, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::FILL); + + mainVBox->pack_start(_layout_table, true, true, 4); + + // Buttons + _close_button.set_use_stock(true); + _close_button.set_label(Gtk::Stock::CANCEL.id); + _close_button.set_can_default(); + + _apply_button.set_use_underline(true); + _apply_button.set_can_default(); + + _close_button.signal_clicked() + .connect(sigc::mem_fun(*this, &PowerstrokePropertiesDialog::_close)); + _apply_button.signal_clicked() + .connect(sigc::mem_fun(*this, &PowerstrokePropertiesDialog::_apply)); + + signal_delete_event().connect( + sigc::bind_return( + sigc::hide(sigc::mem_fun(*this, &PowerstrokePropertiesDialog::_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(); + + set_focus(_powerstroke_width_entry); +} + +PowerstrokePropertiesDialog::~PowerstrokePropertiesDialog() { + + _setDesktop(NULL); +} + +void PowerstrokePropertiesDialog::showDialog(SPDesktop *desktop, Geom::Point knotpoint, const Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity *pt) +{ + PowerstrokePropertiesDialog *dialog = new PowerstrokePropertiesDialog(); + + dialog->_setDesktop(desktop); + dialog->_setKnotPoint(knotpoint); + dialog->_setPt(pt); + + dialog->set_title(_("Modify Node Position")); + dialog->_apply_button.set_label(_("_Move")); + + dialog->set_modal(true); + desktop->setWindowTransient (dialog->gobj()); + dialog->property_destroy_with_parent() = true; + + dialog->show(); + dialog->present(); +} + +void +PowerstrokePropertiesDialog::_apply() +{ + std::istringstream i_pos(_powerstroke_position_entry.get_text()); + std::istringstream i_width(_powerstroke_width_entry.get_text()); + double d_pos, d_width; + if ((i_pos >> d_pos) && i_width >> d_width) { + _knotpoint->knot_set_offset(Geom::Point(d_pos, d_width)); + } + _close(); +} + +void +PowerstrokePropertiesDialog::_close() +{ + _setDesktop(NULL); + destroy_(); + Glib::signal_idle().connect( + sigc::bind_return( + sigc::bind(sigc::ptr_fun(&::operator delete), this), + false + ) + ); +} + +bool PowerstrokePropertiesDialog::_handleKeyEvent(GdkEventKey *event) +{ + + /*switch (get_group0_keyval(event)) { + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: { + _apply(); + return true; + } + break; + }*/ + return false; +} + +void PowerstrokePropertiesDialog::_handleButtonEvent(GdkEventButton* event) +{ + if ( (event->type == GDK_2BUTTON_PRESS) && (event->button == 1) ) { + _apply(); + } +} + +void PowerstrokePropertiesDialog::_setKnotPoint(Geom::Point knotpoint) +{ + _powerstroke_position_entry.set_text(boost::lexical_cast<std::string>(knotpoint.x())); + _powerstroke_width_entry.set_text(boost::lexical_cast<std::string>(knotpoint.y())); +} + +void PowerstrokePropertiesDialog::_setPt(const Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity *pt) +{ + _knotpoint = const_cast<Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity *>(pt); +} + +void PowerstrokePropertiesDialog::_setDesktop(SPDesktop *desktop) { + if (desktop) { + Inkscape::GC::anchor (desktop); + } + if (_desktop) { + Inkscape::GC::release (_desktop); + } + _desktop = desktop; +} + +} // namespace +} // namespace +} // namespace + + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/lpe-powerstroke-properties.h b/src/ui/dialog/lpe-powerstroke-properties.h new file mode 100644 index 000000000..c53eac0d9 --- /dev/null +++ b/src/ui/dialog/lpe-powerstroke-properties.h @@ -0,0 +1,99 @@ +/** @file + * @brief Dialog for renaming layers + */ +/* Author: + * Bryce W. Harrington <bryce@bryceharrington.com> + * + * Copyright (C) 2004 Bryce Harrington + * + * Released under GNU GPL. Read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_DIALOG_POWERSTROKE_PROPERTIES_H +#define INKSCAPE_DIALOG_POWERSTROKE_PROPERTIES_H + +#include <2geom/point.h> +#include <gtkmm/dialog.h> +#include <gtkmm/entry.h> +#include <gtkmm/label.h> +#include <gtkmm/table.h> +#include <gtkmm/combobox.h> +#include <gtkmm/liststore.h> +#include <gtkmm/treeview.h> +#include <gtkmm/treestore.h> +#include <gtkmm/scrolledwindow.h> +#include "live_effects/parameter/powerstrokepointarray.h" + +class SPDesktop; + +namespace Inkscape { +namespace UI { +namespace Dialogs { + +class PowerstrokePropertiesDialog : public Gtk::Dialog { + public: + PowerstrokePropertiesDialog(); + virtual ~PowerstrokePropertiesDialog(); + + Glib::ustring getName() const { return "LayerPropertiesDialog"; } + + static void showDialog(SPDesktop *desktop, Geom::Point knotpoint, const Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity *pt); + +protected: + + SPDesktop *_desktop; + Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity *_knotpoint; + + Gtk::Label _powerstroke_position_label; + Gtk::Entry _powerstroke_position_entry; + Gtk::Label _powerstroke_width_label; + Gtk::Entry _powerstroke_width_entry; + Gtk::Table _layout_table; + bool _position_visible; + + Gtk::Button _close_button; + Gtk::Button _apply_button; + + sigc::connection _destroy_connection; + + static PowerstrokePropertiesDialog &_instance() { + static PowerstrokePropertiesDialog instance; + return instance; + } + + void _setDesktop(SPDesktop *desktop); + void _setPt(const Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity *pt); + + void _apply(); + void _close(); + + void _setKnotPoint(Geom::Point knotpoint); + void _prepareLabelRenderer(Gtk::TreeModel::const_iterator const &row); + + bool _handleKeyEvent(GdkEventKey *event); + void _handleButtonEvent(GdkEventButton* event); + + friend class Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity; + +private: + PowerstrokePropertiesDialog(PowerstrokePropertiesDialog const &); // no copy + PowerstrokePropertiesDialog &operator=(PowerstrokePropertiesDialog const &); // no assign +}; + +} // namespace +} // namespace +} // namespace + + +#endif //INKSCAPE_DIALOG_LAYER_PROPERTIES_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/new-from-template.cpp b/src/ui/dialog/new-from-template.cpp index f326bb3ee..e30b148bb 100644 --- a/src/ui/dialog/new-from-template.cpp +++ b/src/ui/dialog/new-from-template.cpp @@ -8,6 +8,9 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#if HAVE_CONFIG_H + #include "config.h" +#endif #include "new-from-template.h" #include "file.h" @@ -25,12 +28,22 @@ NewFromTemplate::NewFromTemplate() { set_title(_("New From Template")); resize(400, 400); - + +#if WITH_GTKMM_3_0 + get_content_area()->pack_start(_main_widget); +#else get_vbox()->pack_start(_main_widget); +#endif Gtk::Alignment *align; align = Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_END, Gtk::ALIGN_CENTER, 0.0, 0.0)); + +#if WITH_GTKMM_3_0 + get_content_area()->pack_end(*align, Gtk::PACK_SHRINK); +#else get_vbox()->pack_end(*align, Gtk::PACK_SHRINK); +#endif + align->set_padding(0, 0, 0, 15); align->add(_create_template_button); diff --git a/src/ui/dialog/objects.cpp b/src/ui/dialog/objects.cpp new file mode 100644 index 000000000..93d5dfbd5 --- /dev/null +++ b/src/ui/dialog/objects.cpp @@ -0,0 +1,2144 @@ +/* + * A simple panel for objects + * + * Authors: + * Theodore Janeczko + * Tweaked by Liam P White for use in Inkscape + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "objects.h" +#include <gtkmm/widget.h> +#include <gtkmm/icontheme.h> +#include <gtkmm/imagemenuitem.h> +#include <gtkmm/separatormenuitem.h> +#include <gtkmm/stock.h> + +#include <glibmm/i18n.h> +#include <glibmm/main.h> + +#include "desktop.h" +#include "desktop-style.h" +#include "ui/dialog-events.h" +#include "document.h" +#include "document-undo.h" +#include "filter-chemistry.h" +#include "filters/blend.h" +#include "filters/gaussian-blur.h" +#include "helper/action.h" +#include "inkscape.h" +#include "layer-manager.h" +#include "preferences.h" +#include "selection.h" +#include "sp-clippath.h" +#include "sp-mask.h" +#include "sp-item.h" +#include "sp-object.h" +#include "sp-root.h" +#include "sp-shape.h" +#include "style.h" +#include "ui/tools-switch.h" +#include "ui/icon-names.h" +#include "ui/widget/imagetoggler.h" +#include "ui/widget/layertypeicon.h" +#include "ui/widget/insertordericon.h" +#include "ui/widget/clipmaskicon.h" +#include "ui/widget/highlight-picker.h" +#include "ui/tools/node-tool.h" +#include "ui/tools/tool-base.h" +#include "verbs.h" +#include "widgets/sp-color-notebook.h" +#include "widgets/icon.h" +#include "xml/node.h" +#include "xml/node-observer.h" +#include "xml/repr.h" + +//#define DUMP_LAYERS 1 + +namespace Inkscape { +namespace UI { +namespace Dialog { + +using Inkscape::XML::Node; + +/** + * Gets an instance of the Objects panel + */ +ObjectsPanel& ObjectsPanel::getInstance() +{ + return *new ObjectsPanel(); +} + +/** + * Column enumeration + */ +enum { + COL_VISIBLE = 1, + COL_LOCKED, + COL_TYPE, +// COL_INSERTORDER, + COL_CLIPMASK, + COL_HIGHLIGHT +}; + +/** + * Button enumeration + */ +enum { + BUTTON_NEW = 0, + BUTTON_RENAME, + BUTTON_TOP, + BUTTON_BOTTOM, + BUTTON_UP, + BUTTON_DOWN, + BUTTON_DUPLICATE, + BUTTON_DELETE, + BUTTON_SOLO, + BUTTON_SHOW_ALL, + BUTTON_HIDE_ALL, + BUTTON_LOCK_OTHERS, + BUTTON_LOCK_ALL, + BUTTON_UNLOCK_ALL, + BUTTON_SETCLIP, + BUTTON_CLIPGROUP, +// BUTTON_SETINVCLIP, + BUTTON_UNSETCLIP, + BUTTON_SETMASK, + BUTTON_UNSETMASK, + BUTTON_GROUP, + BUTTON_UNGROUP, + BUTTON_COLLAPSE_ALL, + DRAGNDROP +}; + +/** + * Xml node observer for observing objects in the document + */ +class ObjectsPanel::ObjectWatcher : public Inkscape::XML::NodeObserver { +public: + /** + * Creates a new object watcher + * @param pnl The panel to which the object watcher belongs + * @param obj The object to watch + */ + ObjectWatcher(ObjectsPanel* pnl, SPObject* obj) : + _pnl(pnl), + _obj(obj), + _repr(obj->getRepr()), + _highlightAttr(g_quark_from_string("inkscape:highlight-color")), + _lockedAttr(g_quark_from_string("sodipodi:insensitive")), + _labelAttr(g_quark_from_string("inkscape:label")), + _groupAttr(g_quark_from_string("inkscape:groupmode")), + _styleAttr(g_quark_from_string("style")), + _clipAttr(g_quark_from_string("clip-path")), + _maskAttr(g_quark_from_string("mask")) + {} + + virtual void notifyChildAdded( Node &/*node*/, Node &/*child*/, Node */*prev*/ ) + { + if ( _pnl && _obj ) { + _pnl->_objectsChanged( _obj ); + } + } + virtual void notifyChildRemoved( Node &/*node*/, Node &/*child*/, Node */*prev*/ ) + { + if ( _pnl && _obj ) { + _pnl->_objectsChanged( _obj ); + } + } + virtual void notifyChildOrderChanged( Node &/*node*/, Node &/*child*/, Node */*old_prev*/, Node */*new_prev*/ ) + { + if ( _pnl && _obj ) { + _pnl->_objectsChanged( _obj ); + } + } + virtual void notifyContentChanged( Node &/*node*/, Util::ptr_shared<char> /*old_content*/, Util::ptr_shared<char> /*new_content*/ ) {} + virtual void notifyAttributeChanged( Node &/*node*/, GQuark name, Util::ptr_shared<char> /*old_value*/, Util::ptr_shared<char> /*new_value*/ ) { + if ( _pnl && _obj ) { + if ( name == _lockedAttr || name == _labelAttr || name == _highlightAttr || name == _groupAttr || name == _styleAttr || name == _clipAttr || name == _maskAttr ) { + _pnl->_updateObject(_obj, name == _highlightAttr); + if ( name == _styleAttr ) { + _pnl->_updateComposite(); + } + } + } + } + + /** + * Objects panel to which this watcher belongs + */ + ObjectsPanel* _pnl; + + /** + * The object that is being observed + */ + SPObject* _obj; + + /** + * The xml representation of the object that is being observed + */ + Inkscape::XML::Node* _repr; + + /* These are quarks which define the attributes that we are observing */ + GQuark _highlightAttr; + GQuark _lockedAttr; + GQuark _labelAttr; + GQuark _groupAttr; + GQuark _styleAttr; + GQuark _clipAttr; + GQuark _maskAttr; +}; + +class ObjectsPanel::InternalUIBounce +{ +public: + int _actionCode; +}; + +class ObjectsPanel::ModelColumns : public Gtk::TreeModel::ColumnRecord +{ +public: + + ModelColumns() + { + add(_colObject); + add(_colVisible); + add(_colLocked); + add(_colLabel); + add(_colType); + add(_colHighlight); + add(_colClipMask); + //add(_colInsertOrder); + } + virtual ~ModelColumns() {} + + Gtk::TreeModelColumn<SPItem*> _colObject; + Gtk::TreeModelColumn<Glib::ustring> _colLabel; + Gtk::TreeModelColumn<bool> _colVisible; + Gtk::TreeModelColumn<bool> _colLocked; + Gtk::TreeModelColumn<int> _colType; + Gtk::TreeModelColumn<guint32> _colHighlight; + Gtk::TreeModelColumn<int> _colClipMask; + //Gtk::TreeModelColumn<int> _colInsertOrder; +}; + +/** + * Stylizes a button using the given icon name and tooltip + */ +void ObjectsPanel::_styleButton( Gtk::Button& btn, char const* iconName, char const* tooltip ) +{ + GtkWidget *child = sp_icon_new( Inkscape::ICON_SIZE_SMALL_TOOLBAR, iconName ); + gtk_widget_show( child ); + btn.add( *Gtk::manage(Glib::wrap(child)) ); + btn.set_relief(Gtk::RELIEF_NONE); + + btn.set_tooltip_text (tooltip); + +} + +/** + * Adds an item to the pop-up (right-click) menu + * @param desktop The active destktop + * @param code Action code + * @param iconName Icon name + * @param fallback Fallback text + * @param id Button id for callback function + * @return The generated menu item + */ +Gtk::MenuItem& ObjectsPanel::_addPopupItem( SPDesktop *desktop, unsigned int code, char const* iconName, char const* fallback, int id ) +{ + GtkWidget* iconWidget = 0; + const char* label = 0; + + if ( iconName ) { + iconWidget = sp_icon_new( Inkscape::ICON_SIZE_MENU, iconName ); + } + + if ( desktop ) { + Verb *verb = Verb::get( code ); + if ( verb ) { + SPAction *action = verb->get_action(desktop); + if ( !iconWidget && action && action->image ) { + iconWidget = sp_icon_new( Inkscape::ICON_SIZE_MENU, action->image ); + } + + if ( action ) { + // label = action->name; + } + } + } + + if ( !label && fallback ) { + label = fallback; + } + + Gtk::Widget* wrapped = 0; + if ( iconWidget ) { + wrapped = Gtk::manage(Glib::wrap(iconWidget)); + wrapped->show(); + } + + + Gtk::MenuItem* item = 0; + + if (wrapped) { + item = Gtk::manage(new Gtk::ImageMenuItem(*wrapped, label, true)); + } else { + item = Gtk::manage(new Gtk::MenuItem(label, true)); + } + + item->signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &ObjectsPanel::_takeAction), id)); + _popupMenu.append(*item); + + return *item; +} + +/** + * Callback function for when an object changes. Essentially refreshes the entire tree + * @param obj Object which was changed (currently not used as the entire tree is recreated) + */ +void ObjectsPanel::_objectsChanged(SPObject */*obj*/) +{ + //First, unattach the watchers + while (!_objectWatchers.empty()) + { + ObjectsPanel::ObjectWatcher *w = _objectWatchers.back(); + w->_repr->removeObserver(*w); + _objectWatchers.pop_back(); + delete w; + } + + if (_desktop) { + //Get the current document's root and use that to enumerate the tree + SPDocument* document = _desktop->doc(); + SPRoot* root = document->getRoot(); + if ( root ) { + _selectedConnection.block(); + //Clear the tree store + _store->clear(); + //Add all items recursively + _addObject( root, 0 ); + _selectedConnection.unblock(); + //Set the tree selection + _objectsSelected(_desktop->selection); + //Handle button sensitivity + _checkTreeSelection(); + } + } +} + +/** + * Recursively adds the children of the given item to the tree + * @param obj Root object to add to the tree + * @param parentRow Parent tree row (or NULL if adding to tree root) + */ +void ObjectsPanel::_addObject(SPObject* obj, Gtk::TreeModel::Row* parentRow) +{ + if ( _desktop && obj ) { + for ( SPObject *child = obj->children; child != NULL; child = child->next) { + + if (SP_IS_ITEM(child)) + { + SPItem * item = SP_ITEM(child); + SPGroup * group = SP_IS_GROUP(child) ? SP_GROUP(child) : 0; + + //Add the item to the tree and set the column information + Gtk::TreeModel::iterator iter = parentRow ? _store->prepend(parentRow->children()) : _store->prepend(); + Gtk::TreeModel::Row row = *iter; + row[_model->_colObject] = item; + //this seems to crash on convert stroke to path then undo (probably no ID?) + try { + row[_model->_colLabel] = item->label() ? item->label() : item->getId(); + } catch (...) { + row[_model->_colLabel] = Glib::ustring("getId_failure"); + g_critical("item->getId() failed, using \"getId_failure\""); + } + row[_model->_colVisible] = !item->isHidden(); + row[_model->_colLocked] = !item->isSensitive(); + row[_model->_colType] = group ? (group->layerMode() == SPGroup::LAYER ? 2 : 1) : 0; + row[_model->_colHighlight] = item->isHighlightSet() ? item->highlight_color() : item->highlight_color() & 0xffffff00; + row[_model->_colClipMask] = item->clip_ref && item->clip_ref->getObject() ? 1 : (item->mask_ref && item->mask_ref->getObject() ? 2 : 0); + //row[_model->_colInsertOrder] = group ? (group->insertBottom() ? 2 : 1) : 0; + + //If our parent object is a group and it's expanded, expand the tree + if (SP_IS_GROUP(obj) && SP_GROUP(obj)->expanded()) + { + _tree.expand_to_path( _store->get_path(iter) ); + } + + //Add an object watcher to the item + ObjectsPanel::ObjectWatcher *w = new ObjectsPanel::ObjectWatcher(this, child); + child->getRepr()->addObserver(*w); + _objectWatchers.push_back(w); + + //If the item is a group, recursively add its children + if (group) + { + _addObject( child, &row ); + } + } + } + } +} + +/** + * Updates an item in the tree and optionally recursively updates the item's children + * @param obj The item to update in the tree + * @param recurse Whether to recurse through the item's children + */ +void ObjectsPanel::_updateObject( SPObject *obj, bool recurse ) { + //Find the object in the tree store and update it + + //mark + _store->foreach_iter( sigc::bind<SPObject*>(sigc::mem_fun(*this, &ObjectsPanel::_checkForUpdated), obj) ); + //end mark + if (recurse) + { + for (SPObject * iter = obj->children; iter != NULL; iter = iter->next) + { + _updateObject(iter, recurse); + } + } +} + +/** + * Checks items in the tree store and updates the given item + * @param iter Current item being looked at in the tree + * @param obj Object to update + * @return + */ +bool ObjectsPanel::_checkForUpdated(const Gtk::TreeIter& iter, SPObject* obj) +{ + Gtk::TreeModel::Row row = *iter; + if ( obj == row[_model->_colObject] ) + { + //We found our item in the tree!! Update it! + SPItem * item = SP_IS_ITEM(obj) ? SP_ITEM(obj) : 0; + SPGroup * group = SP_IS_GROUP(obj) ? SP_GROUP(obj) : 0; + + row[_model->_colLabel] = obj->label() ? obj->label() : obj->getId(); + row[_model->_colVisible] = item ? !item->isHidden() : false; + row[_model->_colLocked] = item ? !item->isSensitive() : false; + row[_model->_colType] = group ? (group->layerMode() == SPGroup::LAYER ? 2 : 1) : 0; + row[_model->_colHighlight] = item ? (item->isHighlightSet() ? item->highlight_color() : item->highlight_color() & 0xffffff00) : 0; + row[_model->_colClipMask] = item ? (item->clip_ref && item->clip_ref->getObject() ? 1 : (item->mask_ref && item->mask_ref->getObject() ? 2 : 0)) : 0; + //row[_model->_colInsertOrder] = group ? (group->insertBottom() ? 2 : 1) : 0; + + return true; + } + + return false; +} + +/** + * Updates the composite controls for the selected item + */ +void ObjectsPanel::_updateComposite() { + if (!_blockCompositeUpdate) + { + //Set the default values + bool setValues = true; + + //Get/set the values + _tree.get_selection()->selected_foreach_iter(sigc::bind<bool *>(sigc::mem_fun(*this, &ObjectsPanel::_compositingChanged), &setValues)); + } +} + +/** + * Sets the compositing values for the first selected item in the tree + * @param iter Current tree item + * @param setValues Whether to set the compositing values + * @param blur Blur value to use + */ +void ObjectsPanel::_compositingChanged( const Gtk::TreeModel::iterator& iter, bool *setValues ) +{ + if (iter) { + Gtk::TreeModel::Row row = *iter; + SPItem *item = row[_model->_colObject]; + if (*setValues) + { + _setCompositingValues(item); + *setValues = false; + } + } +} + +/** + * Occurs when the current desktop selection changes + * @param sel The current selection + */ +void ObjectsPanel::_objectsSelected( Selection *sel ) { + + bool setOpacity = true; + _selectedConnection.block(); + _tree.get_selection()->unselect_all(); + SPItem *item = NULL; + for (const GSList * iter = sel->itemList(); iter != NULL; iter = iter->next) + { + item = reinterpret_cast<SPItem *>(iter->data); + if (setOpacity) + { + _setCompositingValues(item); + setOpacity = false; + } + _store->foreach(sigc::bind<SPItem *, bool>( sigc::mem_fun(*this, &ObjectsPanel::_checkForSelected), item, iter->next == NULL)); + } + if (!item) { + if (_desktop->currentLayer() && SP_IS_ITEM(_desktop->currentLayer())) { + item = SP_ITEM(_desktop->currentLayer()); + _setCompositingValues(item); + _store->foreach(sigc::bind<SPItem *, bool>( sigc::mem_fun(*this, &ObjectsPanel::_checkForSelected), item, true)); + } + } + _selectedConnection.unblock(); + _checkTreeSelection(); +} + +/** + * Helper function for setting the compositing values + * @param item Item to use for setting the compositing values + */ +void ObjectsPanel::_setCompositingValues(SPItem *item) +{ + //Block the connections to avoid interference + _opacityConnection.block(); + _blendConnection.block(); + _blurConnection.block(); + + //Set the opacity +#if WITH_GTKMM_3_0 + _opacity_adjustment->set_value((item->style->opacity.set ? SP_SCALE24_TO_FLOAT(item->style->opacity.value) : 1) * _opacity_adjustment->get_upper()); +#else + _opacity_adjustment.set_value((item->style->opacity.set ? SP_SCALE24_TO_FLOAT(item->style->opacity.value) : 1) * _opacity_adjustment.get_upper()); +#endif + SPFeBlend *spblend = NULL; + SPGaussianBlur *spblur = NULL; + if (item->style->getFilter()) + { + for(SPObject *primitive_obj = item->style->getFilter()->children; primitive_obj && SP_IS_FILTER_PRIMITIVE(primitive_obj); primitive_obj = primitive_obj->next) { + if(SP_IS_FEBLEND(primitive_obj) && !spblend) { + //Get the blend mode + spblend = SP_FEBLEND(primitive_obj); + } + + if(SP_IS_GAUSSIANBLUR(primitive_obj) && !spblur) { + //Get the blur value + spblur = SP_GAUSSIANBLUR(primitive_obj); + } + } + } + + //Set the blend mode + _fe_cb.set_blend_mode(spblend ? spblend->blend_mode : Inkscape::Filters::BLEND_NORMAL); + + //Set the blur value + Geom::OptRect bbox = item->bounds(SPItem::GEOMETRIC_BBOX); + if (bbox && spblur) { + double perimeter = bbox->dimensions()[Geom::X] + bbox->dimensions()[Geom::Y]; // fixme: this is only half the perimeter, is that correct? + _fe_blur.set_blur_value(spblur->stdDeviation.getNumber() * 400 / perimeter); + } else { + _fe_blur.set_blur_value(0); + } + + //Unblock connections + _blurConnection.unblock(); + _blendConnection.unblock(); + _opacityConnection.unblock(); +} + +/** + * Checks the tree and selects the specified item, optionally scrolling to the item + * @param path Current tree path + * @param iter Current tree item + * @param item Item to select in the tree + * @param scrollto Whether to scroll to the item + * @return Whether to continue searching the tree + */ +bool ObjectsPanel::_checkForSelected(const Gtk::TreePath &path, const Gtk::TreeIter& iter, SPItem* item, bool scrollto) +{ + bool stopGoing = false; + + Gtk::TreeModel::Row row = *iter; + if ( item == row[_model->_colObject] ) + { + //We found the item! Expand to the path and select it in the tree. + _tree.expand_to_path( path ); + + Glib::RefPtr<Gtk::TreeSelection> select = _tree.get_selection(); + + select->select(iter); + if (scrollto) { + //Scroll to the item in the tree + _tree.scroll_to_row(path); + } + + stopGoing = true; + } + + return stopGoing; +} + +/** + * Pushes the current tree selection to the canvas + */ +void ObjectsPanel::_pushTreeSelectionToCurrent() +{ + if ( _desktop && _desktop->currentRoot() ) { + //block connections for selection and compositing values to prevent interference + _selectionChangedConnection.block(); + + //Clear the selection and then iterate over the tree selection, pushing each item to the desktop + _desktop->selection->clear(); + bool setOpacity = true; + _tree.get_selection()->selected_foreach_iter( sigc::bind<bool *>(sigc::mem_fun(*this, &ObjectsPanel::_selected_row_callback), &setOpacity)); + //unblock connections + _selectionChangedConnection.unblock(); + + _checkTreeSelection(); + } +} + +/** + * Helper function for pushing the current tree selection to the current desktop + * @param iter Current tree item + * @param setCompositingValues Whether to set the compositing values + * @param blur + */ +void ObjectsPanel::_selected_row_callback( const Gtk::TreeModel::iterator& iter, bool *setCompositingValues ) +{ + if (iter) { + Gtk::TreeModel::Row row = *iter; + SPItem *item = row[_model->_colObject]; + if (!SP_IS_GROUP(item) || SP_GROUP(item)->layerMode() != SPGroup::LAYER) + { + //If the item is not a layer, then select it and set the current layer to its parent (if it's the first item) + if (_desktop->selection->isEmpty()) _desktop->setCurrentLayer(item->parent); + _desktop->selection->add(item); + } + else + { + //If the item is a layer, set the current layer + if (_desktop->selection->isEmpty()) _desktop->setCurrentLayer(item); + } + if (*setCompositingValues) + { + //Only set the compositing values for the first item + _setCompositingValues(item); + *setCompositingValues = false; + } + } +} + +/** + * Handles button sensitivity + */ +void ObjectsPanel::_checkTreeSelection() +{ + bool sensitive = _tree.get_selection()->count_selected_rows() > 0; + //TODO: top/bottom sensitivity + bool sensitiveNonTop = true; + bool sensitiveNonBottom = true; + + for ( std::vector<Gtk::Widget*>::iterator it = _watching.begin(); it != _watching.end(); ++it ) { + (*it)->set_sensitive( sensitive ); + } + for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonTop.begin(); it != _watchingNonTop.end(); ++it ) { + (*it)->set_sensitive( sensitiveNonTop ); + } + for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonBottom.begin(); it != _watchingNonBottom.end(); ++it ) { + (*it)->set_sensitive( sensitiveNonBottom ); + } +} + +/** + * Sets visibility of items in the tree + * @param iter Current item in the tree + * @param visible Whether the item should be visible or not + */ +void ObjectsPanel::_setVisibleIter( const Gtk::TreeModel::iterator& iter, const bool visible ) +{ + Gtk::TreeModel::Row row = *iter; + SPItem* item = row[_model->_colObject]; + if (item) + { + item->setHidden( !visible ); + row[_model->_colVisible] = visible; + item->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } +} + +/** + * Sets sensitivity of items in the tree + * @param iter Current item in the tree + * @param locked Whether the item should be locked + */ +void ObjectsPanel::_setLockedIter( const Gtk::TreeModel::iterator& iter, const bool locked ) +{ + Gtk::TreeModel::Row row = *iter; + SPItem* item = row[_model->_colObject]; + if (item) + { + item->setLocked( locked ); + row[_model->_colLocked] = locked; + item->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } +} + +/** + * Handles keyboard events + * @param event Keyboard event passed in from GDK + * @return Whether the event should be eaten (om nom nom) + */ +bool ObjectsPanel::_handleKeyEvent(GdkEventKey *event) +{ + + bool empty = _desktop->selection->isEmpty(); + + switch (Inkscape::UI::Tools::get_group0_keyval(event)) { + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + case GDK_KEY_F2: + { + Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected(); + if (iter && !_text_renderer->property_editable()) { + //Rename item + Gtk::TreeModel::Path *path = new Gtk::TreeModel::Path(iter); + _text_renderer->property_editable() = true; + _tree.set_cursor(*path, *_name_column, true); + grab_focus(); + return true; + } + } + break; + case GDK_KEY_Home: + //Move item(s) to top of containing group/layer + _fireAction( empty ? SP_VERB_LAYER_TO_TOP : SP_VERB_SELECTION_TO_FRONT ); + break; + case GDK_KEY_End: + //Move item(s) to bottom of containing group/layer + _fireAction( empty ? SP_VERB_LAYER_TO_BOTTOM : SP_VERB_SELECTION_TO_BACK ); + break; + case GDK_KEY_Page_Up: + { + //Move item(s) up in containing group/layer + int ch = event->state & GDK_SHIFT_MASK ? SP_VERB_LAYER_MOVE_TO_NEXT : SP_VERB_SELECTION_RAISE; + _fireAction( empty ? SP_VERB_LAYER_RAISE : ch ); + break; + } + case GDK_KEY_Page_Down: + { + //Move item(s) down in containing group/layer + int ch = event->state & GDK_SHIFT_MASK ? SP_VERB_LAYER_MOVE_TO_PREV : SP_VERB_SELECTION_LOWER; + _fireAction( empty ? SP_VERB_LAYER_LOWER : ch ); + break; + } + + //TODO: Handle Ctrl-A, etc. + default: + return false; + } + return true; +} + +/** + * Handles mouse events + * @param event Mouse event from GDK + * @return whether to eat the event (om nom nom) + */ +bool ObjectsPanel::_handleButtonEvent(GdkEventButton* event) +{ + static unsigned doubleclick = 0; + static bool overVisible = false; + + //Right mouse button was clicked, launch the pop-up menu + if ( (event->type == GDK_BUTTON_PRESS) && (event->button == 3) ) { + Gtk::TreeModel::Path path; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + if ( _tree.get_path_at_pos( x, y, path ) ) { + _checkTreeSelection(); + _popupMenu.popup(event->button, event->time); + if (_tree.get_selection()->is_selected(path)) { + return true; + } + } + } + + //Left mouse button was pressed! In order to handle multiple item drag & drop, + //we need to defer selection by setting the select function so that the tree doesn't + //automatically select anything. In order to handle multiple item icon clicking, + //we need to eat the event. There might be a better way to do both of these... + if ( (event->type == GDK_BUTTON_PRESS) && (event->button == 1)) { + overVisible = false; + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col = 0; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + int x2 = 0; + int y2 = 0; + if ( _tree.get_path_at_pos( x, y, path, col, x2, y2 ) ) { + if (col == _tree.get_column(COL_VISIBLE-1)) { + //Click on visible column, eat this event to keep row selection + overVisible = true; + return true; + } else if (col == _tree.get_column(COL_LOCKED-1) || + col == _tree.get_column(COL_TYPE-1) || + //col == _tree.get_column(COL_INSERTORDER - 1) || + col == _tree.get_column(COL_HIGHLIGHT-1)) { + //Click on an icon column, eat this event to keep row selection + return true; + } else if ( !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) & _tree.get_selection()->is_selected(path) ) { + //Click on a selected item with no modifiers, defer selection to the mouse-up by + //setting the select function to _noSelection + _tree.get_selection()->set_select_function(sigc::mem_fun(*this, &ObjectsPanel::_noSelection)); + _defer_target = path; + } + } + } + + //Restore the selection function to allow tree selection on mouse button release + if ( event->type == GDK_BUTTON_RELEASE) { + _tree.get_selection()->set_select_function(sigc::mem_fun(*this, &ObjectsPanel::_rowSelectFunction)); + } + + //CellRenderers do not have good support for dealing with multiple items, so + //we handle all events on them here + if ( (event->type == GDK_BUTTON_RELEASE) && (event->button == 1)) { + + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col = 0; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + int x2 = 0; + int y2 = 0; + if ( _tree.get_path_at_pos( x, y, path, col, x2, y2 ) ) { + if (_defer_target) { + //We had deferred a selection target, select it here (assuming no drag & drop) + if (_defer_target == path && !(event->x == 0 && event->y == 0)) + { + _tree.set_cursor(path, *col, false); + } + _defer_target = Gtk::TreeModel::Path(); + } + else { + if (event->state & GDK_SHIFT_MASK) { + // Shift left click on the visible/lock columns toggles "solo" mode + if (col == _tree.get_column(COL_VISIBLE - 1)) { + _takeAction(BUTTON_SOLO); + } else if (col == _tree.get_column(COL_LOCKED - 1)) { + _takeAction(BUTTON_LOCK_OTHERS); + } + } else if (event->state & GDK_MOD1_MASK) { + // Alt+left click on the visible/lock columns toggles "solo" mode and preserves selection + Gtk::TreeModel::iterator iter = _store->get_iter(path); + if (_store->iter_is_valid(iter)) { + Gtk::TreeModel::Row row = *iter; + SPItem *item = row[_model->_colObject]; + if (col == _tree.get_column(COL_VISIBLE - 1)) { + _desktop->toggleLayerSolo( item ); + DocumentUndo::maybeDone(_desktop->doc(), "layer:solo", SP_VERB_LAYER_SOLO, _("Toggle layer solo")); + } else if (col == _tree.get_column(COL_LOCKED - 1)) { + _desktop->toggleLockOtherLayers( item ); + DocumentUndo::maybeDone(_desktop->doc(), "layer:lockothers", SP_VERB_LAYER_LOCK_OTHERS, _("Lock other layers")); + } + } + } else { + Gtk::TreeModel::Children::iterator iter = _tree.get_model()->get_iter(path); + Gtk::TreeModel::Row row = *iter; + + SPItem* item = row[_model->_colObject]; + + if (col == _tree.get_column(COL_VISIBLE - 1)) { + if (overVisible) { + //Toggle visibility + bool newValue = !row[_model->_colVisible]; + if (_tree.get_selection()->is_selected(path)) + { + //If the current row is selected, toggle the visibility + //for all selected items + _tree.get_selection()->selected_foreach_iter(sigc::bind<bool>(sigc::mem_fun(*this, &ObjectsPanel::_setVisibleIter), newValue)); + } + else + { + //If the current row is not selected, toggle just its visibility + row[_model->_colVisible] = newValue; + item->setHidden(!newValue); + item->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } + DocumentUndo::done( _desktop->doc() , SP_VERB_DIALOG_OBJECTS, + newValue? _("Unhide objects") : _("Hide objects")); + overVisible = false; + } + } else if (col == _tree.get_column(COL_LOCKED - 1)) { + //Toggle locking + bool newValue = !row[_model->_colLocked]; + if (_tree.get_selection()->is_selected(path)) + { + //If the current row is selected, toggle the sensitivity for + //all selected items + _tree.get_selection()->selected_foreach_iter(sigc::bind<bool>(sigc::mem_fun(*this, &ObjectsPanel::_setLockedIter), newValue)); + } + else + { + //If the current row is not selected, toggle just its sensitivity + row[_model->_colLocked] = newValue; + item->setLocked( newValue ); + item->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } + DocumentUndo::done( _desktop->doc() , SP_VERB_DIALOG_OBJECTS, + newValue? _("Lock objects") : _("Unlock objects")); + + } else if (col == _tree.get_column(COL_TYPE - 1)) { + if (SP_IS_GROUP(item)) + { + //Toggle the current item between a group and a layer + SPGroup * g = SP_GROUP(item); + bool newValue = g->layerMode() == SPGroup::LAYER; + row[_model->_colType] = newValue ? 1: 2; + g->setLayerMode(newValue ? SPGroup::GROUP : SPGroup::LAYER); + g->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + DocumentUndo::done( _desktop->doc() , SP_VERB_DIALOG_OBJECTS, + newValue? _("Layer to group") : _("Group to layer")); + } + } /*else if (col == _tree.get_column(COL_INSERTORDER - 1)) { + if (SP_IS_GROUP(item)) + { + //Toggle the current item's insert order + SPGroup * g = SP_GROUP(item); + bool newValue = !g->insertBottom(); + row[_model->_colInsertOrder] = newValue ? 2: 1; + g->setInsertBottom(newValue); + g->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + DocumentUndo::done( _desktop->doc() , SP_VERB_DIALOG_OBJECTS, + newValue? _("Set insert mode bottom") : _("Set insert mode top")); + } + }*/ else if (col == _tree.get_column(COL_HIGHLIGHT - 1)) { + //Clear the highlight targets + _highlight_target.clear(); + if (_tree.get_selection()->is_selected(path)) + { + //If the current item is selected, store all selected items + //in the highlight source + _tree.get_selection()->selected_foreach_iter(sigc::mem_fun(*this, &ObjectsPanel::_storeHighlightTarget)); + } else { + //If the current item is not selected, store only it in the highlight source + _storeHighlightTarget(iter); + } + if (_colorSelector) + { + //Set up the color selector + SPColor color; + color.set( row[_model->_colHighlight] ); + _colorSelector->base->setColorAlpha(color, SP_RGBA32_A_F(row[_model->_colHighlight])); + } + //Show the color selector dialog + _colorSelectorDialog.show(); + } + } + } + } + } + + //Second mouse button press, set double click status for when the mouse is released + if ( (event->type == GDK_2BUTTON_PRESS) && (event->button == 1) ) { + doubleclick = 1; + } + + //Double click on mouse button release, if we're over the label column, edit + //the item name + if ( event->type == GDK_BUTTON_RELEASE && doubleclick) { + doubleclick = 0; + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col = 0; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + int x2 = 0; + int y2 = 0; + if ( _tree.get_path_at_pos( x, y, path, col, x2, y2 ) && col == _name_column) { + // Double click on the Layer name, enable editing + _text_renderer->property_editable() = true; + _tree.set_cursor (path, *_name_column, true); + grab_focus(); + } + } + + return false; +} + +/** + * Stores items in the highlight target vector to manipulate with the color selector + * @param iter Current tree item to store + */ +void ObjectsPanel::_storeHighlightTarget(const Gtk::TreeModel::iterator& iter) +{ + Gtk::TreeModel::Row row = *iter; + SPItem* item = row[_model->_colObject]; + if (item) + { + _highlight_target.push_back(item); + } +} + +/* + * Drap and drop within the tree + */ +bool ObjectsPanel::_handleDragDrop(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time) +{ + int cell_x = 0, cell_y = 0; + Gtk::TreeModel::Path target_path; + Gtk::TreeView::Column *target_column; + + //Set up our defaults and clear the source vector + _dnd_into = false; + _dnd_target = NULL; + _dnd_source.clear(); + + //Add all selected items to the source vector + _tree.get_selection()->selected_foreach_iter(sigc::mem_fun(*this, &ObjectsPanel::_storeDragSource)); + + if (_tree.get_path_at_pos (x, y, target_path, target_column, cell_x, cell_y)) { + // Are we before, inside or after the drop layer + Gdk::Rectangle rect; + _tree.get_background_area (target_path, *target_column, rect); + int cell_height = rect.get_height(); + _dnd_into = (cell_y > (int)(cell_height * 1/4) && cell_y <= (int)(cell_height * 3/4)); + if (cell_y > (int)(cell_height * 3/4)) { + Gtk::TreeModel::Path next_path = target_path; + next_path.next(); + if (_store->iter_is_valid(_store->get_iter(next_path))) { + target_path = next_path; + } else { + // Dragging to the "end" + Gtk::TreeModel::Path up_path = target_path; + up_path.up(); + if (_store->iter_is_valid(_store->get_iter(up_path))) { + // Drop into parent + target_path = up_path; + _dnd_into = true; + } else { + // Drop into the top level + _dnd_target = NULL; + } + } + } + Gtk::TreeModel::iterator iter = _store->get_iter(target_path); + if (_store->iter_is_valid(iter)) { + Gtk::TreeModel::Row row = *iter; + //Set the drop target. If we're not dropping into a group, we cannot + //drop into it, so set _dnd_into false. + _dnd_target = row[_model->_colObject]; + if (!(SP_IS_GROUP(_dnd_target))) _dnd_into = false; + } + } + + _takeAction(DRAGNDROP); + + return false; +} + +/** + * Stores all selected items as the drag source + * @param iter Current tree item + */ +void ObjectsPanel::_storeDragSource(const Gtk::TreeModel::iterator& iter) +{ + Gtk::TreeModel::Row row = *iter; + SPItem* item = row[_model->_colObject]; + if (item) + { + _dnd_source.push_back(item); + } +} + +/* + * Move a layer in response to a drag & drop action + */ +void ObjectsPanel::_doTreeMove( ) +{ + g_assert(_desktop != NULL); + g_assert(_document != NULL); + + std::vector<gchar *> idvector; + + //Clear the desktop selection + _desktop->selection->clear(); + while (!_dnd_source.empty()) + { + SPItem *obj = _dnd_source.back(); + _dnd_source.pop_back(); + + if (obj != _dnd_target) { + //Store the object id (for selection later) and move the object + idvector.push_back(g_strdup(obj->getId())); + obj->moveTo(_dnd_target, _dnd_into); + } + } + + //Select items + while (!idvector.empty()) { + //Grab the id from the vector, get the item in the document and select it + gchar * id = idvector.back(); + idvector.pop_back(); + SPObject *obj = _document->getObjectById(id); + g_free(id); + if (obj && SP_IS_ITEM(obj)) { + SPItem *item = SP_ITEM(obj); + if (!SP_IS_GROUP(item) || SP_GROUP(item)->layerMode() != SPGroup::LAYER) + { + if (_desktop->selection->isEmpty()) _desktop->setCurrentLayer(item->parent); + _desktop->selection->add(item); + } + else + { + if (_desktop->selection->isEmpty()) _desktop->setCurrentLayer(item); + } + } + } + + DocumentUndo::done( _desktop->doc() , SP_VERB_NONE, + _("Moved objects")); +} + +/** + * Fires the action verb + */ +void ObjectsPanel::_fireAction( unsigned int code ) +{ + if ( _desktop ) { + Verb *verb = Verb::get( code ); + if ( verb ) { + SPAction *action = verb->get_action(_desktop); + if ( action ) { + sp_action_perform( action, NULL ); + } + } + } +} + +/** + * Executes the given button action during the idle time + */ +void ObjectsPanel::_takeAction( int val ) +{ + if ( !_pending ) { + _pending = new InternalUIBounce(); + _pending->_actionCode = val; + Glib::signal_timeout().connect( sigc::mem_fun(*this, &ObjectsPanel::_executeAction), 0 ); + } +} + +/** + * Executes the pending button action + */ +bool ObjectsPanel::_executeAction() +{ + // Make sure selected layer hasn't changed since the action was triggered + if ( _document && _pending) + { + int val = _pending->_actionCode; +// SPObject* target = _pending->_target; + + switch ( val ) { + case BUTTON_NEW: + { + _fireAction( SP_VERB_LAYER_NEW ); + } + break; + case BUTTON_RENAME: + { + _fireAction( SP_VERB_LAYER_RENAME ); + } + break; + case BUTTON_TOP: + { + if (_desktop->selection->isEmpty()) + { + _fireAction( SP_VERB_LAYER_TO_TOP ); + } + else + { + _fireAction( SP_VERB_SELECTION_TO_FRONT); + } + } + break; + case BUTTON_BOTTOM: + { + if (_desktop->selection->isEmpty()) + { + _fireAction( SP_VERB_LAYER_TO_BOTTOM ); + } + else + { + _fireAction( SP_VERB_SELECTION_TO_BACK); + } + } + break; + case BUTTON_UP: + { + if (_desktop->selection->isEmpty()) + { + _fireAction( SP_VERB_LAYER_RAISE ); + } + else + { + _fireAction( SP_VERB_SELECTION_RAISE ); + } + } + break; + case BUTTON_DOWN: + { + if (_desktop->selection->isEmpty()) + { + _fireAction( SP_VERB_LAYER_LOWER ); + } + else + { + _fireAction( SP_VERB_SELECTION_LOWER ); + } + } + break; + case BUTTON_DUPLICATE: + { + if (_desktop->selection->isEmpty()) + { + _fireAction( SP_VERB_LAYER_DUPLICATE ); + } + else + { + _fireAction( SP_VERB_EDIT_DUPLICATE ); + } + } + break; + case BUTTON_DELETE: + { + if (_desktop->selection->isEmpty()) + { + _fireAction( SP_VERB_LAYER_DELETE ); + } + else + { + _fireAction( SP_VERB_EDIT_DELETE ); + } + } + break; + case BUTTON_SOLO: + { + _fireAction( SP_VERB_LAYER_SOLO ); + } + break; + case BUTTON_SHOW_ALL: + { + _fireAction( SP_VERB_LAYER_SHOW_ALL ); + } + break; + case BUTTON_HIDE_ALL: + { + _fireAction( SP_VERB_LAYER_HIDE_ALL ); + } + break; + case BUTTON_LOCK_OTHERS: + { + _fireAction( SP_VERB_LAYER_LOCK_OTHERS ); + } + break; + case BUTTON_LOCK_ALL: + { + _fireAction( SP_VERB_LAYER_LOCK_ALL ); + } + break; + case BUTTON_UNLOCK_ALL: + { + _fireAction( SP_VERB_LAYER_UNLOCK_ALL ); + } + break; + case BUTTON_CLIPGROUP: + { + _fireAction ( SP_VERB_OBJECT_CREATE_CLIP_GROUP ); + } + case BUTTON_SETCLIP: + { + _fireAction( SP_VERB_OBJECT_SET_CLIPPATH ); + } + break; + case BUTTON_UNSETCLIP: + { + _fireAction( SP_VERB_OBJECT_UNSET_CLIPPATH ); + } + break; + case BUTTON_SETMASK: + { + _fireAction( SP_VERB_OBJECT_SET_MASK ); + } + break; + case BUTTON_UNSETMASK: + { + _fireAction( SP_VERB_OBJECT_UNSET_MASK ); + } + break; + case BUTTON_GROUP: + { + _fireAction( SP_VERB_SELECTION_GROUP ); + } + break; + case BUTTON_UNGROUP: + { + _fireAction( SP_VERB_SELECTION_UNGROUP ); + } + break; + case BUTTON_COLLAPSE_ALL: + { + for (SPObject* obj = _document->getRoot()->firstChild(); obj != NULL; obj = obj->next) { + if (SP_IS_GROUP(obj)) { + _setCollapsed(SP_GROUP(obj)); + } + } + _objectsChanged(_document->getRoot()); + } + break; + case DRAGNDROP: + { + _doTreeMove( ); + } + break; + } + + delete _pending; + _pending = 0; + } + + return false; +} + +/** + * Handles an unsuccessful item label edit (escape pressed, etc.) + */ +void ObjectsPanel::_handleEditingCancelled() +{ + _text_renderer->property_editable() = false; +} + +/** + * Handle a successful item label edit + * @param path Tree path of the item currently being edited + * @param new_text New label text + */ +void ObjectsPanel::_handleEdited(const Glib::ustring& path, const Glib::ustring& new_text) +{ + Gtk::TreeModel::iterator iter = _tree.get_model()->get_iter(path); + Gtk::TreeModel::Row row = *iter; + + _renameObject(row, new_text); + _text_renderer->property_editable() = false; +} + +/** + * Renames an item in the tree + * @param row Tree row + * @param name New label to give to the item + */ +void ObjectsPanel::_renameObject(Gtk::TreeModel::Row row, const Glib::ustring& name) +{ + if ( row && _desktop) { + SPItem* item = row[_model->_colObject]; + if ( item ) { + gchar const* oldLabel = item->label(); + if ( !name.empty() && (!oldLabel || name != oldLabel) ) { + item->setLabel(name.c_str()); + DocumentUndo::done( _desktop->doc() , SP_VERB_NONE, + _("Rename object")); + } + } + } +} + +/** + * A row selection function used by the tree that doesn't allow any new items to be selected. + * Currently, this is used to allow multi-item drag & drop. + */ +bool ObjectsPanel::_noSelection( Glib::RefPtr<Gtk::TreeModel> const & /*model*/, Gtk::TreeModel::Path const & /*path*/, bool /*currentlySelected*/ ) +{ + return false; +} + +/** + * Default row selection function taken from the layers dialog + */ +bool ObjectsPanel::_rowSelectFunction( Glib::RefPtr<Gtk::TreeModel> const & /*model*/, Gtk::TreeModel::Path const & /*path*/, bool currentlySelected ) +{ + bool val = true; + if ( !currentlySelected && _toggleEvent ) + { + GdkEvent* event = gtk_get_current_event(); + if ( event ) { + // (keep these checks separate, so we know when to call gdk_event_free() + if ( event->type == GDK_BUTTON_PRESS ) { + GdkEventButton const* target = reinterpret_cast<GdkEventButton const*>(_toggleEvent); + GdkEventButton const* evtb = reinterpret_cast<GdkEventButton const*>(event); + + if ( (evtb->window == target->window) + && (evtb->send_event == target->send_event) + && (evtb->time == target->time) + && (evtb->state == target->state) + ) + { + // Ooooh! It's a magic one + val = false; + } + } + gdk_event_free(event); + } + } + return val; +} + +/** + * Sets a group to be collapsed and recursively collapses its children + * @param group The group to collapse + */ +void ObjectsPanel::_setCollapsed(SPGroup * group) +{ + group->setExpanded(false); + group->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + for (SPObject *iter = group->children; iter != NULL; iter = iter->next) + { + if (SP_IS_GROUP(iter)) _setCollapsed(SP_GROUP(iter)); + } +} + +/** + * Sets a group to be expanded or collapsed + * @param iter Current tree item + * @param isexpanded Whether to expand or collapse + */ +void ObjectsPanel::_setExpanded(const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& /*path*/, bool isexpanded) +{ + Gtk::TreeModel::Row row = *iter; + + SPItem* item = row[_model->_colObject]; + if (item && SP_IS_GROUP(item)) + { + if (isexpanded) + { + //If we're expanding, simply perform the expansion + SP_GROUP(item)->setExpanded(isexpanded); + item->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } + else + { + //If we're collapsing, we need to recursively collapse, so call our helper function + _setCollapsed(SP_GROUP(item)); + } + } +} + +/** + * Callback for when the highlight color is changed + * @param csel Color selector + * @param cp Objects panel + */ +void sp_highlight_picker_color_mod(SPColorSelector *csel, GObject * cp) +{ + SPColor color; + float alpha = 0; + csel->base->getColorAlpha(color, alpha); + guint32 rgba = color.toRGBA32( alpha ); + + ObjectsPanel *ptr = reinterpret_cast<ObjectsPanel *>(cp); + + //Set the highlight color for all items in the _highlight_target (all selected items) + for (std::vector<SPItem *>::iterator iter = ptr->_highlight_target.begin(); iter != ptr->_highlight_target.end(); ++iter) + { + SPItem * target = *iter; + target->setHighlightColor(rgba); + target->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } + DocumentUndo::maybeDone(SP_ACTIVE_DOCUMENT, "highlight", SP_VERB_DIALOG_OBJECTS, _("Set object highlight color")); +} + +/** + * Callback for when the opacity value is changed + */ +void ObjectsPanel::_opacityValueChanged() +{ + _blockCompositeUpdate = true; + _tree.get_selection()->selected_foreach_iter(sigc::mem_fun(*this, &ObjectsPanel::_opacityChangedIter)); + DocumentUndo::maybeDone(_document, "opacity", SP_VERB_DIALOG_OBJECTS, _("Set object opacity")); + _blockCompositeUpdate = false; +} + +/** + * Change the opacity of the selected items in the tree + * @param iter Current tree item + */ +void ObjectsPanel::_opacityChangedIter(const Gtk::TreeIter& iter) +{ + Gtk::TreeModel::Row row = *iter; + SPItem* item = row[_model->_colObject]; + if (item) + { + item->style->opacity.set = TRUE; +#if WITH_GTKMM_3_0 + item->style->opacity.value = SP_SCALE24_FROM_FLOAT(_opacity_adjustment->get_value() / _opacity_adjustment->get_upper()); +#else + item->style->opacity.value = SP_SCALE24_FROM_FLOAT(_opacity_adjustment.get_value() / _opacity_adjustment.get_upper()); +#endif + item->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } +} + +/** + * Callback for when the blend mode is changed + */ +void ObjectsPanel::_blendValueChanged() +{ + _blockCompositeUpdate = true; + const Glib::ustring blendmode = _fe_cb.get_blend_mode(); + + _tree.get_selection()->selected_foreach_iter(sigc::bind<Glib::ustring>(sigc::mem_fun(*this, &ObjectsPanel::_blendChangedIter), blendmode)); + DocumentUndo::done(_document, SP_VERB_DIALOG_OBJECTS, _("Set object blend mode")); + _blockCompositeUpdate = false; +} + +/** + * Sets the blend mode of the selected tree items + * @param iter Current tree item + * @param blendmode Blend mode to set + */ +void ObjectsPanel::_blendChangedIter(const Gtk::TreeIter& iter, Glib::ustring blendmode) +{ + Gtk::TreeModel::Row row = *iter; + SPItem* item = row[_model->_colObject]; + if (item) + { + //Since blur and blend are both filters, we need to set both at the same time + SPStyle *style = item->style; + g_assert(style != NULL); + + if (blendmode != "normal") { + gdouble radius = 0; + if (item->style->getFilter()) { + for (SPObject *primitive = item->style->getFilter()->children; primitive && SP_IS_FILTER_PRIMITIVE(primitive); primitive = primitive->next) { + if (SP_IS_GAUSSIANBLUR(primitive)) { + Geom::OptRect bbox = item->bounds(SPItem::GEOMETRIC_BBOX); + if (bbox) { + radius = SP_GAUSSIANBLUR(primitive)->stdDeviation.getNumber(); + } + } + } + } + SPFilter *filter = new_filter_simple_from_item(_document, item, blendmode.c_str(), radius); + sp_style_set_property_url(item, "filter", filter, false); + } else { + for (SPObject *primitive = item->style->getFilter()->children; primitive && SP_IS_FILTER_PRIMITIVE(primitive); primitive = primitive->next) { + if (SP_IS_FEBLEND(primitive)) { + primitive->deleteObject(); + break; + } + } + if (!item->style->getFilter()->children) { + remove_filter(item, false); + } + } + + item->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } +} + +/** + * Callback for when the blur value has changed + */ +void ObjectsPanel::_blurValueChanged() +{ + _blockCompositeUpdate = true; + _tree.get_selection()->selected_foreach_iter(sigc::bind<double>(sigc::mem_fun(*this, &ObjectsPanel::_blurChangedIter), _fe_blur.get_blur_value())); + DocumentUndo::maybeDone(_document, "blur", SP_VERB_DIALOG_OBJECTS, _("Set object blur")); + _blockCompositeUpdate = false; +} + +/** + * Sets the blur value for the selected items in the tree + * @param iter Current tree item + * @param blur Blur value to set + */ +void ObjectsPanel::_blurChangedIter(const Gtk::TreeIter& iter, double blur) +{ + Gtk::TreeModel::Row row = *iter; + SPItem* item = row[_model->_colObject]; + if (item) + { + //Since blur and blend are both filters, we need to set both at the same time + SPStyle *style = item->style; + if (style) { + Geom::OptRect bbox = item->bounds(SPItem::GEOMETRIC_BBOX); + double radius; + if (bbox) { + double perimeter = bbox->dimensions()[Geom::X] + bbox->dimensions()[Geom::Y]; // fixme: this is only half the perimeter, is that correct? + radius = blur * perimeter / 400; + } else { + radius = 0; + } + + if (radius != 0) { + SPFilter *filter = modify_filter_gaussian_blur_from_item(_document, item, radius); + sp_style_set_property_url(item, "filter", filter, false); + } else if (item->style->filter.set && item->style->getFilter()) { + for (SPObject *primitive = item->style->getFilter()->children; primitive && SP_IS_FILTER_PRIMITIVE(primitive); primitive = primitive->next) { + if (SP_IS_GAUSSIANBLUR(primitive)) { + primitive->deleteObject(); + break; + } + } + if (!item->style->getFilter()->children) { + remove_filter(item, false); + } + } + item->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } + } +} + +/** + * Constructor + */ +ObjectsPanel::ObjectsPanel() : + UI::Widget::Panel("", "/dialogs/objects", SP_VERB_DIALOG_OBJECTS), + _rootWatcher(0), + _deskTrack(), + _desktop(0), + _document(0), + _model(0), + _pending(0), + _toggleEvent(0), + _defer_target(), + _composite_vbox(false, 0), + _opacity_vbox(false, 0), + _opacity_label(_("Opacity:")), + _opacity_label_unit(_("%")), +#if WITH_GTKMM_3_0 + _opacity_adjustment(Gtk::Adjustment::create(100.0, 0.0, 100.0, 1.0, 1.0, 0.0)), +#else + _opacity_adjustment(100.0, 0.0, 100.0, 1.0, 1.0, 0.0), +#endif + _opacity_hscale(_opacity_adjustment), + _opacity_spin_button(_opacity_adjustment, 0.01, 1), + _fe_cb(UI::Widget::SimpleFilterModifier::BLEND), + _fe_vbox(false, 0), + _fe_alignment(1, 1, 1, 1), + _fe_blur(UI::Widget::SimpleFilterModifier::BLUR), + _blur_vbox(false, 0), + _blur_alignment(1, 1, 1, 1), + _colorSelectorDialog("dialogs.colorpickerwindow") +{ + //Create the tree model and store + ModelColumns *zoop = new ModelColumns(); + _model = zoop; + + _store = Gtk::TreeStore::create( *zoop ); + + //Set up the tree + _tree.set_model( _store ); + _tree.set_headers_visible(false); + _tree.set_reorderable(true); + _tree.enable_model_drag_dest (Gdk::ACTION_MOVE); + + //Create the column CellRenderers + //Visible + Inkscape::UI::Widget::ImageToggler *eyeRenderer = Gtk::manage( new Inkscape::UI::Widget::ImageToggler( + INKSCAPE_ICON("object-visible"), INKSCAPE_ICON("object-hidden")) ); + int visibleColNum = _tree.append_column("vis", *eyeRenderer) - 1; + eyeRenderer->property_activatable() = true; + Gtk::TreeViewColumn* col = _tree.get_column(visibleColNum); + if ( col ) { + col->add_attribute( eyeRenderer->property_active(), _model->_colVisible ); + } + + //Locked + Inkscape::UI::Widget::ImageToggler * renderer = Gtk::manage( new Inkscape::UI::Widget::ImageToggler( + INKSCAPE_ICON("object-locked"), INKSCAPE_ICON("object-unlocked")) ); + int lockedColNum = _tree.append_column("lock", *renderer) - 1; + renderer->property_activatable() = true; + col = _tree.get_column(lockedColNum); + if ( col ) { + col->add_attribute( renderer->property_active(), _model->_colLocked ); + } + + //Type + Inkscape::UI::Widget::LayerTypeIcon * typeRenderer = Gtk::manage( new Inkscape::UI::Widget::LayerTypeIcon()); + int typeColNum = _tree.append_column("type", *typeRenderer) - 1; + typeRenderer->property_activatable() = true; + col = _tree.get_column(typeColNum); + if ( col ) { + col->add_attribute( typeRenderer->property_active(), _model->_colType ); + } + + //Insert order (LiamW: unused) + /*Inkscape::UI::Widget::InsertOrderIcon * insertRenderer = Gtk::manage( new Inkscape::UI::Widget::InsertOrderIcon()); + int insertColNum = _tree.append_column("type", *insertRenderer) - 1; + col = _tree.get_column(insertColNum); + if ( col ) { + col->add_attribute( insertRenderer->property_active(), _model->_colInsertOrder ); + }*/ + + //Clip/mask + Inkscape::UI::Widget::ClipMaskIcon * clipRenderer = Gtk::manage( new Inkscape::UI::Widget::ClipMaskIcon()); + int clipColNum = _tree.append_column("clipmask", *clipRenderer) - 1; + col = _tree.get_column(clipColNum); + if ( col ) { + col->add_attribute( clipRenderer->property_active(), _model->_colClipMask ); + } + + //Highlight + Inkscape::UI::Widget::HighlightPicker * highlightRenderer = Gtk::manage( new Inkscape::UI::Widget::HighlightPicker()); + int highlightColNum = _tree.append_column("highlight", *highlightRenderer) - 1; + col = _tree.get_column(highlightColNum); + if ( col ) { + col->add_attribute( highlightRenderer->property_active(), _model->_colHighlight ); + } + + //Label + _text_renderer = Gtk::manage(new Gtk::CellRendererText()); + int nameColNum = _tree.append_column("Name", *_text_renderer) - 1; + _name_column = _tree.get_column(nameColNum); + _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel); + + //Set the expander and search columns + _tree.set_expander_column( *_tree.get_column(nameColNum) ); + _tree.set_search_column(_model->_colLabel); + + //Set up the tree selection + _tree.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); + _selectedConnection = _tree.get_selection()->signal_changed().connect( sigc::mem_fun(*this, &ObjectsPanel::_pushTreeSelectionToCurrent) ); + _tree.get_selection()->set_select_function( sigc::mem_fun(*this, &ObjectsPanel::_rowSelectFunction) ); + + //Set up tree signals + _tree.signal_button_press_event().connect( sigc::mem_fun(*this, &ObjectsPanel::_handleButtonEvent), false ); + _tree.signal_button_release_event().connect( sigc::mem_fun(*this, &ObjectsPanel::_handleButtonEvent), false ); + _tree.signal_key_press_event().connect( sigc::mem_fun(*this, &ObjectsPanel::_handleKeyEvent), false ); + _tree.signal_drag_drop().connect( sigc::mem_fun(*this, &ObjectsPanel::_handleDragDrop), false); + _tree.signal_row_collapsed().connect( sigc::bind<bool>(sigc::mem_fun(*this, &ObjectsPanel::_setExpanded), false)); + _tree.signal_row_expanded().connect( sigc::bind<bool>(sigc::mem_fun(*this, &ObjectsPanel::_setExpanded), true)); + + //Set up the label editing signals + _text_renderer->signal_edited().connect( sigc::mem_fun(*this, &ObjectsPanel::_handleEdited) ); + _text_renderer->signal_editing_canceled().connect( sigc::mem_fun(*this, &ObjectsPanel::_handleEditingCancelled) ); + + //Set up the scroller window and pack the page + _scroller.add( _tree ); + _scroller.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC ); + _scroller.set_shadow_type(Gtk::SHADOW_IN); + Gtk::Requisition sreq; +#if WITH_GTKMM_3_0 + Gtk::Requisition sreq_natural; + _scroller.get_preferred_size(sreq_natural, sreq); +#else + sreq = _scroller.size_request(); +#endif + int minHeight = 70; + if (sreq.height < minHeight) { + // Set a min height to see the layers when used with Ubuntu liboverlay-scrollbar + _scroller.set_size_request(sreq.width, minHeight); + } + + _page.pack_start( _scroller, Gtk::PACK_EXPAND_WIDGET ); + + //Set up the compositing items + //Blend mode filter effect + _composite_vbox.pack_start(_fe_vbox, false, false, 2); + _fe_alignment.set_padding(0, 0, 4, 0); + _fe_alignment.add(_fe_cb); + _fe_vbox.pack_start(_fe_alignment, false, false, 0); + _blendConnection = _fe_cb.signal_blend_blur_changed().connect(sigc::mem_fun(*this, &ObjectsPanel::_blendValueChanged)); + + //Blur filter effect + _composite_vbox.pack_start(_blur_vbox, false, false, 2); + _blur_alignment.set_padding(0, 0, 4, 0); + _blur_alignment.add(_fe_blur); + _blur_vbox.pack_start(_blur_alignment, false, false, 0); + _blurConnection = _fe_blur.signal_blend_blur_changed().connect(sigc::mem_fun(*this, &ObjectsPanel::_blurValueChanged)); + + //Opacity + _composite_vbox.pack_start(_opacity_vbox, false, false, 2); + _opacity_label.set_alignment(Gtk::ALIGN_END, Gtk::ALIGN_CENTER); + _opacity_hbox.pack_start(_opacity_label, false, false, 3); + _opacity_vbox.pack_start(_opacity_hbox, false, false, 0); + _opacity_hbox.pack_start(_opacity_hscale, true, true, 0); + _opacity_hbox.pack_start(_opacity_spin_button, false, false, 0); + _opacity_hbox.pack_start(_opacity_label_unit, false, false, 3); + _opacity_hscale.set_draw_value(false); +#if WITH_GTKMM_3_0 + _opacityConnection = _opacity_adjustment->signal_value_changed().connect(sigc::mem_fun(*this, &ObjectsPanel::_opacityValueChanged)); + _opacity_label.set_mnemonic_widget(_opacity_hscale); +#else + _opacityConnection = _opacity_adjustment.signal_value_changed().connect(sigc::mem_fun(*this, &ObjectsPanel::_opacityValueChanged)); + _opacity_label.set_mnemonic_widget(_opacity_hscale); +#endif + + //Keep the labels aligned + GtkSizeGroup *labels = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget(labels, GTK_WIDGET(_opacity_label.gobj())); + gtk_size_group_add_widget(labels, GTK_WIDGET(_fe_cb.get_blur_label()->gobj())); + gtk_size_group_add_widget(labels, GTK_WIDGET(_fe_blur.get_blur_label()->gobj())); + + //Pack the compositing functions and the button row + _page.pack_end(_composite_vbox, Gtk::PACK_SHRINK); + _page.pack_end(_buttonsRow, Gtk::PACK_SHRINK); + + //Pack into the panel contents + _getContents()->pack_start(_page, Gtk::PACK_EXPAND_WIDGET); + + SPDesktop* targetDesktop = getDesktop(); + + //Set up the button row + + + //Add object/layer + Gtk::Button* btn = Gtk::manage( new Gtk::Button() ); + btn->set_tooltip_text(_("Add layer...")); +#if GTK_CHECK_VERSION(3,10,0) + btn->set_image_from_icon_name(INKSCAPE_ICON("list-add"), Gtk::ICON_SIZE_SMALL_TOOLBAR); +#else + Gtk::Image *image_add = Gtk::manage(new Gtk::Image()); + image_add->set_from_icon_name(INKSCAPE_ICON("list-add"), Gtk::ICON_SIZE_SMALL_TOOLBAR); + btn->set_image(*image_add); +#endif + btn->set_relief(Gtk::RELIEF_NONE); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &ObjectsPanel::_takeAction), (int)BUTTON_NEW) ); + _buttonsSecondary.pack_start(*btn, Gtk::PACK_SHRINK); + + + //Remove object + btn = Gtk::manage( new Gtk::Button() ); + btn->set_tooltip_text(_("Remove object")); +#if GTK_CHECK_VERSION(3,10,0) + btn->set_image_from_icon_name(INKSCAPE_ICON("list-remove"), Gtk::ICON_SIZE_SMALL_TOOLBAR); +#else + Gtk::Image *image_remove = Gtk::manage(new Gtk::Image()); + image_remove->set_from_icon_name(INKSCAPE_ICON("list-remove"), Gtk::ICON_SIZE_SMALL_TOOLBAR); + btn->set_image(*image_remove); +#endif + btn->set_relief(Gtk::RELIEF_NONE); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &ObjectsPanel::_takeAction), (int)BUTTON_DELETE) ); + _watching.push_back( btn ); + _buttonsSecondary.pack_start(*btn, Gtk::PACK_SHRINK); + + //Move to bottom + btn = Gtk::manage( new Gtk::Button() ); + btn->set_tooltip_text(_("Move To Bottom")); +#if GTK_CHECK_VERSION(3,10,0) + btn->set_image_from_icon_name(INKSCAPE_ICON("go-bottom"), Gtk::ICON_SIZE_SMALL_TOOLBAR); +#else + image_remove = Gtk::manage(new Gtk::Image()); + image_remove->set_from_icon_name(INKSCAPE_ICON("go-bottom"), Gtk::ICON_SIZE_SMALL_TOOLBAR); + btn->set_image(*image_remove); +#endif + btn->set_relief(Gtk::RELIEF_NONE); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &ObjectsPanel::_takeAction), (int)BUTTON_BOTTOM) ); + _watchingNonBottom.push_back( btn ); + _buttonsPrimary.pack_end(*btn, Gtk::PACK_SHRINK); + + //Move down + btn = Gtk::manage( new Gtk::Button() ); + btn->set_tooltip_text(_("Move Down")); +#if GTK_CHECK_VERSION(3,10,0) + btn->set_image_from_icon_name(INKSCAPE_ICON("go-down"), Gtk::ICON_SIZE_SMALL_TOOLBAR); +#else + image_remove = Gtk::manage(new Gtk::Image()); + image_remove->set_from_icon_name(INKSCAPE_ICON("go-down"), Gtk::ICON_SIZE_SMALL_TOOLBAR); + btn->set_image(*image_remove); +#endif + btn->set_relief(Gtk::RELIEF_NONE); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &ObjectsPanel::_takeAction), (int)BUTTON_DOWN) ); + _watchingNonBottom.push_back( btn ); + _buttonsPrimary.pack_end(*btn, Gtk::PACK_SHRINK); + + //Move up + btn = Gtk::manage( new Gtk::Button() ); + btn->set_tooltip_text(_("Move Up")); +#if GTK_CHECK_VERSION(3,10,0) + btn->set_image_from_icon_name(INKSCAPE_ICON("go-up"), Gtk::ICON_SIZE_SMALL_TOOLBAR); +#else + image_remove = Gtk::manage(new Gtk::Image()); + image_remove->set_from_icon_name(INKSCAPE_ICON("go-up"), Gtk::ICON_SIZE_SMALL_TOOLBAR); + btn->set_image(*image_remove); +#endif + btn->set_relief(Gtk::RELIEF_NONE); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &ObjectsPanel::_takeAction), (int)BUTTON_UP) ); + _watchingNonTop.push_back( btn ); + _buttonsPrimary.pack_end(*btn, Gtk::PACK_SHRINK); + + //Move to top + btn = Gtk::manage( new Gtk::Button() ); + btn->set_tooltip_text(_("Move To Top")); +#if GTK_CHECK_VERSION(3,10,0) + btn->set_image_from_icon_name(INKSCAPE_ICON("go-top"), Gtk::ICON_SIZE_SMALL_TOOLBAR); +#else + image_remove = Gtk::manage(new Gtk::Image()); + image_remove->set_from_icon_name(INKSCAPE_ICON("go-top"), Gtk::ICON_SIZE_SMALL_TOOLBAR); + btn->set_image(*image_remove); +#endif + btn->set_relief(Gtk::RELIEF_NONE); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &ObjectsPanel::_takeAction), (int)BUTTON_TOP) ); + _watchingNonTop.push_back( btn ); + _buttonsPrimary.pack_end(*btn, Gtk::PACK_SHRINK); + + //Collapse all + btn = Gtk::manage( new Gtk::Button() ); + btn->set_tooltip_text(_("Collapse All")); +#if GTK_CHECK_VERSION(3,10,0) + btn->set_image_from_icon_name(INKSCAPE_ICON("gtk-unindent-ltr"), Gtk::ICON_SIZE_SMALL_TOOLBAR); +#else + image_remove = Gtk::manage(new Gtk::Image()); + image_remove->set_from_icon_name(INKSCAPE_ICON("gtk-unindent-ltr"), Gtk::ICON_SIZE_SMALL_TOOLBAR); + btn->set_image(*image_remove); +#endif + btn->set_relief(Gtk::RELIEF_NONE); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &ObjectsPanel::_takeAction), (int)BUTTON_COLLAPSE_ALL) ); + _watchingNonBottom.push_back( btn ); + _buttonsPrimary.pack_end(*btn, Gtk::PACK_SHRINK); + + _buttonsRow.pack_start(_buttonsSecondary, Gtk::PACK_EXPAND_WIDGET); + _buttonsRow.pack_end(_buttonsPrimary, Gtk::PACK_EXPAND_WIDGET); + + _watching.push_back(&_composite_vbox); + + //Set up the pop-up menu + // ------------------------------------------------------- + { + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RENAME, 0, "Rename", (int)BUTTON_RENAME ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_EDIT_DUPLICATE, 0, "Duplicate", (int)BUTTON_DUPLICATE ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_NEW, 0, "New", (int)BUTTON_NEW ) ); + + _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); + + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_SOLO, 0, "Solo", (int)BUTTON_SOLO ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_SHOW_ALL, 0, "Show All", (int)BUTTON_SHOW_ALL ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_HIDE_ALL, 0, "Hide All", (int)BUTTON_HIDE_ALL ) ); + + _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); + + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOCK_OTHERS, 0, "Lock Others", (int)BUTTON_LOCK_OTHERS ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOCK_ALL, 0, "Lock All", (int)BUTTON_LOCK_ALL ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_UNLOCK_ALL, 0, "Unlock All", (int)BUTTON_UNLOCK_ALL ) ); + + _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); + + _watchingNonTop.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_RAISE, GTK_STOCK_GO_UP, "Up", (int)BUTTON_UP ) ); + _watchingNonBottom.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_LOWER, GTK_STOCK_GO_DOWN, "Down", (int)BUTTON_DOWN ) ); + + _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); + + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_GROUP, 0, "Group", (int)BUTTON_GROUP ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_UNGROUP, 0, "Ungroup", (int)BUTTON_UNGROUP ) ); + + _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); + + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_SET_CLIPPATH, 0, "Set Clip", (int)BUTTON_SETCLIP ) ); + + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_CREATE_CLIP_GROUP, 0, "Create Clip Group", (int)BUTTON_CLIPGROUP ) ); + + //will never be implemented + //_watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_SET_INVERSE_CLIPPATH, 0, "Set Inverse Clip", (int)BUTTON_SETINVCLIP ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_UNSET_CLIPPATH, 0, "Unset Clip", (int)BUTTON_UNSETCLIP ) ); + + _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); + + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_SET_MASK, 0, "Set Mask", (int)BUTTON_SETMASK ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_UNSET_MASK, 0, "Unset Mask", (int)BUTTON_UNSETMASK ) ); + + _popupMenu.show_all_children(); + } + // ------------------------------------------------------- + + //Set initial sensitivity of buttons + for ( std::vector<Gtk::Widget*>::iterator it = _watching.begin(); it != _watching.end(); ++it ) { + (*it)->set_sensitive( false ); + } + for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonTop.begin(); it != _watchingNonTop.end(); ++it ) { + (*it)->set_sensitive( false ); + } + for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonBottom.begin(); it != _watchingNonBottom.end(); ++it ) { + (*it)->set_sensitive( false ); + } + + //Set up the color selection dialog + GtkWidget *dlg = GTK_WIDGET(_colorSelectorDialog.gobj()); + sp_transientize(dlg); + + _colorSelectorDialog.hide(); + _colorSelectorDialog.set_title (_("Select Highlight Color")); + _colorSelectorDialog.set_border_width (4); + _colorSelectorDialog.property_modal() = true; + _colorSelector = SP_COLOR_SELECTOR(sp_color_selector_new(SP_TYPE_COLOR_NOTEBOOK)); + _colorSelectorDialog.get_vbox()->pack_start ( + *Glib::wrap(&_colorSelector->vbox), true, true, 0); + + g_signal_connect(G_OBJECT(_colorSelector), "dragged", + G_CALLBACK(sp_highlight_picker_color_mod), (void *)this); + g_signal_connect(G_OBJECT(_colorSelector), "released", + G_CALLBACK(sp_highlight_picker_color_mod), (void *)this); + g_signal_connect(G_OBJECT(_colorSelector), "changed", + G_CALLBACK(sp_highlight_picker_color_mod), (void *)this); + + gtk_widget_show(GTK_WIDGET(_colorSelector)); + + setDesktop( targetDesktop ); + + show_all_children(); + + //Connect the desktop changed connection + desktopChangeConn = _deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &ObjectsPanel::setDesktop) ); + _deskTrack.connect(GTK_WIDGET(gobj())); +} + +/** + * Destructor + */ +ObjectsPanel::~ObjectsPanel() +{ + //Close the highlight selection dialog + _colorSelectorDialog.hide(); + _colorSelector = NULL; + + //Set the desktop to null, which will disconnect all object watchers + setDesktop(NULL); + + if ( _model ) + { + delete _model; + _model = 0; + } + + if (_pending) { + delete _pending; + _pending = 0; + } + + if ( _toggleEvent ) + { + gdk_event_free( _toggleEvent ); + _toggleEvent = 0; + } + + desktopChangeConn.disconnect(); + _deskTrack.disconnect(); +} + +/** + * Sets the current document + */ +void ObjectsPanel::setDocument(SPDesktop* /*desktop*/, SPDocument* document) +{ + //Clear all object watchers + while (!_objectWatchers.empty()) + { + ObjectsPanel::ObjectWatcher *w = _objectWatchers.back(); + w->_repr->removeObserver(*w); + _objectWatchers.pop_back(); + delete w; + } + + //Delete the root watcher + if (_rootWatcher) + { + _rootWatcher->_repr->removeObserver(*_rootWatcher); + delete _rootWatcher; + _rootWatcher = NULL; + } + + _document = document; + + if (document && document->getRoot() && document->getRoot()->getRepr()) + { + //Create a new root watcher for the document and then call _objectsChanged to fill the tree + _rootWatcher = new ObjectsPanel::ObjectWatcher(this, document->getRoot()); + document->getRoot()->getRepr()->addObserver(*_rootWatcher); + _objectsChanged(document->getRoot()); + } +} + +/** + * Set the current panel desktop + */ +void ObjectsPanel::setDesktop( SPDesktop* desktop ) +{ + Panel::setDesktop(desktop); + + if ( desktop != _desktop ) { + _documentChangedConnection.disconnect(); + _selectionChangedConnection.disconnect(); + if ( _desktop ) { + _desktop = 0; + } + + _desktop = Panel::getDesktop(); + if ( _desktop ) { + //Connect desktop signals + _documentChangedConnection = _desktop->connectDocumentReplaced( sigc::mem_fun(*this, &ObjectsPanel::setDocument)); + _selectionChangedConnection = _desktop->selection->connectChanged( sigc::mem_fun(*this, &ObjectsPanel::_objectsSelected)); + + setDocument(_desktop, _desktop->doc()); + } else { + setDocument(NULL, NULL); + } + } + _deskTrack.setBase(desktop); +} +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + +//should be okay to put these here because they are never referenced anywhere else +using namespace Inkscape::UI::Tools; + +void SPItem::setHighlightColor(guint32 const color) +{ + g_free(_highlightColor); + if (color & 0x000000ff) + { + _highlightColor = g_strdup_printf("%u", color); + } + else + { + _highlightColor = NULL; + } + + NodeTool *tool = 0; + if (SP_ACTIVE_DESKTOP ) { + Inkscape::UI::Tools::ToolBase *ec = SP_ACTIVE_DESKTOP->event_context; + if (INK_IS_NODE_TOOL(ec)) { + tool = static_cast<NodeTool*>(ec); + tools_switch(tool->desktop, TOOLS_NODES); + } + } +} + +void SPItem::unsetHighlightColor() +{ + g_free(_highlightColor); + _highlightColor = NULL; + NodeTool *tool = 0; + if (SP_ACTIVE_DESKTOP ) { + Inkscape::UI::Tools::ToolBase *ec = SP_ACTIVE_DESKTOP->event_context; + if (INK_IS_NODE_TOOL(ec)) { + tool = static_cast<NodeTool*>(ec); + tools_switch(tool->desktop, TOOLS_NODES); + } + } +} + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/objects.h b/src/ui/dialog/objects.h new file mode 100644 index 000000000..74c2382ac --- /dev/null +++ b/src/ui/dialog/objects.h @@ -0,0 +1,263 @@ +/* + * A simple dialog for objects UI. + * + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_OBJECTS_PANEL_H +#define SEEN_OBJECTS_PANEL_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H +# include <glibmm/threads.h> +#endif + +#include <gtkmm/box.h> +#include <gtkmm/treeview.h> +#include <gtkmm/treestore.h> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/dialog.h> +#include "ui/widget/spinbutton.h" +#include "ui/widget/panel.h" +#include "ui/widget/object-composite-settings.h" +#include "desktop-tracker.h" +#include "ui/widget/style-subject.h" +#include "selection.h" +#include "ui/widget/filter-effect-chooser.h" + +class SPObject; +class SPGroup; +struct SPColorSelector; + +namespace Inkscape { + +namespace UI { +namespace Dialog { + + +/** + * A panel that displays objects. + */ +class ObjectsPanel : public UI::Widget::Panel +{ +public: + ObjectsPanel(); + virtual ~ObjectsPanel(); + + static ObjectsPanel& getInstance(); + + void setDesktop( SPDesktop* desktop ); + void setDocument( SPDesktop* desktop, SPDocument* document); + +private: + //Internal Classes: + class ModelColumns; + class InternalUIBounce; + class ObjectWatcher; + + //Connections, Watchers, Trackers: + + //Document root watcher + ObjectsPanel::ObjectWatcher* _rootWatcher; + + //All object watchers + std::vector<ObjectsPanel::ObjectWatcher*> _objectWatchers; + + //Connection for when the desktop changes + sigc::connection desktopChangeConn; + + //Connection for when the document changes + sigc::connection _documentChangedConnection; + + //Connection for when the active selection in the document changes + sigc::connection _selectionChangedConnection; + + //Connection for when the selection in the dialog changes + sigc::connection _selectedConnection; + + //Connections for when the opacity/blend/blur of the active selection in the document changes + sigc::connection _opacityConnection; + sigc::connection _blendConnection; + sigc::connection _blurConnection; + + //Desktop tracker for grabbing the desktop changed connection + DesktopTracker _deskTrack; + + //Members: + + //The current desktop + SPDesktop* _desktop; + + //The current document + SPDocument* _document; + + //Tree data model + ModelColumns* _model; + + //Prevents the composite controls from updating + bool _blockCompositeUpdate; + + // + InternalUIBounce* _pending; + + //Whether the drag & drop was dragged into an item + gboolean _dnd_into; + + //List of drag & drop source items + std::vector<SPItem*> _dnd_source; + + //Drag & drop target item + SPItem* _dnd_target; + + //List of items to change the highlight on + std::vector<SPItem*> _highlight_target; + + //GUI Members: + + GdkEvent* _toggleEvent; + + Gtk::TreeModel::Path _defer_target; + + Glib::RefPtr<Gtk::TreeStore> _store; + std::vector<Gtk::Widget*> _watching; + std::vector<Gtk::Widget*> _watchingNonTop; + std::vector<Gtk::Widget*> _watchingNonBottom; + + Gtk::TreeView _tree; + Gtk::CellRendererText *_text_renderer; + Gtk::TreeView::Column *_name_column; +#if WITH_GTKMM_3_0 + Gtk::Box _buttonsRow; + Gtk::Box _buttonsPrimary; + Gtk::Box _buttonsSecondary; +#else + Gtk::HBox _buttonsRow; + Gtk::HBox _buttonsPrimary; + Gtk::HBox _buttonsSecondary; +#endif + Gtk::ScrolledWindow _scroller; + Gtk::Menu _popupMenu; + Inkscape::UI::Widget::SpinButton _spinBtn; + Gtk::VBox _page; + + /* Composite Settings */ + Gtk::VBox _composite_vbox; + Gtk::VBox _opacity_vbox; + Gtk::HBox _opacity_hbox; + Gtk::Label _opacity_label; + Gtk::Label _opacity_label_unit; +#if WITH_GTKMM_3_0 + Glib::RefPtr<Gtk::Adjustment> _opacity_adjustment; +#else + Gtk::Adjustment _opacity_adjustment; +#endif + Gtk::HScale _opacity_hscale; + Inkscape::UI::Widget::SpinButton _opacity_spin_button; + + Inkscape::UI::Widget::SimpleFilterModifier _fe_cb; + Gtk::VBox _fe_vbox; + Gtk::Alignment _fe_alignment; + Inkscape::UI::Widget::SimpleFilterModifier _fe_blur; + Gtk::VBox _blur_vbox; + Gtk::Alignment _blur_alignment; + + Gtk::Dialog _colorSelectorDialog; + SPColorSelector *_colorSelector; + + + //Methods: + + ObjectsPanel(ObjectsPanel const &); // no copy + ObjectsPanel &operator=(ObjectsPanel const &); // no assign + + void _styleButton( Gtk::Button& btn, char const* iconName, char const* tooltip ); + void _fireAction( unsigned int code ); + + Gtk::MenuItem& _addPopupItem( SPDesktop *desktop, unsigned int code, char const* iconName, char const* fallback, int id ); + + void _setVisibleIter( const Gtk::TreeModel::iterator& iter, const bool visible ); + void _setLockedIter( const Gtk::TreeModel::iterator& iter, const bool locked ); + + bool _handleButtonEvent(GdkEventButton *event); + bool _handleKeyEvent(GdkEventKey *event); + + void _storeHighlightTarget(const Gtk::TreeModel::iterator& iter); + void _storeDragSource(const Gtk::TreeModel::iterator& iter); + bool _handleDragDrop(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time); + void _handleEdited(const Glib::ustring& path, const Glib::ustring& new_text); + void _handleEditingCancelled(); + + void _doTreeMove(); + void _renameObject(Gtk::TreeModel::Row row, const Glib::ustring& name); + + void _pushTreeSelectionToCurrent(); + void _selected_row_callback( const Gtk::TreeModel::iterator& iter, bool *setOpacity ); + + void _checkTreeSelection(); + + void _takeAction( int val ); + bool _executeAction(); + + void _setExpanded( const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path, bool isexpanded ); + void _setCollapsed(SPGroup * group); + + bool _noSelection( Glib::RefPtr<Gtk::TreeModel> const & model, Gtk::TreeModel::Path const & path, bool b ); + bool _rowSelectFunction( Glib::RefPtr<Gtk::TreeModel> const & model, Gtk::TreeModel::Path const & path, bool b ); + + void _compositingChanged( const Gtk::TreeModel::iterator& iter, bool *setValues ); + void _updateComposite(); + void _setCompositingValues(SPItem *item); + + void _updateObject(SPObject *obj, bool recurse); + bool _checkForUpdated(const Gtk::TreeIter& iter, SPObject* obj); + + void _objectsSelected(Selection *sel); + bool _checkForSelected(const Gtk::TreePath& path, const Gtk::TreeIter& iter, SPItem* item, bool scrollto); + + void _objectsChanged(SPObject *obj); + void _addObject( SPObject* obj, Gtk::TreeModel::Row* parentRow ); + + void _opacityChangedIter(const Gtk::TreeIter& iter); + void _opacityValueChanged(); + + void _blendChangedIter(const Gtk::TreeIter& iter, Glib::ustring blendmode); + void _blendValueChanged(); + + void _blurChangedIter(const Gtk::TreeIter& iter, double blur); + void _blurValueChanged(); + + + void setupDialog(const Glib::ustring &title); + + friend void sp_highlight_picker_color_mod(SPColorSelector *csel, GObject *cp); + +}; + + + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + + +#endif // SEEN_OBJECTS_PANEL_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/ocaldialogs.cpp b/src/ui/dialog/ocaldialogs.cpp index f676e75fd..c4dd9df98 100644 --- a/src/ui/dialog/ocaldialogs.cpp +++ b/src/ui/dialog/ocaldialogs.cpp @@ -25,9 +25,9 @@ #include "path-prefix.h" #include "filedialogimpl-gtkmm.h" -#include "interface.h" +#include "ui/interface.h" #include "gc-core.h" -#include <dialogs/dialog-events.h> +#include "ui/dialog-events.h" #include "io/sys.h" #include "preferences.h" diff --git a/src/ui/dialog/ocaldialogs.h b/src/ui/dialog/ocaldialogs.h index e21030bcd..bd028c145 100644 --- a/src/ui/dialog/ocaldialogs.h +++ b/src/ui/dialog/ocaldialogs.h @@ -38,8 +38,7 @@ //Inkscape includes #include "ui/dialog/filedialog.h" - -#include <dialogs/dialog-events.h> +#include "ui/dialog-events.h" struct _xmlNode; typedef _xmlNode xmlNode; diff --git a/src/ui/dialog/panel-dialog.h b/src/ui/dialog/panel-dialog.h index 1fefd811e..b4a355083 100644 --- a/src/ui/dialog/panel-dialog.h +++ b/src/ui/dialog/panel-dialog.h @@ -49,19 +49,19 @@ public: virtual UI::Widget::Panel &getPanel() { return _panel; } protected: - static void handle_deactivate_desktop(Inkscape::Application *application, SPDesktop *desktop, void *data) { + static void handle_deactivate_desktop(InkscapeApplication *application, SPDesktop *desktop, void *data) { g_return_if_fail(data != NULL); static_cast<PanelDialogBase *>(data)->_propagateDesktopDeactivated(application, desktop); } - static void _handle_activate_desktop(Inkscape::Application *application, SPDesktop *desktop, void *data) { + static void _handle_activate_desktop(InkscapeApplication *application, SPDesktop *desktop, void *data) { g_return_if_fail(data != NULL); static_cast<PanelDialogBase *>(data)->_propagateDesktopActivated(application, desktop); } inline virtual void _propagateDocumentReplaced(SPDesktop* desktop, SPDocument *document); - inline virtual void _propagateDesktopActivated(Inkscape::Application *, SPDesktop *); - inline virtual void _propagateDesktopDeactivated(Inkscape::Application *, SPDesktop *); + inline virtual void _propagateDesktopActivated(InkscapeApplication *, SPDesktop *); + inline virtual void _propagateDesktopDeactivated(InkscapeApplication *, SPDesktop *); UI::Widget::Panel &_panel; sigc::connection _document_replaced_connection; @@ -134,14 +134,14 @@ void PanelDialogBase::_propagateDocumentReplaced(SPDesktop *desktop, SPDocument _panel.signalDocumentReplaced().emit(desktop, document); } -void PanelDialogBase::_propagateDesktopActivated(Inkscape::Application *application, SPDesktop *desktop) +void PanelDialogBase::_propagateDesktopActivated(InkscapeApplication *application, SPDesktop *desktop) { _document_replaced_connection = desktop->connectDocumentReplaced(sigc::mem_fun(*this, &PanelDialogBase::_propagateDocumentReplaced)); _panel.signalActivateDesktop().emit(application, desktop); } -void PanelDialogBase::_propagateDesktopDeactivated(Inkscape::Application *application, SPDesktop *desktop) +void PanelDialogBase::_propagateDesktopDeactivated(InkscapeApplication *application, SPDesktop *desktop) { _document_replaced_connection.disconnect(); _panel.signalDeactiveDesktop().emit(application, desktop); diff --git a/src/ui/dialog/polar-arrange-tab.cpp b/src/ui/dialog/polar-arrange-tab.cpp index a00b8fc02..80579c9d3 100644 --- a/src/ui/dialog/polar-arrange-tab.cpp +++ b/src/ui/dialog/polar-arrange-tab.cpp @@ -25,6 +25,7 @@ #include "desktop.h" #include "sp-ellipse.h" #include "sp-item-transform.h" +#include <gtkmm/messagedialog.h> namespace Inkscape { namespace UI { @@ -32,7 +33,11 @@ namespace Dialog { PolarArrangeTab::PolarArrangeTab(ArrangeDialog *parent_) : parent(parent_), +#if WITH_GTKMM_3_0 + parametersTable(), +#else parametersTable(3, 3, false), +#endif centerY("", "Y coordinate of the center", UNIT_TYPE_LINEAR), centerX("", "X coordinate of the center", centerY), radiusY("", "Y coordinate of the radius", UNIT_TYPE_LINEAR), @@ -76,7 +81,11 @@ PolarArrangeTab::PolarArrangeTab(ArrangeDialog *parent_) pack_start(arrangeOnParametersRadio, false, false); centerLabel.set_text(_("Center X/Y:")); +#if WITH_GTKMM_3_0 + parametersTable.attach(centerLabel, 0, 0, 1, 1); +#else parametersTable.attach(centerLabel, 0, 1, 0, 1, Gtk::FILL); +#endif centerX.setDigits(2); centerX.setIncrements(0.2, 0); centerX.setRange(-10000, 10000); @@ -85,11 +94,20 @@ PolarArrangeTab::PolarArrangeTab(ArrangeDialog *parent_) centerY.setIncrements(0.2, 0); centerY.setRange(-10000, 10000); centerY.setValue(0, "px"); +#if WITH_GTKMM_3_0 + parametersTable.attach(centerX, 1, 0, 1, 1); + parametersTable.attach(centerY, 2, 0, 1, 1); +#else parametersTable.attach(centerX, 1, 2, 0, 1, Gtk::FILL); parametersTable.attach(centerY, 2, 3, 0, 1, Gtk::FILL); +#endif radiusLabel.set_text(_("Radius X/Y:")); +#if WITH_GTKMM_3_0 + parametersTable.attach(radiusLabel, 0, 1, 1, 1); +#else parametersTable.attach(radiusLabel, 0, 1, 1, 2, Gtk::FILL); +#endif radiusX.setDigits(2); radiusX.setIncrements(0.2, 0); radiusX.setRange(0.001, 10000); @@ -98,11 +116,20 @@ PolarArrangeTab::PolarArrangeTab(ArrangeDialog *parent_) radiusY.setIncrements(0.2, 0); radiusY.setRange(0.001, 10000); radiusY.setValue(100, "px"); +#if WITH_GTKMM_3_0 + parametersTable.attach(radiusX, 1, 1, 1, 1); + parametersTable.attach(radiusY, 2, 1, 1, 1); +#else parametersTable.attach(radiusX, 1, 2, 1, 2, Gtk::FILL); parametersTable.attach(radiusY, 2, 3, 1, 2, Gtk::FILL); +#endif angleLabel.set_text(_("Angle X/Y:")); +#if WITH_GTKMM_3_0 + parametersTable.attach(angleLabel, 0, 2, 1, 1); +#else parametersTable.attach(angleLabel, 0, 1, 2, 3, Gtk::FILL); +#endif angleX.setDigits(2); angleX.setIncrements(0.2, 0); angleX.setRange(-10000, 10000); @@ -111,8 +138,13 @@ PolarArrangeTab::PolarArrangeTab(ArrangeDialog *parent_) angleY.setIncrements(0.2, 0); angleY.setRange(-10000, 10000); angleY.setValue(180, "°"); +#if WITH_GTKMM_3_0 + parametersTable.attach(angleX, 1, 2, 1, 1); + parametersTable.attach(angleY, 2, 2, 1, 1); +#else parametersTable.attach(angleX, 1, 2, 2, 3, Gtk::FILL); parametersTable.attach(angleY, 2, 3, 2, 3, Gtk::FILL); +#endif pack_start(parametersTable, false, false); rotateObjectsCheckBox.set_label(_("Rotate objects")); diff --git a/src/ui/dialog/polar-arrange-tab.h b/src/ui/dialog/polar-arrange-tab.h index f6c3b2906..f7d7bf11f 100644 --- a/src/ui/dialog/polar-arrange-tab.h +++ b/src/ui/dialog/polar-arrange-tab.h @@ -10,9 +10,22 @@ #ifndef INKSCAPE_UI_DIALOG_POLAR_ARRANGE_TAB_H #define INKSCAPE_UI_DIALOG_POLAR_ARRANGE_TAB_H +#if HAVE_CONFIG_H + #include "config.h" +#endif + +#include "ui/widget/scalar-unit.h" #include "ui/widget/anchor-selector.h" #include "ui/dialog/arrange-tab.h" -#include "ui/widget/scalar-unit.h" + +#include <gtkmm/radiobutton.h> +#include <gtkmm/radiobuttongroup.h> + +#if WITH_GTKMM_3_0 + #include <gtkmm/grid.h> +#else + #include <gtkmm/table.h> +#endif namespace Inkscape { namespace UI { @@ -62,7 +75,11 @@ private: Gtk::RadioButton arrangeOnLastCircleRadio; Gtk::RadioButton arrangeOnParametersRadio; +#if WITH_GTKMM_3_0 + Gtk::Grid parametersTable; +#else Gtk::Table parametersTable; +#endif Gtk::Label centerLabel; Inkscape::UI::Widget::ScalarUnit centerY; diff --git a/src/ui/dialog/spellcheck.cpp b/src/ui/dialog/spellcheck.cpp index 8a4ddc57e..9faa8a2cb 100644 --- a/src/ui/dialog/spellcheck.cpp +++ b/src/ui/dialog/spellcheck.cpp @@ -23,9 +23,9 @@ #include "selection.h" #include "desktop.h" #include "desktop-handles.h" -#include "tools-switch.h" +#include "ui/tools-switch.h" #include "ui/tools/text-tool.h" -#include "interface.h" +#include "ui/interface.h" #include "preferences.h" #include "sp-text.h" #include "sp-flowtext.h" @@ -347,7 +347,7 @@ SpellCheck::init(SPDesktop *d) char *slashPos = strrchr(exeName, '\\'); if (slashPos) *slashPos = '\0'; - g_print ("%s\n", exeName); + //g_print ("Aspell prefix path: %s\n", exeName); #endif _stops = 0; @@ -356,54 +356,54 @@ SpellCheck::init(SPDesktop *d) #ifdef HAVE_ASPELL { - AspellConfig *config = new_aspell_config(); + AspellConfig *config = new_aspell_config(); #ifdef WIN32 - aspell_config_replace(config, "prefix", exeName); + aspell_config_replace(config, "prefix", exeName); #endif - aspell_config_replace(config, "lang", _lang.c_str()); - aspell_config_replace(config, "encoding", "UTF-8"); - AspellCanHaveError *ret = new_aspell_speller(config); - delete_aspell_config(config); - if (aspell_error(ret) != 0) { - g_warning("Error: %s\n", aspell_error_message(ret)); - delete_aspell_can_have_error(ret); - return false; - } - _speller = to_aspell_speller(ret); + aspell_config_replace(config, "lang", _lang.c_str()); + aspell_config_replace(config, "encoding", "UTF-8"); + AspellCanHaveError *ret = new_aspell_speller(config); + delete_aspell_config(config); + if (aspell_error(ret) != 0) { + g_warning("Error: %s\n", aspell_error_message(ret)); + delete_aspell_can_have_error(ret); + return false; + } + _speller = to_aspell_speller(ret); } if (_lang2 != "") { - AspellConfig *config = new_aspell_config(); + AspellConfig *config = new_aspell_config(); #ifdef WIN32 - aspell_config_replace(config, "prefix", exeName); + aspell_config_replace(config, "prefix", exeName); #endif - aspell_config_replace(config, "lang", _lang2.c_str()); - aspell_config_replace(config, "encoding", "UTF-8"); - AspellCanHaveError *ret = new_aspell_speller(config); - delete_aspell_config(config); - if (aspell_error(ret) != 0) { - g_warning("Error: %s\n", aspell_error_message(ret)); - delete_aspell_can_have_error(ret); - return false; - } - _speller2 = to_aspell_speller(ret); + aspell_config_replace(config, "lang", _lang2.c_str()); + aspell_config_replace(config, "encoding", "UTF-8"); + AspellCanHaveError *ret = new_aspell_speller(config); + delete_aspell_config(config); + if (aspell_error(ret) != 0) { + g_warning("Error: %s\n", aspell_error_message(ret)); + delete_aspell_can_have_error(ret); + return false; + } + _speller2 = to_aspell_speller(ret); } if (_lang3 != "") { - AspellConfig *config = new_aspell_config(); + AspellConfig *config = new_aspell_config(); #ifdef WIN32 - aspell_config_replace(config, "prefix", exeName); + aspell_config_replace(config, "prefix", exeName); #endif - aspell_config_replace(config, "lang", _lang3.c_str()); - aspell_config_replace(config, "encoding", "UTF-8"); - AspellCanHaveError *ret = new_aspell_speller(config); - delete_aspell_config(config); - if (aspell_error(ret) != 0) { - g_warning("Error: %s\n", aspell_error_message(ret)); - delete_aspell_can_have_error(ret); - return false; - } - _speller3 = to_aspell_speller(ret); + aspell_config_replace(config, "lang", _lang3.c_str()); + aspell_config_replace(config, "encoding", "UTF-8"); + AspellCanHaveError *ret = new_aspell_speller(config); + delete_aspell_config(config); + if (aspell_error(ret) != 0) { + g_warning("Error: %s\n", aspell_error_message(ret)); + delete_aspell_can_have_error(ret); + return false; + } + _speller3 = to_aspell_speller(ret); } #endif /* HAVE_ASPELL */ diff --git a/src/ui/dialog/swatches.cpp b/src/ui/dialog/swatches.cpp index 4f0cb211a..a3cfeeba8 100644 --- a/src/ui/dialog/swatches.cpp +++ b/src/ui/dialog/swatches.cpp @@ -745,11 +745,10 @@ void SwatchesPanel::setDesktop( SPDesktop* desktop ) class DocTrack { public: - DocTrack(SPDocument *doc, sigc::connection &docDestroy, sigc::connection &gradientRsrcChanged, sigc::connection &defsChanged, sigc::connection &defsModified) : - doc(doc), + DocTrack(SPDocument *doc, sigc::connection &gradientRsrcChanged, sigc::connection &defsChanged, sigc::connection &defsModified) : + doc(doc->doRef()), updatePending(false), lastGradientUpdate(0.0), - docDestroy(docDestroy), gradientRsrcChanged(gradientRsrcChanged), defsChanged(defsChanged), defsModified(defsModified) @@ -774,10 +773,10 @@ public: } } if (doc) { - docDestroy.disconnect(); gradientRsrcChanged.disconnect(); defsChanged.disconnect(); defsModified.disconnect(); + doc->doUnref(); doc = NULL; } } @@ -798,7 +797,6 @@ public: SPDocument *doc; bool updatePending; double lastGradientUpdate; - sigc::connection docDestroy; sigc::connection gradientRsrcChanged; sigc::connection defsChanged; sigc::connection defsModified; @@ -894,12 +892,11 @@ void SwatchesPanel::_trackDocument( SwatchesPanel *panel, SPDocument *document ) } docPerPanel[panel] = document; if (!found) { - sigc::connection conn0 = document->connectDestroy(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDocumentDestroy), document)); sigc::connection conn1 = document->connectResourcesChanged( "gradient", sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleGradientsChange), document) ); sigc::connection conn2 = document->getDefs()->connectRelease( sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document)) ); sigc::connection conn3 = document->getDefs()->connectModified( sigc::hide(sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document))) ); - DocTrack *dt = new DocTrack(document, conn0, conn1, conn2, conn3); + DocTrack *dt = new DocTrack(document, conn1, conn2, conn3); docTrackings.push_back(dt); if (docPalettes.find(document) == docPalettes.end()) { @@ -928,13 +925,11 @@ static void recalcSwatchContents(SPDocument* doc, { std::vector<SPGradient*> newList; - if (doc) { - const GSList *gradients = doc->getResourceList("gradient"); - for (const GSList *item = gradients; item; item = item->next) { - SPGradient* grad = SP_GRADIENT(item->data); - if ( grad->isSwatch() ) { - newList.push_back(SP_GRADIENT(item->data)); - } + const GSList *gradients = doc->getResourceList("gradient"); + for (const GSList *item = gradients; item; item = item->next) { + SPGradient* grad = SP_GRADIENT(item->data); + if ( grad->isSwatch() ) { + newList.push_back(SP_GRADIENT(item->data)); } } @@ -973,37 +968,6 @@ static void recalcSwatchContents(SPDocument* doc, } } -void SwatchesPanel::handleDocumentDestroy(SPDocument *document) -{ - if (document) { - for (std::vector<DocTrack*>::iterator it = docTrackings.begin(); it != docTrackings.end(); ++it){ - if ((*it)->doc == document) { - delete *it; - docTrackings.erase(it); - break; - } - } - - if (docPalettes.find(document) != docPalettes.end()) { - docPalettes.erase(document); - } - - for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) { - if (it->second == document) { - SwatchesPanel* swp = it->first; - std::vector<SwatchPage*> pages = swp->_getSwatchSets(); - if ((swp->_currentIndex >= static_cast<int>(pages.size())) && (pages.size() > 0)) - { - swp->_setSelectedIndex(swp->_getSwatchSets().size() - 1); - } - swp->_rebuild(); - docPerPanel.erase(it); - break; - } - } - } -} - void SwatchesPanel::handleGradientsChange(SPDocument *document) { SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0; @@ -1178,45 +1142,38 @@ void SwatchesPanel::_handleAction( int setId, int itemId ) switch( setId ) { case 3: { - _setSelectedIndex(itemId); - } - break; - } -} + std::vector<SwatchPage*> pages = _getSwatchSets(); + if ( itemId >= 0 && itemId < static_cast<int>(pages.size()) ) { + _currentIndex = itemId; -void SwatchesPanel::_setSelectedIndex( int index ) -{ - std::vector<SwatchPage*> pages = _getSwatchSets(); - if ( index >= 0 && index < static_cast<int>(pages.size()) ) { - _currentIndex = index; + if ( !_prefs_path.empty() ) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setString(_prefs_path + "/palette", pages[_currentIndex]->_name); + } - if ( !_prefs_path.empty() ) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setString(_prefs_path + "/palette", pages[_currentIndex]->_name); + _rebuild(); + } } - - _rebuild(); + break; } } void SwatchesPanel::_rebuild() { std::vector<SwatchPage*> pages = _getSwatchSets(); - if (_currentIndex < static_cast<int>(pages.size())) { - SwatchPage* curr = pages[_currentIndex]; - _holder->clear(); + SwatchPage* curr = pages[_currentIndex]; + _holder->clear(); - if ( curr->_prefWidth > 0 ) { - _holder->setColumnPref( curr->_prefWidth ); - } - _holder->freezeUpdates(); - // TODO restore once 'clear' works _holder->addPreview(_clear); - _holder->addPreview(_remove); - for ( boost::ptr_vector<ColorItem>::iterator it = curr->_colors.begin(); it != curr->_colors.end(); ++it) { - _holder->addPreview(&*it); - } - _holder->thawUpdates(); + if ( curr->_prefWidth > 0 ) { + _holder->setColumnPref( curr->_prefWidth ); + } + _holder->freezeUpdates(); + // TODO restore once 'clear' works _holder->addPreview(_clear); + _holder->addPreview(_remove); + for ( boost::ptr_vector<ColorItem>::iterator it = curr->_colors.begin(); it != curr->_colors.end(); ++it) { + _holder->addPreview(&*it); } + _holder->thawUpdates(); } } //namespace Dialogs diff --git a/src/ui/dialog/swatches.h b/src/ui/dialog/swatches.h index 3abb81d98..ca4c1687d 100644 --- a/src/ui/dialog/swatches.h +++ b/src/ui/dialog/swatches.h @@ -43,13 +43,11 @@ public: virtual int getSelectedIndex() {return _currentIndex;} // temporary protected: - static void handleDocumentDestroy(SPDocument *document); static void handleGradientsChange(SPDocument *document); virtual void _updateFromSelection(); virtual void _handleAction( int setId, int itemId ); virtual void _setDocument( SPDocument *document ); - virtual void _setSelectedIndex( int index ); virtual void _rebuild(); virtual std::vector<SwatchPage*> _getSwatchSets() const; diff --git a/src/ui/dialog/tags.cpp b/src/ui/dialog/tags.cpp new file mode 100644 index 000000000..127e4d95e --- /dev/null +++ b/src/ui/dialog/tags.cpp @@ -0,0 +1,1165 @@ +/* + * A simple panel for tags + * + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if WITH_GLIBMM_2_32 +# include <glibmm/threads.h> +#endif + +#include "tags.h" +#include <gtkmm/widget.h> +#include <gtkmm/icontheme.h> +#include <gtkmm/imagemenuitem.h> +#include <gtkmm/separatormenuitem.h> + +#include <glibmm/main.h> +#include <glibmm/i18n.h> + +#include "desktop.h" +#include "desktop-style.h" +#include "document.h" +#include "document-undo.h" +#include "helper/action.h" +#include "inkscape.h" +#include "layer-fns.h" +#include "layer-manager.h" +#include "preferences.h" +#include "sp-item.h" +#include "sp-object.h" +#include "sp-shape.h" +#include "svg/css-ostringstream.h" +#include "ui/icon-names.h" +#include "ui/widget/layertypeicon.h" +#include "ui/widget/addtoicon.h" +#include "verbs.h" +#include "widgets/icon.h" +#include "xml/node.h" +#include "xml/node-observer.h" +#include "xml/repr.h" +#include "sp-root.h" +#include "ui/tools/tool-base.h" //"event-context.h" +#include "selection.h" +//#include "dialogs/dialog-events.h" +#include "widgets/sp-color-notebook.h" +#include "style.h" +#include "filter-chemistry.h" +#include "filters/blend.h" +#include "filters/gaussian-blur.h" +#include "sp-clippath.h" +#include "sp-mask.h" +#include "sp-tag.h" +#include "sp-defs.h" +#include "sp-tag-use.h" +#include "sp-tag-use-reference.h" + +//#define DUMP_LAYERS 1 + +namespace Inkscape { +namespace UI { +namespace Dialog { + +using Inkscape::XML::Node; + +TagsPanel& TagsPanel::getInstance() +{ + return *new TagsPanel(); +} + +enum { + COL_ADD = 1 +}; + +enum { + BUTTON_NEW = 0, + BUTTON_TOP, + BUTTON_BOTTOM, + BUTTON_UP, + BUTTON_DOWN, + BUTTON_DELETE, + DRAGNDROP +}; + +class TagsPanel::ObjectWatcher : public Inkscape::XML::NodeObserver { +public: + ObjectWatcher(TagsPanel* pnl, SPObject* obj, Inkscape::XML::Node * repr) : + _pnl(pnl), + _obj(obj), + _repr(repr), + _labelAttr(g_quark_from_string("inkscape:label")) + {} + + ObjectWatcher(TagsPanel* pnl, SPObject* obj) : + _pnl(pnl), + _obj(obj), + _repr(obj->getRepr()), + _labelAttr(g_quark_from_string("inkscape:label")) + {} + + virtual void notifyChildAdded( Node &/*node*/, Node &/*child*/, Node */*prev*/ ) + { + if ( _pnl && _obj ) { + _pnl->_objectsChanged( _obj ); + } + } + virtual void notifyChildRemoved( Node &/*node*/, Node &/*child*/, Node */*prev*/ ) + { + if ( _pnl && _obj ) { + _pnl->_objectsChanged( _obj ); + } + } + virtual void notifyChildOrderChanged( Node &/*node*/, Node &/*child*/, Node */*old_prev*/, Node */*new_prev*/ ) + { + if ( _pnl && _obj ) { + _pnl->_objectsChanged( _obj ); + } + } + virtual void notifyContentChanged( Node &/*node*/, Util::ptr_shared<char> /*old_content*/, Util::ptr_shared<char> /*new_content*/ ) {} + virtual void notifyAttributeChanged( Node &/*node*/, GQuark name, Util::ptr_shared<char> /*old_value*/, Util::ptr_shared<char> /*new_value*/ ) { + if ( _pnl && _obj ) { + if ( name == _labelAttr ) { + _pnl->_updateObject( _obj); + } + } + } + + TagsPanel* _pnl; + SPObject* _obj; + Inkscape::XML::Node* _repr; + GQuark _labelAttr; +}; + +class TagsPanel::InternalUIBounce +{ +public: + int _actionCode; +}; + +void TagsPanel::_styleButton( Gtk::Button& btn, SPDesktop *desktop, unsigned int code, char const* iconName, char const* tooltip ) +{ + bool set = false; + + if ( iconName ) { + GtkWidget *child = sp_icon_new( Inkscape::ICON_SIZE_SMALL_TOOLBAR, iconName ); + gtk_widget_show( child ); + btn.add( *manage(Glib::wrap(child)) ); + btn.set_relief(Gtk::RELIEF_NONE); + set = true; + } + + if ( desktop ) { + Verb *verb = Verb::get( code ); + if ( verb ) { + SPAction *action = verb->get_action(desktop); + if ( !set && action && action->image ) { + GtkWidget *child = sp_icon_new( Inkscape::ICON_SIZE_SMALL_TOOLBAR, action->image ); + gtk_widget_show( child ); + btn.add( *manage(Glib::wrap(child)) ); + set = true; + } + } + } + + btn.set_tooltip_text (tooltip); +} + + +Gtk::MenuItem& TagsPanel::_addPopupItem( SPDesktop *desktop, unsigned int code, char const* iconName, char const* fallback, int id ) +{ + GtkWidget* iconWidget = 0; + const char* label = 0; + + if ( iconName ) { + iconWidget = sp_icon_new( Inkscape::ICON_SIZE_MENU, iconName ); + } + + if ( desktop ) { + Verb *verb = Verb::get( code ); + if ( verb ) { + SPAction *action = verb->get_action(desktop); + if ( !iconWidget && action && action->image ) { + iconWidget = sp_icon_new( Inkscape::ICON_SIZE_MENU, action->image ); + } + + if ( action ) { + label = action->name; + } + } + } + + if ( !label && fallback ) { + label = fallback; + } + + Gtk::Widget* wrapped = 0; + if ( iconWidget ) { + wrapped = manage(Glib::wrap(iconWidget)); + wrapped->show(); + } + + + Gtk::MenuItem* item = 0; + + if (wrapped) { + item = Gtk::manage(new Gtk::ImageMenuItem(*wrapped, label, true)); + } else { + item = Gtk::manage(new Gtk::MenuItem(label, true)); + } + + item->signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &TagsPanel::_takeAction), id)); + _popupMenu.append(*item); + + return *item; +} + +void TagsPanel::_fireAction( unsigned int code ) +{ + if ( _desktop ) { + Verb *verb = Verb::get( code ); + if ( verb ) { + SPAction *action = verb->get_action(_desktop); + if ( action ) { + sp_action_perform( action, NULL ); + } + } + } +} + +void TagsPanel::_takeAction( int val ) +{ + if ( !_pending ) { + _pending = new InternalUIBounce(); + _pending->_actionCode = val; + Glib::signal_timeout().connect( sigc::mem_fun(*this, &TagsPanel::_executeAction), 0 ); + } +} + +bool TagsPanel::_executeAction() +{ + // Make sure selected layer hasn't changed since the action was triggered + if ( _pending) + { + int val = _pending->_actionCode; +// SPObject* target = _pending->_target; + bool empty = _desktop->selection->isEmpty(); + + switch ( val ) { + case BUTTON_NEW: + { + _fireAction( SP_VERB_TAG_NEW ); + } + break; + case BUTTON_TOP: + { + _fireAction( empty ? SP_VERB_LAYER_TO_TOP : SP_VERB_SELECTION_TO_FRONT); + } + break; + case BUTTON_BOTTOM: + { + _fireAction( empty ? SP_VERB_LAYER_TO_BOTTOM : SP_VERB_SELECTION_TO_BACK ); + } + break; + case BUTTON_UP: + { + _fireAction( empty ? SP_VERB_LAYER_RAISE : SP_VERB_SELECTION_RAISE ); + } + break; + case BUTTON_DOWN: + { + _fireAction( empty ? SP_VERB_LAYER_LOWER : SP_VERB_SELECTION_LOWER ); + } + break; + case BUTTON_DELETE: + { + std::vector<SPObject *> todelete; + _tree.get_selection()->selected_foreach_iter(sigc::bind<std::vector<SPObject *>*>(sigc::mem_fun(*this, &TagsPanel::_checkForDeleted), &todelete)); + for (std::vector<SPObject *>::iterator iter = todelete.begin(); iter != todelete.end(); ++iter) { + SPObject * obj = *iter; + if (obj && obj->parent && obj->getRepr() && obj->parent->getRepr()) { + //obj->parent->getRepr()->removeChild(obj->getRepr()); + obj->deleteObject(true, true); + } + } + DocumentUndo::done(_document, SP_VERB_DIALOG_TAGS, _("Remove from selection set")); + } + break; + case DRAGNDROP: + { + _doTreeMove( ); + } + break; + } + + delete _pending; + _pending = 0; + } + + return false; +} + + +class TagsPanel::ModelColumns : public Gtk::TreeModel::ColumnRecord +{ +public: + + ModelColumns() + { + add(_colParentObject); + add(_colObject); + add(_colLabel); + add(_colAddRemove); + add(_colAllowAddRemove); + } + virtual ~ModelColumns() {} + + Gtk::TreeModelColumn<SPObject*> _colParentObject; + Gtk::TreeModelColumn<SPObject*> _colObject; + Gtk::TreeModelColumn<Glib::ustring> _colLabel; + Gtk::TreeModelColumn<bool> _colAddRemove; + Gtk::TreeModelColumn<bool> _colAllowAddRemove; +}; + +void TagsPanel::_checkForDeleted(const Gtk::TreeIter& iter, std::vector<SPObject *>* todelete) +{ + Gtk::TreeRow row = *iter; + SPObject * obj = row[_model->_colObject]; + if (obj && obj->parent) { + todelete->push_back(obj); + } +} + +void TagsPanel::_updateObject( SPObject *obj ) { + _store->foreach( sigc::bind<SPObject*>(sigc::mem_fun(*this, &TagsPanel::_checkForUpdated), obj) ); +} + +bool TagsPanel::_checkForUpdated(const Gtk::TreePath &/*path*/, const Gtk::TreeIter& iter, SPObject* obj) +{ + Gtk::TreeModel::Row row = *iter; + if ( obj == row[_model->_colObject] ) + { + /* + * We get notified of layer update here (from layer->setLabel()) before layer->label() is set + * with the correct value (sp-object bug?). So use the inkscape:label attribute instead which + * has the correct value (bug #168351) + */ + //row[_model->_colLabel] = layer->label() ? layer->label() : layer->getId(); + gchar const *label; + SPTagUse * use = SP_IS_TAG_USE(obj) ? SP_TAG_USE(obj) : 0; + if (use && use->ref->isAttached()) { + label = use->ref->getObject()->getAttribute("inkscape:label"); + } else { + label = obj->getAttribute("inkscape:label"); + } + row[_model->_colLabel] = label ? label : obj->getId(); + row[_model->_colAddRemove] = SP_IS_TAG(obj); + } + + return false; +} + +void TagsPanel::_objectsSelected( Selection *sel ) { + + _selectedConnection.block(); + _tree.get_selection()->unselect_all(); + for (const GSList * iter = sel->list(); iter != NULL; iter = iter->next) + { + SPObject *obj = reinterpret_cast<SPObject *>(iter->data); + _store->foreach(sigc::bind<SPObject *>( sigc::mem_fun(*this, &TagsPanel::_checkForSelected), obj)); + } + _selectedConnection.unblock(); + _checkTreeSelection(); +} + +bool TagsPanel::_checkForSelected(const Gtk::TreePath &path, const Gtk::TreeIter& iter, SPObject* obj) +{ + Gtk::TreeModel::Row row = *iter; + SPObject * it = row[_model->_colObject]; + if ( it && SP_IS_TAG_USE(it) && SP_TAG_USE(it)->ref->getObject() == obj ) + { + Glib::RefPtr<Gtk::TreeSelection> select = _tree.get_selection(); + + select->select(iter); + } + return false; +} + +void TagsPanel::_objectsChanged(SPObject* root) +{ + while (!_objectWatchers.empty()) + { + TagsPanel::ObjectWatcher *w = _objectWatchers.back(); + w->_repr->removeObserver(*w); + _objectWatchers.pop_back(); + delete w; + } + + if (_desktop) { + SPDocument* document = _desktop->doc(); + SPDefs* root = document->getDefs(); + if ( root ) { + _selectedConnection.block(); + _store->clear(); + _addObject( document, root, 0 ); + _selectedConnection.unblock(); + _objectsSelected(_desktop->selection); + _checkTreeSelection(); + } + } +} + +void TagsPanel::_addObject( SPDocument* doc, SPObject* obj, Gtk::TreeModel::Row* parentRow ) +{ + if ( _desktop && obj ) { + for ( SPObject *child = obj->children; child != NULL; child = child->next) { + if (SP_IS_TAG(child)) + { + Gtk::TreeModel::iterator iter = parentRow ? _store->prepend(parentRow->children()) : _store->prepend(); + Gtk::TreeModel::Row row = *iter; + row[_model->_colObject] = child; + row[_model->_colParentObject] = NULL; + row[_model->_colLabel] = child->label() ? child->label() : child->getId(); + row[_model->_colAddRemove] = true; + row[_model->_colAllowAddRemove] = true; + + _tree.expand_to_path( _store->get_path(iter) ); + + TagsPanel::ObjectWatcher *w = new TagsPanel::ObjectWatcher(this, child); + child->getRepr()->addObserver(*w); + _objectWatchers.push_back(w); + _addObject( doc, child, &row ); + } + } + if (SP_IS_TAG(obj) && obj->children) + { + Gtk::TreeModel::iterator iteritems = parentRow ? _store->append(parentRow->children()) : _store->prepend(); + Gtk::TreeModel::Row rowitems = *iteritems; + rowitems[_model->_colObject] = NULL; + rowitems[_model->_colParentObject] = obj; + rowitems[_model->_colLabel] = _("Items"); + rowitems[_model->_colAddRemove] = false; + rowitems[_model->_colAllowAddRemove] = false; + + _tree.expand_to_path( _store->get_path(iteritems) ); + + for ( SPObject *child = obj->children; child != NULL; child = child->next) { + if (SP_IS_TAG_USE(child)) + { + SPItem *item = SP_TAG_USE(child)->ref->getObject(); + Gtk::TreeModel::iterator iter = _store->prepend(rowitems->children()); + Gtk::TreeModel::Row row = *iter; + row[_model->_colObject] = child; + row[_model->_colParentObject] = NULL; + row[_model->_colLabel] = item ? (item->label() ? item->label() : item->getId()) : SP_TAG_USE(child)->href; + row[_model->_colAddRemove] = false; + row[_model->_colAllowAddRemove] = true; + + if (SP_TAG(obj)->expanded()) { + _tree.expand_to_path( _store->get_path(iter) ); + } + + if (item) { + TagsPanel::ObjectWatcher *w = new TagsPanel::ObjectWatcher(this, child, item->getRepr()); + item->getRepr()->addObserver(*w); + _objectWatchers.push_back(w); + } + } + } + } + } +} + +void TagsPanel::_select_tag( SPTag * tag ) +{ + for (SPObject * child = tag->children; child != NULL; child = child->next) + { + if (SP_IS_TAG(child)) { + _select_tag(SP_TAG(child)); + } else if (SP_IS_TAG_USE(child)) { + SPObject * obj = SP_TAG_USE(child)->ref->getObject(); + if (obj) { + if (_desktop->selection->isEmpty()) _desktop->setCurrentLayer(obj->parent); + _desktop->selection->add(obj); + } + } + } +} + +void TagsPanel::_selected_row_callback( const Gtk::TreeModel::iterator& iter ) +{ + if (iter) { + Gtk::TreeModel::Row row = *iter; + SPObject *obj = row[_model->_colObject]; + if (obj) { + if (SP_IS_TAG(obj)) { + _select_tag(SP_TAG(obj)); + } else if (SP_IS_TAG_USE(obj)) { + SPObject * item = SP_TAG_USE(obj)->ref->getObject(); + if (item) { + if (_desktop->selection->isEmpty()) _desktop->setCurrentLayer(item->parent); + _desktop->selection->add(item); + } + } + } + } +} + +void TagsPanel::_pushTreeSelectionToCurrent() +{ + _selectionChangedConnection.block(); + // TODO hunt down the possible API abuse in getting NULL + if ( _desktop && _desktop->currentRoot() ) { + _desktop->selection->clear(); + _tree.get_selection()->selected_foreach_iter( sigc::mem_fun(*this, &TagsPanel::_selected_row_callback)); + } + _selectionChangedConnection.unblock(); + + _checkTreeSelection(); +} + +void TagsPanel::_checkTreeSelection() +{ + bool sensitive = _tree.get_selection()->count_selected_rows() > 0; + bool sensitiveNonTop = true; + bool sensitiveNonBottom = true; +// if ( _tree.get_selection()->count_selected_rows() > 0 ) { +// sensitive = true; +// +// SPObject* inTree = _selectedLayer(); +// if ( inTree ) { +// +// sensitiveNonTop = (Inkscape::Nex(inTree->parent, inTree) != 0); +// sensitiveNonBottom = (Inkscape::previous_layer(inTree->parent, inTree) != 0); +// +// } +// } + + + for ( std::vector<Gtk::Widget*>::iterator it = _watching.begin(); it != _watching.end(); ++it ) { + (*it)->set_sensitive( sensitive ); + } + for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonTop.begin(); it != _watchingNonTop.end(); ++it ) { + (*it)->set_sensitive( sensitiveNonTop ); + } + for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonBottom.begin(); it != _watchingNonBottom.end(); ++it ) { + (*it)->set_sensitive( sensitiveNonBottom ); + } +} + +bool TagsPanel::_handleKeyEvent(GdkEventKey *event) +{ + + switch (Inkscape::UI::Tools::get_group0_keyval(event)) { + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + case GDK_KEY_F2: { + Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected(); + if (iter && !_text_renderer->property_editable()) { + Gtk::TreeRow row = *iter; + SPObject * obj = row[_model->_colObject]; + if (obj && SP_IS_TAG(obj)) { + Gtk::TreeModel::Path *path = new Gtk::TreeModel::Path(iter); + // Edit the layer label + _text_renderer->property_editable() = true; + _tree.set_cursor(*path, *_name_column, true); + grab_focus(); + return true; + } + } + } + case GDK_KEY_Delete: { + std::vector<SPObject *> todelete; + _tree.get_selection()->selected_foreach_iter(sigc::bind<std::vector<SPObject *>*>(sigc::mem_fun(*this, &TagsPanel::_checkForDeleted), &todelete)); + if (!todelete.empty()) { + for (std::vector<SPObject *>::iterator iter = todelete.begin(); iter != todelete.end(); ++iter) { + SPObject * obj = *iter; + if (obj && obj->parent && obj->getRepr() && obj->parent->getRepr()) { + //obj->parent->getRepr()->removeChild(obj->getRepr()); + obj->deleteObject(true, true); + } + } + DocumentUndo::done(_document, SP_VERB_DIALOG_TAGS, _("Remove from selection set")); + } + return true; + } + break; + } + return false; +} + +bool TagsPanel::_handleButtonEvent(GdkEventButton* event) +{ + static unsigned doubleclick = 0; + + if ( (event->type == GDK_BUTTON_PRESS) && (event->button == 3) ) { + // TODO - fix to a better is-popup function + Gtk::TreeModel::Path path; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + if ( _tree.get_path_at_pos( x, y, path ) ) { + _checkTreeSelection(); + _popupMenu.popup(event->button, event->time); + if (_tree.get_selection()->is_selected(path)) { + return true; + } + } + } + + if ( (event->type == GDK_BUTTON_PRESS) && (event->button == 1)) { + // Alt left click on the visible/lock columns - eat this event to keep row selection + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col = 0; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + int x2 = 0; + int y2 = 0; + if ( _tree.get_path_at_pos( x, y, path, col, x2, y2 ) ) { + if (col == _tree.get_column(COL_ADD-1)) { + down_at_add = true; + return true; + } else if ( !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) & _tree.get_selection()->is_selected(path) ) { + _tree.get_selection()->set_select_function(sigc::mem_fun(*this, &TagsPanel::_noSelection)); + _defer_target = path; + } else { + down_at_add = false; + } + } else { + down_at_add = false; + } + } + + if ( event->type == GDK_BUTTON_RELEASE) { + _tree.get_selection()->set_select_function(sigc::mem_fun(*this, &TagsPanel::_rowSelectFunction)); + } + + // TODO - ImageToggler doesn't seem to handle Shift/Alt clicks - so we deal with them here. + if ( (event->type == GDK_BUTTON_RELEASE) && (event->button == 1)) { + + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col = 0; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + int x2 = 0; + int y2 = 0; + if ( _tree.get_path_at_pos( x, y, path, col, x2, y2 ) ) { + if (_defer_target) { + if (_defer_target == path && !(event->x == 0 && event->y == 0)) + { + _tree.set_cursor(path, *col, false); + } + _defer_target = Gtk::TreeModel::Path(); + } else { + Gtk::TreeModel::Children::iterator iter = _tree.get_model()->get_iter(path); + Gtk::TreeModel::Row row = *iter; + + SPObject* obj = row[_model->_colObject]; + + if (obj) { + if (col == _tree.get_column(COL_ADD - 1) && down_at_add) { + if (SP_IS_TAG(obj)) { + bool wasadded = false; + for (const GSList * iter = _desktop->selection->itemList(); iter != NULL; iter = iter->next) + { + SPObject *newobj = reinterpret_cast<SPObject *>(iter->data); + bool addchild = true; + for ( SPObject *child = obj->children; child != NULL; child = child->next) { + if (SP_IS_TAG_USE(child) && SP_TAG_USE(child)->ref->getObject() == newobj) { + addchild = false; + } + } + if (addchild) { + Inkscape::XML::Node *clone = _document->getReprDoc()->createElement("inkscape:tagref"); + clone->setAttribute("xlink:href", g_strdup_printf("#%s", newobj->getRepr()->attribute("id")), false); + obj->appendChild(clone); + wasadded = true; + } + } + if (wasadded) { + DocumentUndo::done(_document, SP_VERB_DIALOG_TAGS, _("Add selection to set")); + } + } else { + std::vector<SPObject *> todelete; + // FIXME unnecessary use of XML tree + _tree.get_selection()->selected_foreach_iter(sigc::bind<std::vector<SPObject *>*>(sigc::mem_fun(*this, &TagsPanel::_checkForDeleted), &todelete)); + if (!todelete.empty()) { + for (std::vector<SPObject *>::iterator iter = todelete.begin(); iter != todelete.end(); ++iter) { + SPObject * tobj = *iter; + if (tobj && tobj->parent && tobj->getRepr() && tobj->parent->getRepr()) { + //tobj->parent->getRepr()->removeChild(tobj->getRepr()); + tobj->deleteObject(true, true); + } + } + } else if (obj && obj->parent && obj->getRepr() && obj->parent->getRepr()) { + obj->parent->getRepr()->removeChild(obj->getRepr()); + } + DocumentUndo::done(_document, SP_VERB_DIALOG_TAGS, _("Remove from selection set")); + } + } + } + } + } + } + + + if ( (event->type == GDK_2BUTTON_PRESS) && (event->button == 1) ) { + doubleclick = 1; + } + + if ( event->type == GDK_BUTTON_RELEASE && doubleclick) { + doubleclick = 0; + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col = 0; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + int x2 = 0; + int y2 = 0; + if ( _tree.get_path_at_pos( x, y, path, col, x2, y2 ) && col == _name_column) { + Gtk::TreeModel::Children::iterator iter = _tree.get_model()->get_iter(path); + Gtk::TreeModel::Row row = *iter; + + SPObject* obj = row[_model->_colObject]; + if (obj && (SP_IS_TAG(obj) || (SP_IS_TAG_USE(obj) && SP_TAG_USE(obj)->ref->getObject()))) { + // Double click on the Layer name, enable editing + _text_renderer->property_editable() = true; + _tree.set_cursor (path, *_name_column, true); + grab_focus(); + } + } + } + + return false; +} + +void TagsPanel::_storeDragSource(const Gtk::TreeModel::iterator& iter) +{ + Gtk::TreeModel::Row row = *iter; + SPObject* obj = row[_model->_colObject]; + SPTag* item = ( obj && SP_IS_TAG(obj) ) ? SP_TAG(obj) : 0; + if (item) + { + _dnd_source.push_back(item); + } +} + +/* + * Drap and drop within the tree + * Save the drag source and drop target SPObjects and if its a drag between layers or into (sublayer) a layer + */ +bool TagsPanel::_handleDragDrop(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time) +{ + int cell_x = 0, cell_y = 0; + Gtk::TreeModel::Path target_path; + Gtk::TreeView::Column *target_column; + + _dnd_into = true; + _dnd_target = _document->getDefs(); + _dnd_source.clear(); + _tree.get_selection()->selected_foreach_iter(sigc::mem_fun(*this, &TagsPanel::_storeDragSource)); + + if (_dnd_source.empty()) { + return true; + } + + if (_tree.get_path_at_pos (x, y, target_path, target_column, cell_x, cell_y)) { + // Are we before, inside or after the drop layer + Gdk::Rectangle rect; + _tree.get_background_area (target_path, *target_column, rect); + int cell_height = rect.get_height(); + _dnd_into = (cell_y > (int)(cell_height * 1/3) && cell_y <= (int)(cell_height * 2/3)); + if (cell_y > (int)(cell_height * 2/3)) { + Gtk::TreeModel::Path next_path = target_path; + next_path.next(); + if (_store->iter_is_valid(_store->get_iter(next_path))) { + target_path = next_path; + } else { + // Dragging to the "end" + Gtk::TreeModel::Path up_path = target_path; + up_path.up(); + if (_store->iter_is_valid(_store->get_iter(up_path))) { + // Drop into parent + target_path = up_path; + _dnd_into = true; + } else { + // Drop into the top level + _dnd_target = _document->getDefs(); + _dnd_into = true; + } + } + } + Gtk::TreeModel::iterator iter = _store->get_iter(target_path); + if (_store->iter_is_valid(iter)) { + Gtk::TreeModel::Row row = *iter; + SPObject *obj = row[_model->_colObject]; + SPObject *pobj = row[_model->_colParentObject]; + if (obj) { + if (SP_IS_TAG(obj)) { + _dnd_target = SP_TAG(obj); + } else if (SP_IS_TAG(obj->parent)) { + _dnd_target = SP_TAG(obj->parent); + _dnd_into = true; + } + } else if (pobj && SP_IS_TAG(pobj)) { + _dnd_target = SP_TAG(pobj); + _dnd_into = true; + } else { + return true; + } + } + } + + _takeAction(DRAGNDROP); + + return false; +} + +/* + * Move a layer in response to a drag & drop action + */ +void TagsPanel::_doTreeMove( ) +{ + if (_dnd_target) { + for (std::vector<SPTag *>::iterator iter = _dnd_source.begin(); iter != _dnd_source.end(); ++iter) + { + SPTag *src = *iter; + if (src != _dnd_target) { + src->moveTo(_dnd_target, _dnd_into); + } + } + _desktop->selection->clear(); + while (!_dnd_source.empty()) + { + SPTag *src = _dnd_source.back(); + _select_tag(src); + _dnd_source.pop_back(); + } + DocumentUndo::done( _desktop->doc() , SP_VERB_DIALOG_TAGS, + _("Moved sets")); + } +} + + +void TagsPanel::_handleEdited(const Glib::ustring& path, const Glib::ustring& new_text) +{ + Gtk::TreeModel::iterator iter = _tree.get_model()->get_iter(path); + Gtk::TreeModel::Row row = *iter; + + _renameObject(row, new_text); + _text_renderer->property_editable() = false; +} + +void TagsPanel::_handleEditingCancelled() +{ + _text_renderer->property_editable() = false; +} + +void TagsPanel::_renameObject(Gtk::TreeModel::Row row, const Glib::ustring& name) +{ + if ( row && _desktop) { + SPObject* obj = row[_model->_colObject]; + if ( obj ) { + if (SP_IS_TAG(obj)) { + gchar const* oldLabel = obj->label(); + if ( !name.empty() && (!oldLabel || name != oldLabel) ) { + obj->setLabel(name.c_str()); + DocumentUndo::done( _desktop->doc() , SP_VERB_NONE, + _("Rename object")); + } + } else if (SP_IS_TAG_USE(obj) && (obj = SP_TAG_USE(obj)->ref->getObject())) { + gchar const* oldLabel = obj->label(); + if ( !name.empty() && (!oldLabel || name != oldLabel) ) { + obj->setLabel(name.c_str()); + DocumentUndo::done( _desktop->doc() , SP_VERB_NONE, + _("Rename object")); + } + } + } + } +} + +bool TagsPanel::_noSelection( Glib::RefPtr<Gtk::TreeModel> const & /*model*/, Gtk::TreeModel::Path const & /*path*/, bool currentlySelected ) +{ + return false; +} + +bool TagsPanel::_rowSelectFunction( Glib::RefPtr<Gtk::TreeModel> const & /*model*/, Gtk::TreeModel::Path const & /*path*/, bool currentlySelected ) +{ + bool val = true; + if ( !currentlySelected && _toggleEvent ) + { + GdkEvent* event = gtk_get_current_event(); + if ( event ) { + // (keep these checks separate, so we know when to call gdk_event_free() + if ( event->type == GDK_BUTTON_PRESS ) { + GdkEventButton const* target = reinterpret_cast<GdkEventButton const*>(_toggleEvent); + GdkEventButton const* evtb = reinterpret_cast<GdkEventButton const*>(event); + + if ( (evtb->window == target->window) + && (evtb->send_event == target->send_event) + && (evtb->time == target->time) + && (evtb->state == target->state) + ) + { + // Ooooh! It's a magic one + val = false; + } + } + gdk_event_free(event); + } + } + return val; +} + +void TagsPanel::_setExpanded(const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& /*path*/, bool isexpanded) +{ + Gtk::TreeModel::Row row = *iter; + + SPObject* obj = row[_model->_colParentObject]; + if (obj && SP_IS_TAG(obj)) + { + SP_TAG(obj)->setExpanded(isexpanded); + obj->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } +} + +/** + * Constructor + */ +TagsPanel::TagsPanel() : + UI::Widget::Panel("", "/dialogs/tags", SP_VERB_DIALOG_TAGS), + _rootWatcher(0), + deskTrack(), + _desktop(0), + _document(0), + _model(0), + _pending(0), + _toggleEvent(0), + _defer_target(), + desktopChangeConn() +{ + //Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + ModelColumns *zoop = new ModelColumns(); + _model = zoop; + + _store = Gtk::TreeStore::create( *zoop ); + + _tree.set_model( _store ); + _tree.set_headers_visible(false); + _tree.set_reorderable(true); + _tree.enable_model_drag_dest (Gdk::ACTION_MOVE); + + Inkscape::UI::Widget::AddToIcon * addRenderer = manage( new Inkscape::UI::Widget::AddToIcon()); + int addColNum = _tree.append_column("type", *addRenderer) - 1; + Gtk::TreeViewColumn *col = _tree.get_column(addColNum); + if ( col ) { + col->add_attribute( addRenderer->property_active(), _model->_colAddRemove ); + col->add_attribute( addRenderer->property_visible(), _model->_colAllowAddRemove ); + } + + _text_renderer = manage(new Gtk::CellRendererText()); + int nameColNum = _tree.append_column("Name", *_text_renderer) - 1; + _name_column = _tree.get_column(nameColNum); + _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel); + + _tree.set_expander_column( *_tree.get_column(nameColNum) ); + + _tree.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); + _selectedConnection = _tree.get_selection()->signal_changed().connect( sigc::mem_fun(*this, &TagsPanel::_pushTreeSelectionToCurrent) ); + _tree.get_selection()->set_select_function( sigc::mem_fun(*this, &TagsPanel::_rowSelectFunction) ); + + _tree.signal_drag_drop().connect( sigc::mem_fun(*this, &TagsPanel::_handleDragDrop), false); + _collapsedConnection = _tree.signal_row_collapsed().connect( sigc::bind<bool>(sigc::mem_fun(*this, &TagsPanel::_setExpanded), false)); + _expandedConnection = _tree.signal_row_expanded().connect( sigc::bind<bool>(sigc::mem_fun(*this, &TagsPanel::_setExpanded), true)); + + _text_renderer->signal_edited().connect( sigc::mem_fun(*this, &TagsPanel::_handleEdited) ); + _text_renderer->signal_editing_canceled().connect( sigc::mem_fun(*this, &TagsPanel::_handleEditingCancelled) ); + + _tree.signal_button_press_event().connect( sigc::mem_fun(*this, &TagsPanel::_handleButtonEvent), false ); + _tree.signal_button_release_event().connect( sigc::mem_fun(*this, &TagsPanel::_handleButtonEvent), false ); + _tree.signal_key_press_event().connect( sigc::mem_fun(*this, &TagsPanel::_handleKeyEvent), false ); + + _scroller.add( _tree ); + _scroller.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC ); + _scroller.set_shadow_type(Gtk::SHADOW_IN); + Gtk::Requisition sreq; +#if WITH_GTKMM_3_0 + Gtk::Requisition sreq_natural; + _scroller.get_preferred_size(sreq_natural, sreq); +#else + sreq = _scroller.size_request(); +#endif + int minHeight = 70; + if (sreq.height < minHeight) { + // Set a min height to see the layers when used with Ubuntu liboverlay-scrollbar + _scroller.set_size_request(sreq.width, minHeight); + } + + _layersPage.pack_start( _scroller, Gtk::PACK_EXPAND_WIDGET ); + + _layersPage.pack_end(_buttonsRow, Gtk::PACK_SHRINK); + + _getContents()->pack_start(_layersPage, Gtk::PACK_EXPAND_WIDGET); + + SPDesktop* targetDesktop = getDesktop(); + + Gtk::Button* btn = manage( new Gtk::Button() ); + _styleButton( *btn, targetDesktop, SP_VERB_TAG_NEW, GTK_STOCK_ADD, _("Add a new selection set") ); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &TagsPanel::_takeAction), (int)BUTTON_NEW) ); + _buttonsSecondary.pack_start(*btn, Gtk::PACK_SHRINK); + +// btn = manage( new Gtk::Button("Dup") ); +// btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_DUPLICATE) ); +// _buttonsRow.add( *btn ); + + btn = manage( new Gtk::Button() ); + _styleButton( *btn, targetDesktop, SP_VERB_LAYER_DELETE, GTK_STOCK_REMOVE, _("Remove Item/Set") ); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &TagsPanel::_takeAction), (int)BUTTON_DELETE) ); + _watching.push_back( btn ); + _buttonsSecondary.pack_start(*btn, Gtk::PACK_SHRINK); + + _buttonsRow.pack_start(_buttonsSecondary, Gtk::PACK_EXPAND_WIDGET); + _buttonsRow.pack_end(_buttonsPrimary, Gtk::PACK_EXPAND_WIDGET); + + // ------------------------------------------------------- + { + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_TAG_NEW, 0, "Add a new selection set", (int)BUTTON_NEW ) ); + + _popupMenu.show_all_children(); + } + // ------------------------------------------------------- + + + + for ( std::vector<Gtk::Widget*>::iterator it = _watching.begin(); it != _watching.end(); ++it ) { + (*it)->set_sensitive( false ); + } + for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonTop.begin(); it != _watchingNonTop.end(); ++it ) { + (*it)->set_sensitive( false ); + } + for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonBottom.begin(); it != _watchingNonBottom.end(); ++it ) { + (*it)->set_sensitive( false ); + } + + setDesktop( targetDesktop ); + + show_all_children(); + + // restorePanelPrefs(); + + // Connect this up last + desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &TagsPanel::setDesktop) ); + deskTrack.connect(GTK_WIDGET(gobj())); +} + +TagsPanel::~TagsPanel() +{ + + setDesktop(NULL); + + if ( _model ) + { + delete _model; + _model = 0; + } + + if (_pending) { + delete _pending; + _pending = 0; + } + + if ( _toggleEvent ) + { + gdk_event_free( _toggleEvent ); + _toggleEvent = 0; + } + + desktopChangeConn.disconnect(); + deskTrack.disconnect(); +} + +void TagsPanel::setDocument(SPDesktop* /*desktop*/, SPDocument* document) +{ + while (!_objectWatchers.empty()) + { + TagsPanel::ObjectWatcher *w = _objectWatchers.back(); + w->_repr->removeObserver(*w); + _objectWatchers.pop_back(); + delete w; + } + + if (_rootWatcher) + { + _rootWatcher->_repr->removeObserver(*_rootWatcher); + delete _rootWatcher; + _rootWatcher = NULL; + } + + _document = document; + + if (document && document->getDefs() && document->getDefs()->getRepr()) + { + _rootWatcher = new TagsPanel::ObjectWatcher(this, document->getDefs()); + document->getDefs()->getRepr()->addObserver(*_rootWatcher); + _objectsChanged(document->getDefs()); + } +} + +void TagsPanel::setDesktop( SPDesktop* desktop ) +{ + Panel::setDesktop(desktop); + + if ( desktop != _desktop ) { + _documentChangedConnection.disconnect(); + _selectionChangedConnection.disconnect(); + if ( _desktop ) { + _desktop = 0; + } + + _desktop = Panel::getDesktop(); + if ( _desktop ) { + //setLabel( _desktop->doc()->name ); + _documentChangedConnection = _desktop->connectDocumentReplaced( sigc::mem_fun(*this, &TagsPanel::setDocument)); + _selectionChangedConnection = _desktop->selection->connectChanged( sigc::mem_fun(*this, &TagsPanel::_objectsSelected)); + + setDocument(_desktop, _desktop->doc()); + } + } +/* + GSList const *layers = _desktop->doc()->getResourceList( "layer" ); + g_message( "layers list starts at %p", layers ); + for ( GSList const *iter=layers ; iter ; iter = iter->next ) { + SPObject *layer=static_cast<SPObject *>(iter->data); + g_message(" {%s} [%s]", layer->id, layer->label() ); + } +*/ + deskTrack.setBase(desktop); +} + + + + + +} //namespace Dialogs +} //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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/tags.h b/src/ui/dialog/tags.h new file mode 100644 index 000000000..d35dfba01 --- /dev/null +++ b/src/ui/dialog/tags.h @@ -0,0 +1,181 @@ +/* + * A simple dialog for tags UI. + * + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_TAGS_PANEL_H +#define SEEN_TAGS_PANEL_H + +#include <gtkmm/box.h> +#include <gtkmm/treeview.h> +#include <gtkmm/treestore.h> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/dialog.h> +#include "ui/widget/spinbutton.h" +#include "ui/widget/panel.h" +#include "ui/widget/object-composite-settings.h" +#include "desktop-tracker.h" +#include "ui/widget/style-subject.h" +#include "selection.h" +#include "ui/widget/filter-effect-chooser.h" + +class SPObject; +class SPTag; +struct SPColorSelector; + +namespace Inkscape { + +namespace UI { +namespace Dialog { + + +/** + * A panel that displays layers. + */ +class TagsPanel : public UI::Widget::Panel +{ +public: + TagsPanel(); + virtual ~TagsPanel(); + + //virtual void setOrientation( Gtk::AnchorType how ); + + static TagsPanel& getInstance(); + + void setDesktop( SPDesktop* desktop ); + void setDocument( SPDesktop* desktop, SPDocument* document); + +protected: + //virtual void _handleAction( int setId, int itemId ); + friend void sp_highlight_picker_color_mod(SPColorSelector *csel, GObject *cp); +private: + class ModelColumns; + class InternalUIBounce; + class ObjectWatcher; + + TagsPanel(TagsPanel const &); // no copy + TagsPanel &operator=(TagsPanel const &); // no assign + + void _styleButton( Gtk::Button& btn, SPDesktop *desktop, unsigned int code, char const* iconName, char const* tooltip ); + void _fireAction( unsigned int code ); + Gtk::MenuItem& _addPopupItem( SPDesktop *desktop, unsigned int code, char const* iconName, char const* fallback, int id ); + + bool _handleButtonEvent(GdkEventButton *event); + bool _handleKeyEvent(GdkEventKey *event); + + void _storeDragSource(const Gtk::TreeModel::iterator& iter); + bool _handleDragDrop(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time); + void _handleEdited(const Glib::ustring& path, const Glib::ustring& new_text); + void _handleEditingCancelled(); + + void _doTreeMove(); + void _renameObject(Gtk::TreeModel::Row row, const Glib::ustring& name); + + void _pushTreeSelectionToCurrent(); + void _selected_row_callback( const Gtk::TreeModel::iterator& iter ); + void _select_tag( SPTag * tag ); + + void _checkTreeSelection(); + + void _takeAction( int val ); + bool _executeAction(); + + void _setExpanded( const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path, bool isexpanded ); + + bool _noSelection( Glib::RefPtr<Gtk::TreeModel> const & model, Gtk::TreeModel::Path const & path, bool b ); + bool _rowSelectFunction( Glib::RefPtr<Gtk::TreeModel> const & model, Gtk::TreeModel::Path const & path, bool b ); + + void _updateObject(SPObject *obj); + bool _checkForUpdated(const Gtk::TreePath &path, const Gtk::TreeIter& iter, SPObject* obj); + + void _objectsSelected(Selection *sel); + bool _checkForSelected(const Gtk::TreePath& path, const Gtk::TreeIter& iter, SPObject* layer); + + void _objectsChanged(SPObject *root); + void _addObject( SPDocument* doc, SPObject* obj, Gtk::TreeModel::Row* parentRow ); + + void _checkForDeleted(const Gtk::TreeIter& iter, std::vector<SPObject *>* todelete); + +// std::vector<sigc::connection> groupConnections; + TagsPanel::ObjectWatcher* _rootWatcher; + std::vector<TagsPanel::ObjectWatcher*> _objectWatchers; + + // Hooked to the layer manager: + sigc::connection _documentChangedConnection; + sigc::connection _selectionChangedConnection; + + sigc::connection _changedConnection; + sigc::connection _addedConnection; + sigc::connection _removedConnection; + + // Internal + sigc::connection _selectedConnection; + sigc::connection _expandedConnection; + sigc::connection _collapsedConnection; + + DesktopTracker deskTrack; + SPDesktop* _desktop; + SPDocument* _document; + ModelColumns* _model; + InternalUIBounce* _pending; + gboolean _dnd_into; + std::vector<SPTag*> _dnd_source; + SPObject* _dnd_target; + + GdkEvent* _toggleEvent; + bool down_at_add; + + Gtk::TreeModel::Path _defer_target; + + Glib::RefPtr<Gtk::TreeStore> _store; + std::vector<Gtk::Widget*> _watching; + std::vector<Gtk::Widget*> _watchingNonTop; + std::vector<Gtk::Widget*> _watchingNonBottom; + + Gtk::TreeView _tree; + Gtk::CellRendererText *_text_renderer; + Gtk::TreeView::Column *_name_column; +#if WITH_GTKMM_3_0 + Gtk::Box _buttonsRow; + Gtk::Box _buttonsPrimary; + Gtk::Box _buttonsSecondary; +#else + Gtk::HBox _buttonsRow; + Gtk::HBox _buttonsPrimary; + Gtk::HBox _buttonsSecondary; +#endif + Gtk::ScrolledWindow _scroller; + Gtk::Menu _popupMenu; + Inkscape::UI::Widget::SpinButton _spinBtn; + Gtk::VBox _layersPage; + + sigc::connection desktopChangeConn; + +}; + + + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + + +#endif // SEEN_OBJECTS_PANEL_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/template-load-tab.cpp b/src/ui/dialog/template-load-tab.cpp index d75f81456..13cb01eef 100644 --- a/src/ui/dialog/template-load-tab.cpp +++ b/src/ui/dialog/template-load-tab.cpp @@ -24,7 +24,7 @@ #include "extension/db.h" #include "extension/effect.h" #include "inkscape.h" -#include "interface.h" +#include "ui/interface.h" #include "file.h" #include "path-prefix.h" #include "preferences.h" diff --git a/src/ui/dialog/text-edit.cpp b/src/ui/dialog/text-edit.cpp index 9996fc0f9..0f4a6f7f1 100644 --- a/src/ui/dialog/text-edit.cpp +++ b/src/ui/dialog/text-edit.cpp @@ -51,7 +51,7 @@ extern "C" { #include "ui/icon-names.h" #include "preferences.h" #include "verbs.h" -#include "interface.h" +#include "ui/interface.h" #include "svg/css-ostringstream.h" #include "widgets/icon.h" #include "widgets/font-selector.h" diff --git a/src/ui/dialog/transformation.cpp b/src/ui/dialog/transformation.cpp index a7f0b068e..3e135b9b2 100644 --- a/src/ui/dialog/transformation.cpp +++ b/src/ui/dialog/transformation.cpp @@ -47,13 +47,13 @@ namespace Inkscape { namespace UI { namespace Dialog { -static void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selection *selection, Transformation *daad) +static void on_selection_changed(InkscapeApplication */*inkscape*/, Inkscape::Selection *selection, Transformation *daad) { int page = daad->getCurrentPage(); daad->updateSelection((Inkscape::UI::Dialog::Transformation::PageType)page, selection); } -static void on_selection_modified( Inkscape::Application */*inkscape*/, +static void on_selection_modified( InkscapeApplication */*inkscape*/, Inkscape::Selection *selection, guint /*flags*/, Transformation *daad ) diff --git a/src/ui/dialog/xml-tree.cpp b/src/ui/dialog/xml-tree.cpp index c87e42633..7ab6c78ba 100644 --- a/src/ui/dialog/xml-tree.cpp +++ b/src/ui/dialog/xml-tree.cpp @@ -24,13 +24,13 @@ #include "desktop.h" #include "desktop-handles.h" -#include "dialogs/dialog-events.h" +#include "ui/dialog-events.h" #include "document.h" #include "document-undo.h" #include "ui/tools/tool-base.h" #include "helper/window.h" #include "inkscape.h" -#include "interface.h" +#include "ui/interface.h" #include "macros.h" #include "message-context.h" #include "message-stack.h" diff --git a/src/ui/draw-anchor.cpp b/src/ui/draw-anchor.cpp new file mode 100644 index 000000000..84c919018 --- /dev/null +++ b/src/ui/draw-anchor.cpp @@ -0,0 +1,105 @@ +/** \file + * Anchors implementation. + */ + +/* + * Authors: + * Copyright (C) 2000 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * Copyright (C) 2002 Lauris Kaplinski + * Copyright (C) 2004 Monash University + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#include "ui/draw-anchor.h" +#include "desktop.h" +#include "desktop-handles.h" +#include "ui/tools/tool-base.h" +#include "ui/tools/lpe-tool.h" +#include "display/sodipodi-ctrl.h" +#include "display/curve.h" +#include "ui/control-manager.h" + +using Inkscape::ControlManager; + +#define FILL_COLOR_NORMAL 0xffffff7f +#define FILL_COLOR_MOUSEOVER 0xff0000ff + +/** + * Creates an anchor object and initializes it. + */ +SPDrawAnchor *sp_draw_anchor_new(Inkscape::UI::Tools::FreehandBase *dc, SPCurve *curve, bool start, Geom::Point delta) +{ + if (SP_IS_LPETOOL_CONTEXT(dc)) { + // suppress all kinds of anchors in LPEToolContext + return NULL; + } + + SPDrawAnchor *a = g_new(SPDrawAnchor, 1); + + a->dc = dc; + a->curve = curve; + curve->ref(); + a->start = start; + a->active = FALSE; + a->dp = delta; + a->ctrl = ControlManager::getManager().createControl(sp_desktop_controls(&dc->getDesktop()), Inkscape::CTRL_TYPE_ANCHOR); + + SP_CTRL(a->ctrl)->moveto(delta); + + ControlManager::getManager().track(a->ctrl); + + return a; +} + +/** + * Destroys the anchor's canvas item and frees the anchor object. + */ +SPDrawAnchor *sp_draw_anchor_destroy(SPDrawAnchor *anchor) +{ + if (anchor->curve) { + anchor->curve->unref(); + } + if (anchor->ctrl) { + sp_canvas_item_destroy(anchor->ctrl); + } + g_free(anchor); + return NULL; +} + +/** + * Test if point is near anchor, if so fill anchor on canvas and return + * pointer to it or NULL. + */ +SPDrawAnchor *sp_draw_anchor_test(SPDrawAnchor *anchor, Geom::Point w, bool activate) +{ + SPCtrl *ctrl = SP_CTRL(anchor->ctrl); + + if ( activate && ( Geom::LInfty( w - anchor->dc->getDesktop().d2w(anchor->dp) ) <= (ctrl->box.width() / 2.0) ) ) { + if (!anchor->active) { + g_object_set(anchor->ctrl, "fill_color", FILL_COLOR_MOUSEOVER, NULL); + anchor->active = TRUE; + } + return anchor; + } + + if (anchor->active) { + g_object_set(anchor->ctrl, "fill_color", FILL_COLOR_NORMAL, NULL); + anchor->active = FALSE; + } + return 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/draw-anchor.h b/src/ui/draw-anchor.h new file mode 100644 index 000000000..1f7b55920 --- /dev/null +++ b/src/ui/draw-anchor.h @@ -0,0 +1,52 @@ +#ifndef SEEN_DRAW_ANCHOR_H +#define SEEN_DRAW_ANCHOR_H + +/** \file + * Drawing anchors. + */ + +#include <2geom/point.h> + +namespace Inkscape { +namespace UI { +namespace Tools { + +class FreehandBase; + +} +} +} + +class SPCurve; +struct SPCanvasItem; + +/// The drawing anchor. +/// \todo Make this a regular knot, this will allow to set statusbar tips. +struct SPDrawAnchor { + Inkscape::UI::Tools::FreehandBase *dc; + SPCurve *curve; + unsigned int start : 1; + unsigned int active : 1; + Geom::Point dp; + SPCanvasItem *ctrl; +}; + + +SPDrawAnchor *sp_draw_anchor_new(Inkscape::UI::Tools::FreehandBase *dc, SPCurve *curve, bool start, + Geom::Point delta); +SPDrawAnchor *sp_draw_anchor_destroy(SPDrawAnchor *anchor); +SPDrawAnchor *sp_draw_anchor_test(SPDrawAnchor *anchor, Geom::Point w, bool activate); + + +#endif /* !SEEN_DRAW_ANCHOR_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp new file mode 100644 index 000000000..7830a8de1 --- /dev/null +++ b/src/ui/interface.cpp @@ -0,0 +1,2230 @@ +/** + * @file + * Main UI stuff. + */ +/* Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Frank Felfe <innerspace@iname.com> + * bulia byak <buliabyak@users.sf.net> + * Jon A. Cruz <jon@joncruz.org> + * Abhishek Sharma + * Kris De Gussem <Kris.DeGussem@gmail.com> + * + * Copyright (C) 2012 Kris De Gussem + * Copyright (C) 2010 authors + * Copyright (C) 1999-2005 authors + * Copyright (C) 2004 David Turner + * Copyright (C) 2001-2002 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "ui/dialog/dialog-manager.h" +#include <gtkmm/icontheme.h> +#include "file.h" +#include <gtkmm/imagemenuitem.h> +#include <gtkmm/separatormenuitem.h> + +#include "inkscape-private.h" +#include "extension/db.h" +#include "extension/effect.h" +#include "extension/input.h" +#include "widgets/icon.h" +#include "preferences.h" +#include "path-prefix.h" +#include "shortcuts.h" +#include "document.h" +#include "desktop-handles.h" +#include "ui/interface.h" +#include "desktop.h" +#include "selection.h" +#include "selection-chemistry.h" +#include "svg-view-widget.h" +#include "widgets/desktop-widget.h" +#include "sp-item-group.h" +#include "sp-text.h" +#include "sp-gradient.h" +#include "sp-flowtext.h" +#include "sp-namedview.h" +#include "sp-root.h" +#include "ui/view/view.h" +#include "helper/action.h" +#include "helper/action-context.h" +#include "helper/gnome-utils.h" +#include "helper/window.h" +#include "io/sys.h" +#include "ui/dialog-events.h" +#include "message-context.h" +#include "ui/uxmanager.h" +#include "ui/clipboard.h" + +#include "display/sp-canvas.h" +#include "color.h" +#include "svg/svg-color.h" +#include "desktop-style.h" +#include "style.h" +#include "ui/tools/tool-base.h" +#include "gradient-drag.h" +#include "widgets/ege-paint-def.h" +#include "document-undo.h" +#include "sp-anchor.h" +#include "sp-clippath.h" +#include "sp-image.h" +#include "sp-item.h" +#include "sp-mask.h" +#include "message-stack.h" +#include "ui/dialog/layer-properties.h" + +#include <gdk/gdkkeysyms.h> + +#include <glibmm/miscutils.h> + +using Inkscape::DocumentUndo; + +/* Drag and Drop */ +typedef enum { + URI_LIST, + SVG_XML_DATA, + SVG_DATA, + PNG_DATA, + JPEG_DATA, + IMAGE_DATA, + APP_X_INKY_COLOR, + APP_X_COLOR, + APP_OSWB_COLOR, + APP_X_INK_PASTE +} ui_drop_target_info; + +static GtkTargetEntry ui_drop_target_entries [] = { + {(gchar *)"text/uri-list", 0, URI_LIST }, + {(gchar *)"image/svg+xml", 0, SVG_XML_DATA }, + {(gchar *)"image/svg", 0, SVG_DATA }, + {(gchar *)"image/png", 0, PNG_DATA }, + {(gchar *)"image/jpeg", 0, JPEG_DATA }, +#if ENABLE_MAGIC_COLORS + {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR}, +#endif // ENABLE_MAGIC_COLORS + {(gchar *)"application/x-oswb-color", 0, APP_OSWB_COLOR }, + {(gchar *)"application/x-color", 0, APP_X_COLOR }, + {(gchar *)"application/x-inkscape-paste", 0, APP_X_INK_PASTE } +}; + +static GtkTargetEntry *completeDropTargets = 0; +static int completeDropTargetsCount = 0; +static bool temporarily_block_actions = false; + +#define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0]) +static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries); +static void sp_ui_import_files(gchar *buffer); +static void sp_ui_import_one_file(char const *filename); +static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused); +static void sp_ui_drag_data_received(GtkWidget *widget, + GdkDragContext *drag_context, + gint x, gint y, + GtkSelectionData *data, + guint info, + guint event_time, + gpointer user_data); +static void sp_ui_drag_motion( GtkWidget *widget, + GdkDragContext *drag_context, + gint x, gint y, + GtkSelectionData *data, + guint info, + guint event_time, + gpointer user_data ); +static void sp_ui_drag_leave( GtkWidget *widget, + GdkDragContext *drag_context, + guint event_time, + gpointer user_data ); +static void sp_ui_menu_item_set_name(GtkWidget *data, + Glib::ustring const &name); +static void sp_recent_open(GtkRecentChooser *, gpointer); + +static void injectRenamedIcons(); + +static const int MIN_ONSCREEN_DISTANCE = 50; + +void +sp_create_window(SPViewWidget *vw, bool editable) +{ + g_return_if_fail(vw != NULL); + g_return_if_fail(SP_IS_VIEW_WIDGET(vw)); + + Gtk::Window *win = Inkscape::UI::window_new("", TRUE); + + gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw)); + gtk_widget_show(GTK_WIDGET(vw)); + + if (editable) { + g_object_set_data(G_OBJECT(vw), "window", win); + + SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw); + SPDesktop* desktop = desktop_widget->desktop; + + desktop_widget->window = win; + + win->set_data("desktop", desktop); + win->set_data("desktopwidget", desktop_widget); + + win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI)); + win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent)); + win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent)); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gint prefs_geometry = + (2==prefs->getInt("/options/savewindowgeometry/value", 0)); + if (prefs_geometry) { + gint pw = prefs->getInt("/desktop/geometry/width", -1); + gint ph = prefs->getInt("/desktop/geometry/height", -1); + gint px = prefs->getInt("/desktop/geometry/x", -1); + gint py = prefs->getInt("/desktop/geometry/y", -1); + gint full = prefs->getBool("/desktop/geometry/fullscreen"); + gint maxed = prefs->getBool("/desktop/geometry/maximized"); + if (pw>0 && ph>0) { + gint w = MIN(gdk_screen_width(), pw); + gint h = MIN(gdk_screen_height(), ph); + gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px); + gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py); + if (w>0 && h>0) { + x = MIN(gdk_screen_width() - w, x); + y = MIN(gdk_screen_height() - h, y); + desktop->setWindowSize(w, h); + } + + // Only restore xy for the first window so subsequent windows don't overlap exactly + // with first. (Maybe rule should be only restore xy if it's different from xy of + // other desktops?) + + // Empirically it seems that active_desktop==this desktop only the first time a + // desktop is created. + SPDesktop *active_desktop = SP_ACTIVE_DESKTOP; + if (active_desktop == desktop || active_desktop==NULL) { + desktop->setWindowPosition(Geom::Point(x, y)); + } + } + if (maxed) { + win->maximize(); + } + if (full) { + win->fullscreen(); + } + } + + } + + if ( completeDropTargets == 0 || completeDropTargetsCount == 0 ) + { + std::vector<gchar*> types; + + GSList *list = gdk_pixbuf_get_formats(); + while ( list ) { + int i = 0; + GdkPixbufFormat *one = (GdkPixbufFormat*)list->data; + gchar** typesXX = gdk_pixbuf_format_get_mime_types(one); + for ( i = 0; typesXX[i]; i++ ) { + types.push_back(g_strdup(typesXX[i])); + } + g_strfreev(typesXX); + + list = g_slist_next(list); + } + completeDropTargetsCount = nui_drop_target_entries + types.size(); + completeDropTargets = new GtkTargetEntry[completeDropTargetsCount]; + for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) { + completeDropTargets[i] = ui_drop_target_entries[i]; + } + int pos = nui_drop_target_entries; + + for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) { + completeDropTargets[pos].target = *it; + completeDropTargets[pos].flags = 0; + completeDropTargets[pos].info = IMAGE_DATA; + pos++; + } + } + + gtk_drag_dest_set((GtkWidget*)win->gobj(), + GTK_DEST_DEFAULT_ALL, + completeDropTargets, + completeDropTargetsCount, + GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE)); + + + g_signal_connect(G_OBJECT(win->gobj()), + "drag_data_received", + G_CALLBACK(sp_ui_drag_data_received), + NULL); + g_signal_connect(G_OBJECT(win->gobj()), + "drag_motion", + G_CALLBACK(sp_ui_drag_motion), + NULL); + g_signal_connect(G_OBJECT(win->gobj()), + "drag_leave", + G_CALLBACK(sp_ui_drag_leave), + NULL); + win->show(); + + // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet + if ( SP_IS_DESKTOP_WIDGET(vw) ) { + inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop); + } +} + +void +sp_ui_new_view() +{ + SPDocument *document; + SPViewWidget *dtw; + + document = SP_ACTIVE_DOCUMENT; + if (!document) return; + + dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL)); + g_return_if_fail(dtw != NULL); + + sp_create_window(dtw, TRUE); + sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view)); + sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view)); +} + +void sp_ui_new_view_preview() +{ + SPDocument *document = SP_ACTIVE_DOCUMENT; + if ( document ) { + SPViewWidget *dtw = reinterpret_cast<SPViewWidget *>(sp_svg_view_widget_new(document)); + g_return_if_fail(dtw != NULL); + SP_SVG_VIEW_WIDGET(dtw)->setResize(true, 400.0, 400.0); + + sp_create_window(dtw, FALSE); + } +} + +void +sp_ui_close_view(GtkWidget */*widget*/) +{ + SPDesktop *dt = SP_ACTIVE_DESKTOP; + + if (dt == NULL) { + return; + } + + if (dt->shutdown()) { + return; // Shutdown operation has been canceled, so do nothing + } + + // If closing the last document, open a new document so Inkscape doesn't quit. + std::list<SPDesktop *> desktops; + inkscape_get_all_desktops(desktops); + if (desktops.size() == 1) { + Glib::ustring templateUri = sp_file_default_template_uri(); + SPDocument *doc = SPDocument::createNewDoc( templateUri.c_str() , TRUE, true ); + // Set viewBox if it doesn't exist + if (!doc->getRoot()->viewBox_set) { + doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc->getDefaultUnit()), doc->getHeight().value(doc->getDefaultUnit()))); + } + dt->change_document(doc); + sp_namedview_window_from_document(dt); + sp_namedview_update_layers_from_document(dt); + return; + } + + // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP, + // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad) + dt->destroyWidget(); +} + + +unsigned int +sp_ui_close_all(void) +{ + /* Iterate through all the windows, destroying each in the order they + become active */ + while (SP_ACTIVE_DESKTOP) { + SPDesktop *dt = SP_ACTIVE_DESKTOP; + if (dt->shutdown()) { + /* The user canceled the operation, so end doing the close */ + return FALSE; + } + // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP, + // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad) + dt->destroyWidget(); + } + + return TRUE; +} + +/* + * Some day when the right-click menus are ready to start working + * smarter with the verbs, we'll need to change this NULL being + * sent to sp_action_perform to something useful, or set some kind + * of global "right-clicked position" variable for actions to + * investigate when they're called. + */ +static void +sp_ui_menu_activate(void */*object*/, SPAction *action) +{ + if (!temporarily_block_actions) { + sp_action_perform(action, NULL); + } +} + +static void +sp_ui_menu_select_action(void */*object*/, SPAction *action) +{ + sp_action_get_view(action)->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip); +} + +static void +sp_ui_menu_deselect_action(void */*object*/, SPAction *action) +{ + sp_action_get_view(action)->tipsMessageContext()->clear(); +} + +static void +sp_ui_menu_select(gpointer object, gpointer tip) +{ + Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view")); + view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip); +} + +static void +sp_ui_menu_deselect(gpointer object) +{ + Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view")); + view->tipsMessageContext()->clear(); +} + +/** + * Creates and attaches a scaled icon to the given menu item. + */ +static void +sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name ) +{ + static bool iconsInjected = false; + if ( !iconsInjected ) { + iconsInjected = true; + injectRenamedIcons(); + } + GtkWidget *icon; + + icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name ); + gtk_widget_show(icon); + gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon); +} // end of sp_ui_menu_add_icon + +void +sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c) +{ + SPAction *action; + unsigned int shortcut; + gchar *s; + gchar *atitle; + + action = verb->get_action(Inkscape::ActionContext()); + if (!action) + return; + + atitle = sp_action_get_title(action); + + s = g_stpcpy(c, atitle); + + g_free(atitle); + + shortcut = sp_shortcut_get_primary(verb); + if (shortcut!=GDK_KEY_VoidSymbol) { + gchar* key = sp_shortcut_get_label(shortcut); + s = g_stpcpy(s, " ("); + s = g_stpcpy(s, key); + s = g_stpcpy(s, ")"); + g_free(key); + } +} + + +/** + * Appends a custom menu UI from a verb. + * + * @see ContextMenu::AppendItemFromVerb for a c++ified alternative. Consider dropping sp_ui_menu_append_item_from_verb when c++ifying interface.cpp. + */ +static GtkWidget *sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL) +{ + SPAction *action; + GtkWidget *item; + + if (verb->get_code() == SP_VERB_NONE) { + + item = gtk_separator_menu_item_new(); + + } else { + + action = verb->get_action(Inkscape::ActionContext(view)); + if (!action) return NULL; + + if (radio) { + item = gtk_radio_menu_item_new_with_mnemonic(group, action->name); + } else { + item = gtk_image_menu_item_new_with_mnemonic(action->name); + } + + gtk_label_set_markup_with_mnemonic( GTK_LABEL(gtk_bin_get_child(GTK_BIN (item))), action->name); + + GtkAccelGroup *accel_group = sp_shortcut_get_accel_group(); + gtk_menu_set_accel_group(menu, accel_group); + + sp_shortcut_add_accelerator(item, sp_shortcut_get_primary(verb)); + + action->signal_set_sensitive.connect( + sigc::bind<0>( + sigc::ptr_fun(>k_widget_set_sensitive), + item)); + action->signal_set_name.connect( + sigc::bind<0>( + sigc::ptr_fun(&sp_ui_menu_item_set_name), + item)); + + if (!action->sensitive) { + gtk_widget_set_sensitive(item, FALSE); + } + + if (action->image) { + sp_ui_menuitem_add_icon(item, action->image); + } + gtk_widget_set_events(item, GDK_KEY_PRESS_MASK); + g_object_set_data(G_OBJECT(item), "view", (gpointer) view); + g_signal_connect( G_OBJECT(item), "activate", G_CALLBACK(sp_ui_menu_activate), action ); + g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action ); + g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action ); + } + + gtk_widget_show(item); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + return item; + +} // end of sp_ui_menu_append_item_from_verb + + +Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view ) +{ + Glib::ustring prefPath; + + if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) { + prefPath = "/focus/"; + } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) { + prefPath = "/fullscreen/"; + } else { + prefPath = "/window/"; + } + + return prefPath; +} + +static void +checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data) +{ + gchar const *pref = (gchar const *) user_data; + Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view"); + SPAction *action = (SPAction *) g_object_get_data(G_OBJECT(menuitem), "action"); + + if (action) { + + sp_ui_menu_activate(menuitem, action); + + } else if (pref) { + // All check menu items should have actions now, but just in case + Glib::ustring pref_path = getLayoutPrefPath( view ); + pref_path += pref; + pref_path += "/state"; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gboolean checked = gtk_check_menu_item_get_active(menuitem); + prefs->setBool(pref_path, checked); + + reinterpret_cast<SPDesktop*>(view)->layoutWidget(); + } +} + +static bool getViewStateFromPref(Inkscape::UI::View::View *view, gchar const *pref) +{ + Glib::ustring pref_path = getLayoutPrefPath( view ); + pref_path += pref; + pref_path += "/state"; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + return prefs->getBool(pref_path, true); +} + +#if GTK_CHECK_VERSION(3,0,0) +static gboolean checkitem_update(GtkWidget *widget, cairo_t * /*cr*/, gpointer user_data) +#else +static gboolean checkitem_update(GtkWidget *widget, GdkEventExpose * /*event*/, gpointer user_data) +#endif +{ + GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget); + + gchar const *pref = (gchar const *) user_data; + Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view"); + SPAction *action = (SPAction *) g_object_get_data(G_OBJECT(menuitem), "action"); + SPDesktop *dt = static_cast<SPDesktop*>(view); + + bool ison = false; + if (action) { + + if (!strcmp(action->id, "ToggleGrid")) { + ison = dt->gridsEnabled(); + } + else if (!strcmp(action->id, "ToggleGuides")) { + ison = dt->namedview->getGuides(); + } + else if (!strcmp(action->id, "ViewCmsToggle")) { + ison = dt->colorProfAdjustEnabled(); + } + else { + ison = getViewStateFromPref(view, pref); + } + } else if (pref) { + // The Show/Hide menu items without actions + ison = getViewStateFromPref(view, pref); + } + + g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data); + gtk_check_menu_item_set_active(menuitem, ison); + g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data); + + return FALSE; +} + + +static void taskToggled(GtkCheckMenuItem *menuitem, gpointer userData) +{ + if ( gtk_check_menu_item_get_active(menuitem) ) { + gint taskNum = GPOINTER_TO_INT(userData); + taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum; + + Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view"); + + // note: this will change once more options are in the task set support: + Inkscape::UI::UXManager::getInstance()->setTask( dynamic_cast<SPDesktop*>(view), taskNum ); + } +} + + +/** + * Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline) and Color display mode. + */ +#if GTK_CHECK_VERSION(3,0,0) +static gboolean update_view_menu(GtkWidget *widget, cairo_t * /*cr*/, gpointer user_data) +#else +static gboolean update_view_menu(GtkWidget *widget, GdkEventExpose * /*event*/, gpointer user_data) +#endif +{ + SPAction *action = (SPAction *) user_data; + g_assert(action->id != NULL); + + Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(widget), "view"); + SPDesktop *dt = static_cast<SPDesktop*>(view); + Inkscape::RenderMode mode = dt->getMode(); + Inkscape::ColorMode colormode = dt->getColorMode(); + + bool new_state = false; + if (!strcmp(action->id, "ViewModeNormal")) { + new_state = mode == Inkscape::RENDERMODE_NORMAL; + } else if (!strcmp(action->id, "ViewModeNoFilters")) { + new_state = mode == Inkscape::RENDERMODE_NO_FILTERS; + } else if (!strcmp(action->id, "ViewModeOutline")) { + new_state = mode == Inkscape::RENDERMODE_OUTLINE; + } else if (!strcmp(action->id, "ViewColorModeNormal")) { + new_state = colormode == Inkscape::COLORMODE_NORMAL; + } else if (!strcmp(action->id, "ViewColorModeGrayscale")) { + new_state = colormode == Inkscape::COLORMODE_GRAYSCALE; + } else if (!strcmp(action->id, "ViewColorModePrintColorsPreview")) { + new_state = colormode == Inkscape::COLORMODE_PRINT_COLORS_PREVIEW; + } else { + g_warning("update_view_menu does not handle this verb"); + } + + if (new_state) { //only one of the radio buttons has to be activated; the others will automatically be deactivated + if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))) { + // When the GtkMenuItem version of the "activate" signal has been emitted by a GtkRadioMenuItem, there is a second + // emission as the most recently active item is toggled to inactive. This is dealt with before the original signal is handled. + // This emission however should not invoke any actions, hence we block it here: + temporarily_block_actions = true; + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE); + temporarily_block_actions = false; + } + } + + return FALSE; +} + +static void +sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref, + void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data), +#if GTK_CHECK_VERSION(3,0,0) + gboolean (*callback_update)(GtkWidget *widget, cairo_t *cr, gpointer user_data), +#else + gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data), +#endif + Inkscape::Verb *verb) +{ + unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0; + SPAction *action = (verb) ? verb->get_action(Inkscape::ActionContext(view)) : 0; + GtkWidget *item = gtk_check_menu_item_new_with_mnemonic(action ? action->name : label); + +#if 0 + if (!action->sensitive) { + gtk_widget_set_sensitive(item, FALSE); + } +#endif + + sp_shortcut_add_accelerator(item, shortcut); + + gtk_widget_show(item); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + g_object_set_data(G_OBJECT(item), "view", (gpointer) view); + g_object_set_data(G_OBJECT(item), "action", (gpointer) action); + + g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref); + +#if GTK_CHECK_VERSION(3,0,0) + g_signal_connect( G_OBJECT(item), "draw", (GCallback) callback_update, (void *) pref); +#else + g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref); +#endif + + (*callback_update)(item, NULL, (void *)pref); + + g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip)); + g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL); + +} + +static void +sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/) +{ + // dealing with the bizarre filename convention in Inkscape for now + gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu)); + gchar *local_fn = g_filename_from_uri(uri, NULL, NULL); + gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL); + sp_file_open(utf8_fn, NULL); + g_free(utf8_fn); + g_free(local_fn); + g_free(uri); +} + +static void +sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view) +{ + //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu", + // checkitem_toggled, checkitem_update, 0); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "commands", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_COMMANDS_TOOLBAR)); + sp_ui_menu_append_check_item_from_verb(m, view,NULL, NULL, "snaptoolbox", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SNAP_TOOLBAR)); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "toppanel", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_TOOL_TOOLBAR)); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "toolbox", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_TOOLBOX)); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS)); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS)); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "panels", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_PALETTE)); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "statusbar", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_STATUSBAR)); +} + + +static void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view) +{ + gchar const* data[] = { + C_("Interface setup", "Default"), _("Default interface setup"), + C_("Interface setup", "Custom"), _("Setup for custom task"), + C_("Interface setup", "Wide"), _("Setup for widescreen work"), + 0, 0 + }; + + GSList *group = 0; + int count = 0; + gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast<SPDesktop*>(view) ); + for (gchar const **strs = data; strs[0]; strs += 2, count++) + { + GtkWidget *item = gtk_radio_menu_item_new_with_label( group, strs[0] ); + group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) ); + if ( count == active ) + { + gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), TRUE ); + } + + g_object_set_data( G_OBJECT(item), "view", view ); + g_signal_connect( G_OBJECT(item), "toggled", reinterpret_cast<GCallback>(taskToggled), GINT_TO_POINTER(count) ); + g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast<gchar*>(strs[1]) ); + g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), 0 ); + + gtk_widget_show( item ); + gtk_menu_shell_append( GTK_MENU_SHELL(menu), item ); + } +} + + +/** + * Observer that updates the recent list's max document count. + */ +class MaxRecentObserver : public Inkscape::Preferences::Observer { +public: + MaxRecentObserver(GtkWidget *recent_menu) : + Observer("/options/maxrecentdocuments/value"), + _rm(recent_menu) + {} + virtual void notify(Inkscape::Preferences::Entry const &e) { + gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt()); + // hack: the recent menu doesn't repopulate after changing the limit, so we force it + g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed"); + } +private: + GtkWidget *_rm; +}; + +/** + * This function turns XML into a menu. + * + * This function is realitively simple as it just goes through the XML + * and parses the individual elements. In the case of a submenu, it + * just calls itself recursively. Because it is only reasonable to have + * a couple of submenus, it is unlikely this will go more than two or + * three times. + * + * In the case of an unrecognized verb, a menu item is made to identify + * the verb that is missing, and display that. The menu item is also made + * insensitive. + * + * @param menus This is the XML that defines the menu + * @param menu Menu to be added to + * @param view The View that this menu is being built for + */ +static void sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view) +{ + if (menus == NULL) return; + if (menu == NULL) return; + GSList *group = NULL; + + for (Inkscape::XML::Node *menu_pntr = menus; + menu_pntr != NULL; + menu_pntr = menu_pntr->next()) { + if (!strcmp(menu_pntr->name(), "submenu")) { + GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name"))); + GtkWidget *submenu = gtk_menu_new(); + sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + continue; + } + if (!strcmp(menu_pntr->name(), "verb")) { + gchar const *verb_name = menu_pntr->attribute("verb-id"); + Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name); + + if (verb != NULL) { + if (menu_pntr->attribute("radio") != NULL) { + GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group); + group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item)); + if (menu_pntr->attribute("default") != NULL) { + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE); + } + if (verb->get_code() != SP_VERB_NONE) { + SPAction *action = verb->get_action(Inkscape::ActionContext(view)); +#if GTK_CHECK_VERSION(3,0,0) + g_signal_connect( G_OBJECT(item), "draw", (GCallback) update_view_menu, (void *) action); +#else + g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) update_view_menu, (void *) action); +#endif + } + } else if (menu_pntr->attribute("check") != NULL) { + SPAction *action = NULL; + if (verb->get_code() != SP_VERB_NONE) { + action = verb->get_action(Inkscape::ActionContext(view)); + } + sp_ui_menu_append_check_item_from_verb(GTK_MENU(menu), view, action->name, action->tip, NULL, + checkitem_toggled, checkitem_update, verb); + + } else { + sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view); + group = NULL; + } + } else { + gchar string[120]; + g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name); + string[119] = '\0'; /* may not be terminated */ + GtkWidget *item = gtk_menu_item_new_with_label(string); + gtk_widget_set_sensitive(item, false); + gtk_widget_show(item); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } + continue; + } + if (!strcmp(menu_pntr->name(), "separator") + // This was spelt wrong in the original version + // and so this is for backward compatibility. It can + // probably be dropped after the 0.44 release. + || !strcmp(menu_pntr->name(), "seperator")) { + GtkWidget *item = gtk_separator_menu_item_new(); + gtk_widget_show(item); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + continue; + } + + if (!strcmp(menu_pntr->name(), "recent-file-list")) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + // create recent files menu + int max_recent = prefs->getInt("/options/maxrecentdocuments/value"); + GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default()); + gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent); + // sort most recently used documents first to preserve previous behavior + gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU); + g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL); + + // add filter to only open files added by Inkscape + GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new(); + gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname()); + gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter); + + gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE); + gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE); + + GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent")); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(recent_item)); + // this will just sit and update the list's item count + static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu); + prefs->addObserver(*mro); + continue; + } + if (!strcmp(menu_pntr->name(), "objects-checkboxes")) { + sp_ui_checkboxes_menus(GTK_MENU(menu), view); + continue; + } + if (!strcmp(menu_pntr->name(), "task-checkboxes")) { + addTaskMenuItems(GTK_MENU(menu), view); + continue; + } + } +} + +GtkWidget *sp_ui_main_menubar(Inkscape::UI::View::View *view) +{ + GtkWidget *mbar = gtk_menu_bar_new(); + sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view); + return mbar; +} + + +/* Drag and Drop */ +void +sp_ui_drag_data_received(GtkWidget *widget, + GdkDragContext *drag_context, + gint x, gint y, + GtkSelectionData *data, + guint info, + guint /*event_time*/, + gpointer /*user_data*/) +{ + SPDocument *doc = SP_ACTIVE_DOCUMENT; + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + switch (info) { +#if ENABLE_MAGIC_COLORS + case APP_X_INKY_COLOR: + { + int destX = 0; + int destY = 0; + gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY ); + Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) ); + + SPItem *item = desktop->getItemAtPoint( where, true ); + if ( item ) + { + bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE); + + if ( data->length >= 8 ) { + cmsHPROFILE srgbProf = cmsCreate_sRGBProfile(); + + gchar c[64] = {0}; + // Careful about endian issues. + guint16* dataVals = (guint16*)data->data; + sp_svg_write_color( c, sizeof(c), + SP_RGBA32_U_COMPOSE( + 0x0ff & (dataVals[0] >> 8), + 0x0ff & (dataVals[1] >> 8), + 0x0ff & (dataVals[2] >> 8), + 0xff // can't have transparency in the color itself + //0x0ff & (data->data[3] >> 8), + )); + SPCSSAttr *css = sp_repr_css_attr_new(); + bool updatePerformed = false; + + if ( data->length > 14 ) { + int flags = dataVals[4]; + + // piggie-backed palette entry info + int index = dataVals[5]; + Glib::ustring palName; + for ( int i = 0; i < dataVals[6]; i++ ) { + palName += (gunichar)dataVals[7+i]; + } + + // Now hook in a magic tag of some sort. + if ( !palName.empty() && (flags & 1) ) { + gchar* str = g_strdup_printf("%d|", index); + palName.insert( 0, str ); + g_free(str); + str = 0; + + item->setAttribute( + fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag", + palName.c_str(), + false ); + item->updateRepr(); + + sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c ); + updatePerformed = true; + } + } + + if ( !updatePerformed ) { + sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c ); + } + + sp_desktop_apply_css_recursive( item, css, true ); + item->updateRepr(); + + SPDocumentUndo::done( doc , SP_VERB_NONE, + _("Drop color")); + + if ( srgbProf ) { + cmsCloseProfile( srgbProf ); + } + } + } + } + break; +#endif // ENABLE_MAGIC_COLORS + + case APP_X_COLOR: + { + int destX = 0; + int destY = 0; + gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY ); + Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) ); + Geom::Point const button_dt(desktop->w2d(where)); + Geom::Point const button_doc(desktop->dt2doc(button_dt)); + + if ( gtk_selection_data_get_length (data) == 8 ) { + gchar colorspec[64] = {0}; + // Careful about endian issues. + guint16* dataVals = (guint16*)gtk_selection_data_get_data (data); + sp_svg_write_color( colorspec, sizeof(colorspec), + SP_RGBA32_U_COMPOSE( + 0x0ff & (dataVals[0] >> 8), + 0x0ff & (dataVals[1] >> 8), + 0x0ff & (dataVals[2] >> 8), + 0xff // can't have transparency in the color itself + //0x0ff & (data->data[3] >> 8), + )); + + SPItem *item = desktop->getItemAtPoint( where, true ); + + bool consumed = false; + if (desktop->event_context && desktop->event_context->get_drag()) { + consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt); + if (consumed) { + DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") ); + desktop->event_context->get_drag()->updateDraggers(); + } + } + + //if (!consumed && tools_active(desktop, TOOLS_TEXT)) { + // consumed = sp_text_context_drop_color(c, button_doc); + // if (consumed) { + // SPDocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient stop")); + // } + //} + + if (!consumed && item) { + bool fillnotstroke = (gdk_drag_context_get_actions (drag_context) != GDK_ACTION_MOVE); + if (fillnotstroke && + (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) { + Path *livarot_path = Path_for_item(item, true, true); + livarot_path->ConvertWithBackData(0.04); + + boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc); + if (position) { + Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t); + Geom::Point delta = nearest - button_doc; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + delta = desktop->d2w(delta); + double stroke_tolerance = + ( !item->style->stroke.isNone() ? + desktop->current_zoom() * + item->style->stroke_width.computed * + item->i2dt_affine().descrim() * 0.5 + : 0.0) + + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + + if (Geom::L2 (delta) < stroke_tolerance) { + fillnotstroke = false; + } + } + delete livarot_path; + } + + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec ); + + sp_desktop_apply_css_recursive( item, css, true ); + item->updateRepr(); + + DocumentUndo::done( doc , SP_VERB_NONE, + _("Drop color") ); + } + } + } + break; + + case APP_OSWB_COLOR: + { + bool worked = false; + Glib::ustring colorspec; + if ( gtk_selection_data_get_format (data) == 8 ) { + ege::PaintDef color; + worked = color.fromMIMEData("application/x-oswb-color", + reinterpret_cast<char const *>(gtk_selection_data_get_data (data)), + gtk_selection_data_get_length (data), + gtk_selection_data_get_format (data)); + if ( worked ) { + if ( color.getType() == ege::PaintDef::CLEAR ) { + colorspec = ""; // TODO check if this is sufficient + } else if ( color.getType() == ege::PaintDef::NONE ) { + colorspec = "none"; + } else { + unsigned int r = color.getR(); + unsigned int g = color.getG(); + unsigned int b = color.getB(); + + SPGradient* matches = 0; + const GSList *gradients = doc->getResourceList("gradient"); + for (const GSList *item = gradients; item; item = item->next) { + SPGradient* grad = SP_GRADIENT(item->data); + if ( color.descr == grad->getId() ) { + if ( grad->hasStops() ) { + matches = grad; + break; + } + } + } + if (matches) { + colorspec = "url(#"; + colorspec += matches->getId(); + colorspec += ")"; + } else { + gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b); + colorspec = tmp; + g_free(tmp); + } + } + } + } + if ( worked ) { + int destX = 0; + int destY = 0; + gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY ); + Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) ); + Geom::Point const button_dt(desktop->w2d(where)); + Geom::Point const button_doc(desktop->dt2doc(button_dt)); + + SPItem *item = desktop->getItemAtPoint( where, true ); + + bool consumed = false; + if (desktop->event_context && desktop->event_context->get_drag()) { + consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt); + if (consumed) { + DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") ); + desktop->event_context->get_drag()->updateDraggers(); + } + } + + if (!consumed && item) { + bool fillnotstroke = (gdk_drag_context_get_actions (drag_context) != GDK_ACTION_MOVE); + if (fillnotstroke && + (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) { + Path *livarot_path = Path_for_item(item, true, true); + livarot_path->ConvertWithBackData(0.04); + + boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc); + if (position) { + Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t); + Geom::Point delta = nearest - button_doc; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + delta = desktop->d2w(delta); + double stroke_tolerance = + ( !item->style->stroke.isNone() ? + desktop->current_zoom() * + item->style->stroke_width.computed * + item->i2dt_affine().descrim() * 0.5 + : 0.0) + + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + + if (Geom::L2 (delta) < stroke_tolerance) { + fillnotstroke = false; + } + } + delete livarot_path; + } + + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() ); + + sp_desktop_apply_css_recursive( item, css, true ); + item->updateRepr(); + + DocumentUndo::done( doc , SP_VERB_NONE, + _("Drop color") ); + } + } + } + break; + + case SVG_DATA: + case SVG_XML_DATA: { + gchar *svgdata = (gchar *)gtk_selection_data_get_data (data); + + Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, gtk_selection_data_get_length (data), SP_SVG_NS_URI); + + if (rnewdoc == NULL) { + sp_ui_error_dialog(_("Could not parse SVG data")); + return; + } + + Inkscape::XML::Node *repr = rnewdoc->root(); + gchar const *style = repr->attribute("style"); + + Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g"); + newgroup->setAttribute("style", style); + + Inkscape::XML::Document * xml_doc = doc->getReprDoc(); + for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) { + Inkscape::XML::Node *newchild = child->duplicate(xml_doc); + newgroup->appendChild(newchild); + } + + Inkscape::GC::release(rnewdoc); + + // Add it to the current layer + + // Greg's edits to add intelligent positioning of svg drops + SPObject *new_obj = NULL; + new_obj = desktop->currentLayer()->appendChildRepr(newgroup); + + Inkscape::Selection *selection = sp_desktop_selection(desktop); + selection->set(SP_ITEM(new_obj)); + + // move to mouse pointer + { + sp_desktop_document(desktop)->ensureUpToDate(); + Geom::OptRect sel_bbox = selection->visualBounds(); + if (sel_bbox) { + Geom::Point m( desktop->point() - sel_bbox->midpoint() ); + sp_selection_move_relative(selection, m, false); + } + } + + Inkscape::GC::release(newgroup); + DocumentUndo::done( doc, SP_VERB_NONE, + _("Drop SVG") ); + break; + } + + case URI_LIST: { + gchar *uri = (gchar *)gtk_selection_data_get_data (data); + sp_ui_import_files(uri); + break; + } + + case APP_X_INK_PASTE: { + Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); + cm->paste(desktop); + DocumentUndo::done( doc, SP_VERB_NONE, _("Drop Symbol") ); + break; + } + + case PNG_DATA: + case JPEG_DATA: + case IMAGE_DATA: { + const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png"); + + Inkscape::Extension::DB::InputList o; + Inkscape::Extension::db.get_input_list(o); + Inkscape::Extension::DB::InputList::const_iterator i = o.begin(); + while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) { + ++i; + } + Inkscape::Extension::Extension *ext = *i; + bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0); + ext->set_param_optiongroup("link", "embed"); + ext->set_gui(false); + + gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-dnd-import", NULL ); + g_file_set_contents(filename, + reinterpret_cast<gchar const *>(gtk_selection_data_get_data (data)), + gtk_selection_data_get_length (data), + NULL); + file_import(doc, filename, ext); + g_free(filename); + + ext->set_param_optiongroup("link", save ? "embed" : "link"); + ext->set_gui(true); + DocumentUndo::done( doc , SP_VERB_NONE, + _("Drop bitmap image") ); + break; + } + } +} + +#include "ui/tools/gradient-tool.h" + +void sp_ui_drag_motion( GtkWidget */*widget*/, + GdkDragContext */*drag_context*/, + gint /*x*/, gint /*y*/, + GtkSelectionData */*data*/, + guint /*info*/, + guint /*event_time*/, + gpointer /*user_data*/) +{ +// SPDocument *doc = SP_ACTIVE_DOCUMENT; +// SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + +// g_message("drag-n-drop motion (%4d, %4d) at %d", x, y, event_time); +} + +static void sp_ui_drag_leave( GtkWidget */*widget*/, + GdkDragContext */*drag_context*/, + guint /*event_time*/, + gpointer /*user_data*/ ) +{ +// g_message("drag-n-drop leave at %d", event_time); +} + +static void +sp_ui_import_files(gchar *buffer) +{ + GList *list = gnome_uri_list_extract_filenames(buffer); + if (!list) + return; + g_list_foreach(list, sp_ui_import_one_file_with_check, NULL); + g_list_foreach(list, (GFunc) g_free, NULL); + g_list_free(list); +} + +static void +sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/) +{ + if (filename) { + if (strlen((char const *)filename) > 2) + sp_ui_import_one_file((char const *)filename); + } +} + +static void +sp_ui_import_one_file(char const *filename) +{ + SPDocument *doc = SP_ACTIVE_DOCUMENT; + if (!doc) return; + + if (filename == NULL) return; + + // Pass off to common implementation + // TODO might need to get the proper type of Inkscape::Extension::Extension + file_import( doc, filename, NULL ); +} + +void +sp_ui_error_dialog(gchar const *message) +{ + GtkWidget *dlg; + gchar *safeMsg = Inkscape::IO::sanitizeString(message); + + dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, "%s", safeMsg); + sp_transientize(dlg); + gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE); + gtk_dialog_run(GTK_DIALOG(dlg)); + gtk_widget_destroy(dlg); + g_free(safeMsg); +} + +bool +sp_ui_overwrite_file(gchar const *filename) +{ + bool return_value = FALSE; + + if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) { + Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel(); + gchar* baseName = g_path_get_basename( filename ); + gchar* dirName = g_path_get_dirname( filename ); + GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(), + (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n" + "The file already exists in \"%s\". Replacing it will overwrite its contents." ), + baseName, + dirName + ); + gtk_dialog_add_buttons( GTK_DIALOG(dialog), + _("_Cancel"), GTK_RESPONSE_NO, + _("Replace"), GTK_RESPONSE_YES, + NULL ); + gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES ); + + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) { + return_value = TRUE; + } else { + return_value = FALSE; + } + gtk_widget_destroy(dialog); + g_free( baseName ); + g_free( dirName ); + } else { + return_value = TRUE; + } + + return return_value; +} + +static void +sp_ui_menu_item_set_name(GtkWidget *data, Glib::ustring const &name) +{ + if (data || GTK_IS_BIN (data)) { + void *child = gtk_bin_get_child (GTK_BIN (data)); + //child is either + //- a GtkBox, whose first child is a label displaying name if the menu + //item has an accel key + //- a GtkLabel if the menu has no accel key + if (child != NULL){ + if (GTK_IS_LABEL(child)) { + gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str()); + } else if (GTK_IS_BOX(child)) { + gtk_label_set_markup_with_mnemonic( + GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), + name.c_str()); + }//else sp_ui_menu_append_item_from_verb has been modified and can set + //a menu item in yet another way... + } + } +} + +void injectRenamedIcons() +{ + Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); + + std::vector< std::pair<Glib::ustring, Glib::ustring> > renamed; + renamed.push_back(std::make_pair("gtk-file", "document-x-generic")); + renamed.push_back(std::make_pair("gtk-directory", "folder")); + + for ( std::vector< std::pair<Glib::ustring, Glib::ustring> >::iterator it = renamed.begin(); it < renamed.end(); ++it ) { + bool hasIcon = iconTheme->has_icon(it->first); + bool hasSecondIcon = iconTheme->has_icon(it->second); + + if ( !hasIcon && hasSecondIcon ) { + Glib::ArrayHandle<int> sizes = iconTheme->get_icon_sizes(it->second); + for ( Glib::ArrayHandle<int>::iterator it2 = sizes.begin(); it2 < sizes.end(); ++it2 ) { + Glib::RefPtr<Gdk::Pixbuf> pb = iconTheme->load_icon( it->second, *it2 ); + if ( pb ) { + // install a private copy of the pixbuf to avoid pinning a theme + Glib::RefPtr<Gdk::Pixbuf> pbCopy = pb->copy(); + Gtk::IconTheme::add_builtin_icon( it->first, *it2, pbCopy ); + } + } + } + } +} + + +ContextMenu::ContextMenu(SPDesktop *desktop, SPItem *item) : + _item(item), + MIGroup(), + MIParent(_("Go to parent")) +{ +// g_message("ContextMenu"); + _object = static_cast<SPObject *>(item); + _desktop = desktop; + + AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_UNDO)); + AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_REDO)); + AddSeparator(); + AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_CUT)); + AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_COPY)); + AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_PASTE)); + AddSeparator(); + AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE)); + AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_DELETE)); + + positionOfLastDialog = 10; // 9 in front + 1 for the separator in the next if; used to position the dialog menu entries below each other + + /* Item menu */ + if (item!=NULL) { + AddSeparator(); + MakeObjectMenu(); + } + + /* layer menu */ + SPGroup *group=NULL; + if (item) { + if (SP_IS_GROUP(item)) { + group = SP_GROUP(item); + } else if ( item != _desktop->currentRoot() && SP_IS_GROUP(item->parent) ) { + group = SP_GROUP(item->parent); + } + } + + if (( group && group != _desktop->currentLayer() ) || + ( _desktop->currentLayer() != _desktop->currentRoot() && _desktop->currentLayer()->parent != _desktop->currentRoot() ) ) { + AddSeparator(); + } + + if ( group && group != _desktop->currentLayer() ) { + /* TRANSLATORS: #%1 is the id of the group e.g. <g id="#g7">, not a number. */ + MIGroup.set_label (Glib::ustring::compose(_("Enter group #%1"), group->getId())); + MIGroup.set_data("group", group); + MIGroup.signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &ContextMenu::EnterGroup),&MIGroup)); + MIGroup.show(); + append(MIGroup); + } + + if ( _desktop->currentLayer() != _desktop->currentRoot() ) { + if ( _desktop->currentLayer()->parent != _desktop->currentRoot() ) { + MIParent.signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::LeaveGroup)); + MIParent.show(); + append(MIParent); + } + } +} + +ContextMenu::~ContextMenu(void) +{ +} + +Gtk::SeparatorMenuItem* ContextMenu::AddSeparator(void) +{ + Gtk::SeparatorMenuItem* sep = Gtk::manage(new Gtk::SeparatorMenuItem()); + sep->show(); + append(*sep); + return sep; +} + +void ContextMenu::EnterGroup(Gtk::MenuItem* mi) +{ + _desktop->setCurrentLayer(reinterpret_cast<SPObject *>(mi->get_data("group"))); + _desktop->selection->clear(); +} + +void ContextMenu::LeaveGroup(void) +{ + _desktop->setCurrentLayer(_desktop->currentLayer()->parent); +} + +void ContextMenu::AppendItemFromVerb(Inkscape::Verb *verb)//, SPDesktop *view)//, bool radio, GSList *group) +{ + SPAction *action; + SPDesktop *view = _desktop; + + if (verb->get_code() == SP_VERB_NONE) { + Gtk::MenuItem *item = AddSeparator(); + item->show(); + append(*item); + } else { + action = verb->get_action(Inkscape::ActionContext(view)); + if (!action) { + return; + } + + Gtk::ImageMenuItem *item = Gtk::manage(new Gtk::ImageMenuItem(action->name, true)); + + sp_shortcut_add_accelerator(GTK_WIDGET(item->gobj()), sp_shortcut_get_primary(verb)); + + action->signal_set_sensitive.connect(sigc::mem_fun(*this, &ContextMenu::set_sensitive)); + action->signal_set_name.connect(sigc::mem_fun(*item, &ContextMenu::set_name)); + + if (!action->sensitive) { + item->set_sensitive(FALSE); + } + + if (action->image) { + sp_ui_menuitem_add_icon((GtkWidget*)item->gobj(), action->image); + } + item->set_events(Gdk::KEY_PRESS_MASK); + item->signal_activate().connect(sigc::bind(sigc::ptr_fun(sp_ui_menu_activate),item,action)); + item->signal_select().connect(sigc::bind(sigc::ptr_fun(sp_ui_menu_select_action),item,action)); + item->signal_deselect().connect(sigc::bind(sigc::ptr_fun(sp_ui_menu_deselect_action),item,action)); + item->show(); + append(*item); + } +} + +void ContextMenu::MakeObjectMenu(void) +{ +// GObjectClass *klass = G_OBJECT_GET_CLASS(_object); //to deduce the object's type from its class +// +// if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_ITEM)) +// { +// MakeItemMenu (); +// } +// if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_GROUP)) +// { +// MakeGroupMenu(); +// } +// if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_ANCHOR)) +// { +// MakeAnchorMenu(); +// } +// if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_IMAGE)) +// { +// MakeImageMenu(); +// } +// if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_SHAPE)) +// { +// MakeShapeMenu(); +// } +// if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_TEXT)) +// { +// MakeTextMenu(); +// } + + if (SP_IS_ITEM(_object)) { + MakeItemMenu(); + } + + if (SP_IS_GROUP(_object)) { + MakeGroupMenu(); + } + + if (SP_IS_ANCHOR(_object)) { + MakeAnchorMenu(); + } + + if (SP_IS_IMAGE(_object)) { + MakeImageMenu(); + } + + if (SP_IS_SHAPE(_object)) { + MakeShapeMenu(); + } + + if (SP_IS_TEXT(_object)) { + MakeTextMenu(); + } +} + +void ContextMenu::MakeItemMenu (void) +{ + Gtk::MenuItem* mi; + + /* Item dialog */ + mi = Gtk::manage(new Gtk::MenuItem(_("_Object Properties..."),1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ItemProperties)); + mi->show(); + append(*mi);//insert(*mi,positionOfLastDialog++); + + AddSeparator(); + + /* Select item */ + if (Inkscape::Verb::getbyid( "org.inkscape.followlink" )) { + mi = Gtk::manage(new Gtk::MenuItem(_("_Select This"), 1)); + if (_desktop->selection->includes(_item)) { + mi->set_sensitive(FALSE); + } else { + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ItemSelectThis)); + } + mi->show(); + append(*mi); + } + + + mi = Gtk::manage(new Gtk::MenuItem(_("Select Same"))); + mi->show(); + Gtk::Menu *select_same_submenu = Gtk::manage(new Gtk::Menu()); + if (_desktop->selection->isEmpty()) { + mi->set_sensitive(FALSE); + } + mi->set_submenu(*select_same_submenu); + append(*mi); + + /* Select same fill and stroke */ + mi = Gtk::manage(new Gtk::MenuItem(_("Fill and Stroke"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SelectSameFillStroke)); + mi->set_sensitive(!SP_IS_ANCHOR(_item)); + mi->show(); + select_same_submenu->append(*mi); + + /* Select same fill color */ + mi = Gtk::manage(new Gtk::MenuItem(_("Fill Color"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SelectSameFillColor)); + mi->set_sensitive(!SP_IS_ANCHOR(_item)); + mi->show(); + select_same_submenu->append(*mi); + + /* Select same stroke color */ + mi = Gtk::manage(new Gtk::MenuItem(_("Stroke Color"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SelectSameStrokeColor)); + mi->set_sensitive(!SP_IS_ANCHOR(_item)); + mi->show(); + select_same_submenu->append(*mi); + + /* Select same stroke style */ + mi = Gtk::manage(new Gtk::MenuItem(_("Stroke Style"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SelectSameStrokeStyle)); + mi->set_sensitive(!SP_IS_ANCHOR(_item)); + mi->show(); + select_same_submenu->append(*mi); + + /* Select same stroke style */ + mi = Gtk::manage(new Gtk::MenuItem(_("Object type"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SelectSameObjectType)); + mi->set_sensitive(!SP_IS_ANCHOR(_item)); + mi->show(); + select_same_submenu->append(*mi); + + /* Move to layer */ + mi = Gtk::manage(new Gtk::MenuItem(_("_Move to layer ..."), 1)); + if (_desktop->selection->isEmpty()) { + mi->set_sensitive(FALSE); + } else { + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ItemMoveTo)); + } + mi->show(); + append(*mi); + + /* Create link */ + mi = Gtk::manage(new Gtk::MenuItem(_("Create _Link"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ItemCreateLink)); + mi->set_sensitive(!SP_IS_ANCHOR(_item)); + mi->show(); + append(*mi); + + bool ClipRefOK=false; + bool MaskRefOK=false; + if (_item){ + if (_item->clip_ref){ + if (_item->clip_ref->getObject()){ + ClipRefOK=true; + } + } + } + if (_item){ + if (_item->mask_ref){ + if (_item->mask_ref->getObject()){ + MaskRefOK=true; + } + } + } + /* Set mask */ + mi = Gtk::manage(new Gtk::MenuItem(_("Set Mask"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SetMask)); + if (ClipRefOK || MaskRefOK) { + mi->set_sensitive(FALSE); + } else { + mi->set_sensitive(TRUE); + } + mi->show(); + append(*mi); + + /* Release mask */ + mi = Gtk::manage(new Gtk::MenuItem(_("Release Mask"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ReleaseMask)); + if (MaskRefOK) { + mi->set_sensitive(TRUE); + } else { + mi->set_sensitive(FALSE); + } + mi->show(); + append(*mi); + + /*SSet Clip Group */ + mi = Gtk::manage(new Gtk::MenuItem(_("Create Clip G_roup"),1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::CreateGroupClip)); + mi->set_sensitive(TRUE); + mi->show(); + append(*mi); + + /* Set Clip */ + mi = Gtk::manage(new Gtk::MenuItem(_("Set Cl_ip"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SetClip)); + if (ClipRefOK || MaskRefOK) { + mi->set_sensitive(FALSE); + } else { + mi->set_sensitive(TRUE); + } + mi->show(); + append(*mi); + + /* Release Clip */ + mi = Gtk::manage(new Gtk::MenuItem(_("Release C_lip"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ReleaseClip)); + if (ClipRefOK) { + mi->set_sensitive(TRUE); + } else { + mi->set_sensitive(FALSE); + } + mi->show(); + append(*mi); + + /* Group */ + mi = Gtk::manage(new Gtk::MenuItem(_("_Group"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ActivateGroup)); + if (_desktop->selection->isEmpty() || _desktop->selection->single()) { + mi->set_sensitive(FALSE); + } else { + mi->set_sensitive(TRUE); + } + mi->show(); + append(*mi); +} + +void ContextMenu::SelectSameFillStroke(void) +{ + sp_select_same_fill_stroke_style(_desktop, true, true, true); +} + +void ContextMenu::SelectSameFillColor(void) +{ + sp_select_same_fill_stroke_style(_desktop, true, false, false); +} + +void ContextMenu::SelectSameStrokeColor(void) +{ + sp_select_same_fill_stroke_style(_desktop, false, true, false); +} + +void ContextMenu::SelectSameStrokeStyle(void) +{ + sp_select_same_stroke_style(_desktop); +} + +void ContextMenu::SelectSameObjectType(void) +{ + sp_select_same_object_type(_desktop); +} + +void ContextMenu::ItemProperties(void) +{ + _desktop->selection->set(_item); + _desktop->_dlg_mgr->showDialog("ObjectProperties"); +} + +void ContextMenu::ItemSelectThis(void) +{ + _desktop->selection->set(_item); +} + +void ContextMenu::ItemMoveTo(void) +{ + Inkscape::UI::Dialogs::LayerPropertiesDialog::showMove(_desktop, _desktop->currentLayer()); +} + + + +void ContextMenu::ItemCreateLink(void) +{ + Inkscape::XML::Document *xml_doc = _desktop->doc()->getReprDoc(); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:a"); + _item->parent->getRepr()->addChild(repr, _item->getRepr()); + SPObject *object = _item->document->getObjectByRepr(repr); + g_return_if_fail(SP_IS_ANCHOR(object)); + + const char *id = _item->getRepr()->attribute("id"); + Inkscape::XML::Node *child = _item->getRepr()->duplicate(xml_doc); + _item->deleteObject(false); + repr->addChild(child, NULL); + child->setAttribute("id", id); + + Inkscape::GC::release(repr); + Inkscape::GC::release(child); + + DocumentUndo::done(object->document, SP_VERB_NONE, _("Create link")); + + _desktop->selection->set(SP_ITEM(object)); + _desktop->_dlg_mgr->showDialog("ObjectAttributes"); +} + +void ContextMenu::SetMask(void) +{ + sp_selection_set_mask(_desktop, false, false); +} + +void ContextMenu::ReleaseMask(void) +{ + sp_selection_unset_mask(_desktop, false); +} + +void ContextMenu::CreateGroupClip(void) +{ + sp_selection_set_clipgroup(_desktop); +} + +void ContextMenu::SetClip(void) +{ + sp_selection_set_mask(_desktop, true, false); +} + + +void ContextMenu::ReleaseClip(void) +{ + sp_selection_unset_mask(_desktop, true); +} + +void ContextMenu::MakeGroupMenu(void) +{ + /* Ungroup */ + Gtk::MenuItem* mi = Gtk::manage(new Gtk::MenuItem(_("_Ungroup"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ActivateUngroup)); + mi->show(); + append(*mi); +} + +void ContextMenu::ActivateGroup(void) +{ + sp_selection_group(_desktop->selection, _desktop); +} + +void ContextMenu::ActivateUngroup(void) +{ + GSList *children = NULL; + + sp_item_group_ungroup(static_cast<SPGroup*>(_item), &children); + _desktop->selection->setList(children); + g_slist_free(children); +} + +void ContextMenu::MakeAnchorMenu(void) +{ + Gtk::MenuItem* mi; + + /* Link dialog */ + mi = Gtk::manage(new Gtk::MenuItem(_("Link _Properties..."), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::AnchorLinkProperties)); + mi->show(); + insert(*mi,positionOfLastDialog++); + + /* Select item */ + mi = Gtk::manage(new Gtk::MenuItem(_("_Follow Link"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::AnchorLinkFollow)); + mi->show(); + append(*mi); + + /* Reset transformations */ + mi = Gtk::manage(new Gtk::MenuItem(_("_Remove Link"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::AnchorLinkRemove)); + mi->show(); + append(*mi); +} + +void ContextMenu::AnchorLinkProperties(void) +{ + _desktop->_dlg_mgr->showDialog("ObjectAttributes"); +} + +void ContextMenu::AnchorLinkFollow(void) +{ + + if (_desktop->selection->isEmpty()) { + _desktop->selection->set(_item); + } + // Opening the selected links with a python extension + Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.inkscape.followlink" ); + if (verb) { + SPAction *action = verb->get_action(Inkscape::ActionContext(_desktop)); + if (action) { + sp_action_perform(action, NULL); + } + } +} + +void ContextMenu::AnchorLinkRemove(void) +{ + GSList *children = NULL; + sp_item_group_ungroup(static_cast<SPAnchor*>(_item), &children, false); + DocumentUndo::done(_desktop->doc(), SP_VERB_NONE, _("Remove link")); + g_slist_free(children); +} + +void ContextMenu::MakeImageMenu (void) +{ + Gtk::MenuItem* mi; + Inkscape::XML::Node *ir = _object->getRepr(); + const gchar *href = ir->attribute("xlink:href"); + + /* Image properties */ + mi = Gtk::manage(new Gtk::MenuItem(_("Image _Properties..."), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ImageProperties)); + mi->show(); + insert(*mi,positionOfLastDialog++); + + /* Edit externally */ + mi = Gtk::manage(new Gtk::MenuItem(_("Edit Externally..."), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ImageEdit)); + mi->show(); + insert(*mi,positionOfLastDialog++); + if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) { + mi->set_sensitive( FALSE ); + } + + /* Trace Bitmap */ + mi = Gtk::manage(new Gtk::MenuItem(_("_Trace Bitmap..."), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ImageTraceBitmap)); + mi->show(); + insert(*mi,positionOfLastDialog++); + if (_desktop->selection->isEmpty()) { + mi->set_sensitive(FALSE); + } + + /* Trace Pixel Art */ + mi = Gtk::manage(new Gtk::MenuItem(_("Trace Pixel Art"), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ImageTracePixelArt)); + mi->show(); + insert(*mi,positionOfLastDialog++); + if (_desktop->selection->isEmpty()) { + mi->set_sensitive(FALSE); + } + + /* Embed image */ + if (Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" )) { + mi = Gtk::manage(new Gtk::MenuItem(C_("Context menu", "Embed Image"))); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ImageEmbed)); + mi->show(); + insert(*mi,positionOfLastDialog++); + if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) { + mi->set_sensitive( FALSE ); + } + } + + /* Extract image */ + if (Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" )) { + mi = Gtk::manage(new Gtk::MenuItem(C_("Context menu", "Extract Image..."))); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ImageExtract)); + mi->show(); + insert(*mi,positionOfLastDialog++); + if ( (!href) || ((strncmp(href, "data:", 5) != 0)) ) { + mi->set_sensitive( FALSE ); + } + } +} + +void ContextMenu::ImageProperties(void) +{ + _desktop->_dlg_mgr->showDialog("ObjectAttributes"); +} + +Glib::ustring ContextMenu::getImageEditorName() { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + Glib::ustring value; + Glib::ustring choices = prefs->getString("/options/bitmapeditor/value"); + if (!choices.empty()) { + value = choices; + } + else { + value = "gimp"; + } + return value; +} + +void ContextMenu::ImageEdit(void) +{ + if (_desktop->selection->isEmpty()) { + _desktop->selection->set(_item); + } + + GSList const *selected = _desktop->selection->itemList(); + + GError* errThing = 0; + Glib::ustring cmdline = getImageEditorName(); + Glib::ustring name; + Glib::ustring fullname; + +#ifdef WIN32 + // g_spawn_command_line_sync parsing is done according to Unix shell rules, + // not Windows command interpreter rules. Thus we need to enclose the + // executable path with single quotes. + int index = cmdline.find(".exe"); + if ( index < 0 ) index = cmdline.find(".bat"); + if ( index < 0 ) index = cmdline.find(".com"); + if ( index >= 0 ) { + Glib::ustring editorBin = cmdline.substr(0, index + 4).c_str(); + Glib::ustring args = cmdline.substr(index + 4, cmdline.length()).c_str(); + editorBin.insert(0, "'"); + editorBin.append("'"); + cmdline = editorBin; + cmdline.append(args); + } else { + // Enclose the whole command line if no executable path can be extracted. + cmdline.insert(0, "'"); + cmdline.append("'"); + } +#endif + + for (GSList const *iter = selected; iter != NULL; iter = iter->next) { + Inkscape::XML::Node *ir = SP_ITEM(iter->data)->getRepr(); + const gchar *href = ir->attribute("xlink:href"); + + if (strncmp (href,"file:",5) == 0) { + // URI to filename conversion + name = g_filename_from_uri(href, NULL, NULL); + } else { + name.append(href); + } + + if (Glib::path_is_absolute(name)) { + fullname = name; + } else if (SP_ACTIVE_DOCUMENT->getBase()) { + fullname = Glib::build_filename(SP_ACTIVE_DOCUMENT->getBase(), name); + } else { + fullname = Glib::build_filename(Glib::get_current_dir(), name); + } + + cmdline.append(" '"); + cmdline.append(fullname.c_str()); + cmdline.append("'"); + } + + //g_warning("##Command line: %s\n", cmdline.c_str()); + + g_spawn_command_line_async(cmdline.c_str(), &errThing); + + if ( errThing ) { + g_warning("Problem launching editor (%d). %s", errThing->code, errThing->message); + (_desktop->messageStack())->flash(Inkscape::ERROR_MESSAGE, errThing->message); + g_error_free(errThing); + errThing = 0; + } +} + +void ContextMenu::ImageTraceBitmap(void) +{ + inkscape_dialogs_unhide(); + _desktop->_dlg_mgr->showDialog("Trace"); +} + +void ContextMenu::ImageTracePixelArt(void) +{ + inkscape_dialogs_unhide(); + _desktop->_dlg_mgr->showDialog("PixelArt"); +} + +void ContextMenu::ImageEmbed(void) +{ + if (_desktop->selection->isEmpty()) { + _desktop->selection->set(_item); + } + + Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" ); + if (verb) { + SPAction *action = verb->get_action(Inkscape::ActionContext(_desktop)); + if (action) { + sp_action_perform(action, NULL); + } + } +} + +void ContextMenu::ImageExtract(void) +{ + if (_desktop->selection->isEmpty()) { + _desktop->selection->set(_item); + } + + Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" ); + if (verb) { + SPAction *action = verb->get_action(Inkscape::ActionContext(_desktop)); + if (action) { + sp_action_perform(action, NULL); + } + } +} + +void ContextMenu::MakeShapeMenu (void) +{ + Gtk::MenuItem* mi; + + /* Item dialog */ + mi = Gtk::manage(new Gtk::MenuItem(_("_Fill and Stroke..."), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::FillSettings)); + mi->show(); + insert(*mi,positionOfLastDialog++); +} + +void ContextMenu::FillSettings(void) +{ + if (_desktop->selection->isEmpty()) { + _desktop->selection->set(_item); + } + + _desktop->_dlg_mgr->showDialog("FillAndStroke"); +} + +void ContextMenu::MakeTextMenu (void) +{ + Gtk::MenuItem* mi; + + /* Fill and Stroke dialog */ + mi = Gtk::manage(new Gtk::MenuItem(_("_Fill and Stroke..."), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::FillSettings)); + mi->show(); + insert(*mi,positionOfLastDialog++); + + /* Edit Text dialog */ + mi = Gtk::manage(new Gtk::MenuItem(_("_Text and Font..."), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::TextSettings)); + mi->show(); + insert(*mi,positionOfLastDialog++); + + /* Spellcheck dialog */ + mi = Gtk::manage(new Gtk::MenuItem(_("Check Spellin_g..."), 1)); + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SpellcheckSettings)); + mi->show(); + insert(*mi,positionOfLastDialog++); +} + +void ContextMenu::TextSettings (void) +{ + if (_desktop->selection->isEmpty()) { + _desktop->selection->set(_item); + } + + _desktop->_dlg_mgr->showDialog("TextFont"); +} + +void ContextMenu::SpellcheckSettings (void) +{ + if (_desktop->selection->isEmpty()) { + _desktop->selection->set(_item); + } + + _desktop->_dlg_mgr->showDialog("SpellCheck"); +} + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/interface.h b/src/ui/interface.h new file mode 100644 index 000000000..6fb74046f --- /dev/null +++ b/src/ui/interface.h @@ -0,0 +1,277 @@ +#ifndef SEEN_SP_INTERFACE_H +#define SEEN_SP_INTERFACE_H + +/* + * Main UI stuff + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Frank Felfe <innerspace@iname.com> + * Abhishek Sharma + * Kris De Gussem <Kris.DeGussem@gmail.com> + * + * Copyright (C) 2012 Kris De Gussem + * Copyright (C) 1999-2002 authors + * Copyright (C) 2001-2002 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +//#ifdef HAVE_CONFIG_H +//# include <config.h> +//#endif + +#include <gtkmm/menu.h> + +class SPItem; +class SPObject; +class SPDesktop; +class SPViewWidget; + +namespace Gtk { +class SeparatorMenuItem; +} + +namespace Inkscape { + +class Verb; + +namespace UI { +namespace View { +class View; +} // namespace View +} // namespace UI +} // namespace Inkscape + +/** + * Create a new document window. + */ +void sp_create_window (SPViewWidget *vw, bool editable); + +/** + * \param widget unused + */ +void sp_ui_close_view (GtkWidget *widget); + +void sp_ui_new_view (void); + +/** + * @todo TODO: not yet working. To be re-enabled (by adding to menu) once it works. + */ +void sp_ui_new_view_preview (void); + +/** + * This function is called to exit the program, and iterates through all + * open document view windows, attempting to close each in turn. If the + * view has unsaved information, the user will be prompted to save, + * discard, or cancel. + * + * Returns FALSE if the user cancels the close_all operation, TRUE + * otherwise. + */ +unsigned int sp_ui_close_all (void); + +/** + * Build the main tool bar. + * + * Currently the main tool bar is built as a dynamic XML menu using + * \c sp_ui_build_dyn_menus. This function builds the bar, and then + * pass it to get items attached to it. + * + * @param view View to build the bar for + */ +GtkWidget *sp_ui_main_menubar (Inkscape::UI::View::View *view); + +void sp_menu_append_recent_documents (GtkWidget *menu); +void sp_ui_dialog_title_string (Inkscape::Verb * verb, char* c); + +Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view ); + +/** + * + */ +void sp_ui_error_dialog (char const* message); +bool sp_ui_overwrite_file (char const* filename); + + +/** + * Implements the Inkscape context menu. + * + * For the context menu implementation, the ContextMenu class stores the object + * that was selected in a private data member. This should be farely safe to do + * and a pointer to the SPItem as well as SPObject class are kept. + * All callbacks of the context menu entries are implemented as private + * functions. + * + * @todo add callbacks to destroy the context menu when it is closed (=key or mouse button pressed out of the scope of the context menu) + */ +class ContextMenu : public Gtk::Menu +{ + public: + /** + * The ContextMenu constructor contains all code to create and show the + * menu entries (aka child widgets). + * + * @param desktop pointer to the desktop the user is currently working on. + * @param item SPItem pointer to the object selected at the time the ContextMenu is created. + */ + ContextMenu(SPDesktop *desktop, SPItem *item); + ~ContextMenu(void); + + private: + SPItem *_item; // pointer to the object selected at the time the ContextMenu is created + SPObject *_object; // pointer to the object selected at the time the ContextMenu is created + SPDesktop *_desktop; //pointer to the desktop the user was currently working on at the time the ContextMenu is created + + int positionOfLastDialog; + + Gtk::MenuItem MIGroup; //menu entry to enter a group + Gtk::MenuItem MIParent; //menu entry to leave a group + + /** + * auxiliary function that adds a separator line in the context menu + */ + Gtk::SeparatorMenuItem* AddSeparator(void); + + /** + * c++ified version of sp_ui_menu_append_item. + * + * @see sp_ui_menu_append_item_from_verb and synchronize/drop that function when c++ifying other code in interface.cpp + */ + void AppendItemFromVerb(Inkscape::Verb *verb); + + /** + * main function which is responsible for creating the context sensitive menu items, + * calls subfunctions below to create the menu entry widgets. + */ + void MakeObjectMenu (void); + /** + * creates menu entries for an SP_TYPE_ITEM object + */ + void MakeItemMenu (void); + /** + * creates menu entries for a grouped object + */ + void MakeGroupMenu (void); + /** + * creates menu entries for an anchor object + */ + void MakeAnchorMenu (void); + /** + * creates menu entries for a bitmap image object + */ + void MakeImageMenu (void); + /** + * creates menu entries for a shape object + */ + void MakeShapeMenu (void); + /** + * creates menu entries for a text object + */ + void MakeTextMenu (void); + + void EnterGroup(Gtk::MenuItem* mi); + void LeaveGroup(void); + ////////////////////////////////////////// + //callbacks for the context menu entries of an SP_TYPE_ITEM object + void ItemProperties(void); + void ItemSelectThis(void); + void ItemMoveTo(void); + void SelectSameFillStroke(void); + void SelectSameFillColor(void); + void SelectSameStrokeColor(void); + void SelectSameStrokeStyle(void); + void SelectSameObjectType(void); + void ItemCreateLink(void); + void CreateGroupClip(void); + void SetMask(void); + void ReleaseMask(void); + void SetClip(void); + void ReleaseClip(void); + ////////////////////////////////////////// + + + /** + * callback, is executed on clicking the anchor "Group" and "Ungroup" menu entry + */ + void ActivateUngroup(void); + void ActivateGroup(void); + + void AnchorLinkProperties(void); + /** + * placeholder for callback to be executed on clicking the anchor "Follow link" context menu entry + * @todo add code to follow link externally + */ + void AnchorLinkFollow(void); + + /** + * callback, is executed on clicking the anchor "Link remove" menu entry + */ + void AnchorLinkRemove(void); + + + /** + * callback, opens the image properties dialog and is executed on clicking the context menu entry with similar name + */ + void ImageProperties(void); + + /** + * callback, is executed on clicking the image "Edit Externally" menu entry + */ + void ImageEdit(void); + + /** + * auxiliary function that loads the external image editor name from the settings. + */ + Glib::ustring getImageEditorName(); + + /** + * callback, is executed on clicking the "Embed Image" menu entry + */ + void ImageEmbed(void); + + /** + * callback, is executed on clicking the "Trace Bitmap" menu entry + */ + void ImageTraceBitmap(void); + + /** + * callback, is executed on clicking the "Trace Pixel Art" menu entry + */ + void ImageTracePixelArt(void); + + /** + * callback, is executed on clicking the "Extract Image" menu entry + */ + void ImageExtract(void); + + + /** + * callback, is executed on clicking the "Fill and Stroke" menu entry + */ + void FillSettings(void); + + + /** + * callback, is executed on clicking the "Text and Font" menu entry + */ + void TextSettings(void); + + /** + * callback, is executed on clicking the "Check spelling" menu entry + */ + void SpellcheckSettings(void); +}; + +#endif // SEEN_SP_INTERFACE_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/object-edit.cpp b/src/ui/object-edit.cpp new file mode 100644 index 000000000..ca550502d --- /dev/null +++ b/src/ui/object-edit.cpp @@ -0,0 +1,1452 @@ +/* + * Node editing extension to objects + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Mitsuru Oka + * Maximilian Albert <maximilian.albert@gmail.com> + * Abhishek Sharma + * Jon A. Cruz <jon@joncruz.org> + * + * Licensed under GNU GPL + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + + +#include "sp-item.h" +#include "sp-rect.h" +#include "box3d.h" +#include "sp-ellipse.h" +#include "sp-star.h" +#include "sp-spiral.h" +#include "sp-offset.h" +#include "sp-flowtext.h" +#include "preferences.h" +#include "style.h" +#include "desktop.h" +#include "desktop-handles.h" +#include "sp-namedview.h" +#include "live_effects/effect.h" +#include "sp-pattern.h" +#include "sp-path.h" +#include <glibmm/i18n.h> +#include "ui/object-edit.h" +#include "xml/repr.h" +#include <2geom/math-utils.h> +#include "knot-holder-entity.h" + +#define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m))) + +namespace { + +static KnotHolder *sp_lpe_knot_holder(SPLPEItem *item, SPDesktop *desktop) +{ + KnotHolder *knot_holder = new KnotHolder(desktop, item, NULL); + + Inkscape::LivePathEffect::Effect *effect = item->getCurrentLPE(); + effect->addHandles(knot_holder, desktop, item); + + return knot_holder; +} + +} // namespace + +namespace Inkscape { +namespace UI { + +KnotHolder *createKnotHolder(SPItem *item, SPDesktop *desktop) +{ + KnotHolder *knotholder = NULL; + + SPLPEItem *lpe = dynamic_cast<SPLPEItem *>(item); + if (lpe && + lpe->getCurrentLPE() && + lpe->getCurrentLPE()->isVisible() && + lpe->getCurrentLPE()->providesKnotholder()) { + knotholder = sp_lpe_knot_holder(lpe, desktop); + } else if (dynamic_cast<SPRect *>(item)) { + knotholder = new RectKnotHolder(desktop, item, NULL); + } else if (dynamic_cast<SPBox3D *>(item)) { + knotholder = new Box3DKnotHolder(desktop, item, NULL); + } else if (dynamic_cast<SPGenericEllipse *>(item)) { + knotholder = new ArcKnotHolder(desktop, item, NULL); + } else if (dynamic_cast<SPStar *>(item)) { + knotholder = new StarKnotHolder(desktop, item, NULL); + } else if (dynamic_cast<SPSpiral *>(item)) { + knotholder = new SpiralKnotHolder(desktop, item, NULL); + } else if (dynamic_cast<SPOffset *>(item)) { + knotholder = new OffsetKnotHolder(desktop, item, NULL); + } else { + SPFlowtext *flowtext = dynamic_cast<SPFlowtext *>(item); + if (flowtext && flowtext->has_internal_frame()) { + knotholder = new FlowtextKnotHolder(desktop, flowtext->get_frame(NULL), NULL); + } else if ((item->style->fill.isPaintserver() && dynamic_cast<SPPattern *>(item->style->getFillPaintServer())) || + (item->style->stroke.isPaintserver() && dynamic_cast<SPPattern *>(item->style->getStrokePaintServer()))) { + knotholder = new KnotHolder(desktop, item, NULL); + knotholder->add_pattern_knotholder(); + } + } + + return knotholder; +} + +} +} // namespace Inkscape + +/* SPRect */ + +/* handle for horizontal rounding radius */ +class RectKnotHolderEntityRX : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +/* handle for vertical rounding radius */ +class RectKnotHolderEntityRY : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +/* handle for width/height adjustment */ +class RectKnotHolderEntityWH : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + +protected: + void set_internal(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +/* handle for x/y adjustment */ +class RectKnotHolderEntityXY : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +Geom::Point +RectKnotHolderEntityRX::knot_get() const +{ + SPRect *rect = dynamic_cast<SPRect *>(item); + g_assert(rect != NULL); + + return Geom::Point(rect->x.computed + rect->width.computed - rect->rx.computed, rect->y.computed); +} + +void +RectKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPRect *rect = dynamic_cast<SPRect *>(item); + g_assert(rect != NULL); + + //In general we cannot just snap this radius to an arbitrary point, as we have only a single + //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap + //the radius then we should have a constrained snap. snap_knot_position() is unconstrained + Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(-1, 0)), state); + + if (state & GDK_CONTROL_MASK) { + gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0; + rect->rx.computed = rect->ry.computed = CLAMP(rect->x.computed + rect->width.computed - s[Geom::X], 0.0, temp); + rect->rx._set = rect->ry._set = true; + + } else { + rect->rx.computed = CLAMP(rect->x.computed + rect->width.computed - s[Geom::X], 0.0, rect->width.computed / 2.0); + rect->rx._set = true; + } + + update_knot(); + + rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void +RectKnotHolderEntityRX::knot_click(unsigned int state) +{ + SPRect *rect = dynamic_cast<SPRect *>(item); + g_assert(rect != NULL); + + if (state & GDK_SHIFT_MASK) { + /* remove rounding from rectangle */ + rect->getRepr()->setAttribute("rx", NULL); + rect->getRepr()->setAttribute("ry", NULL); + } else if (state & GDK_CONTROL_MASK) { + /* Ctrl-click sets the vertical rounding to be the same as the horizontal */ + rect->getRepr()->setAttribute("ry", rect->getRepr()->attribute("rx")); + } + +} + +Geom::Point +RectKnotHolderEntityRY::knot_get() const +{ + SPRect *rect = dynamic_cast<SPRect *>(item); + g_assert(rect != NULL); + + return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->ry.computed); +} + +void +RectKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPRect *rect = dynamic_cast<SPRect *>(item); + g_assert(rect != NULL); + + //In general we cannot just snap this radius to an arbitrary point, as we have only a single + //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap + //the radius then we should have a constrained snap. snap_knot_position() is unconstrained + Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(0, 1)), state); + + if (state & GDK_CONTROL_MASK) { // When holding control then rx will be kept equal to ry, + // resulting in a perfect circle (and not an ellipse) + gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0; + rect->rx.computed = rect->ry.computed = CLAMP(s[Geom::Y] - rect->y.computed, 0.0, temp); + rect->ry._set = rect->rx._set = true; + } else { + if (!rect->rx._set || rect->rx.computed == 0) { + rect->ry.computed = CLAMP(s[Geom::Y] - rect->y.computed, + 0.0, + MIN(rect->height.computed / 2.0, rect->width.computed / 2.0)); + } else { + rect->ry.computed = CLAMP(s[Geom::Y] - rect->y.computed, + 0.0, + rect->height.computed / 2.0); + } + + rect->ry._set = true; + } + + update_knot(); + + rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void +RectKnotHolderEntityRY::knot_click(unsigned int state) +{ + SPRect *rect = dynamic_cast<SPRect *>(item); + g_assert(rect != NULL); + + if (state & GDK_SHIFT_MASK) { + /* remove rounding */ + rect->getRepr()->setAttribute("rx", NULL); + rect->getRepr()->setAttribute("ry", NULL); + } else if (state & GDK_CONTROL_MASK) { + /* Ctrl-click sets the vertical rounding to be the same as the horizontal */ + rect->getRepr()->setAttribute("rx", rect->getRepr()->attribute("ry")); + } +} + +#define SGN(x) ((x)>0?1:((x)<0?-1:0)) + +static void sp_rect_clamp_radii(SPRect *rect) +{ + // clamp rounding radii so that they do not exceed width/height + if (2 * rect->rx.computed > rect->width.computed) { + rect->rx.computed = 0.5 * rect->width.computed; + rect->rx._set = true; + } + if (2 * rect->ry.computed > rect->height.computed) { + rect->ry.computed = 0.5 * rect->height.computed; + rect->ry._set = true; + } +} + +Geom::Point +RectKnotHolderEntityWH::knot_get() const +{ + SPRect *rect = dynamic_cast<SPRect *>(item); + g_assert(rect != NULL); + + return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); +} + +void +RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + SPRect *rect = dynamic_cast<SPRect *>(item); + g_assert(rect != NULL); + + Geom::Point s = p; + + if (state & GDK_CONTROL_MASK) { + // original width/height when drag started + gdouble const w_orig = (origin[Geom::X] - rect->x.computed); + gdouble const h_orig = (origin[Geom::Y] - rect->y.computed); + + //original ratio + gdouble ratio = (w_orig / h_orig); + + // mouse displacement since drag started + gdouble minx = p[Geom::X] - origin[Geom::X]; + gdouble miny = p[Geom::Y] - origin[Geom::Y]; + + Geom::Point p_handle(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); + + if (fabs(minx) > fabs(miny)) { + // snap to horizontal or diagonal + if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) { + // closer to the diagonal and in same-sign quarters, change both using ratio + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1)), state); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->height.computed = MAX(h_orig + minx / ratio, 0); + } else { + // closer to the horizontal, change only width, height is h_orig + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-1, 0)), state); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->height.computed = MAX(h_orig, 0); + } + rect->width.computed = MAX(w_orig + minx, 0); + + } else { + // snap to vertical or diagonal + if (miny != 0 && fabs(minx/miny) > 0.5 * ratio && (SGN(minx) == SGN(miny))) { + // closer to the diagonal and in same-sign quarters, change both using ratio + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1)), state); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->width.computed = MAX(w_orig + miny * ratio, 0); + } else { + // closer to the vertical, change only height, width is w_orig + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(0, -1)), state); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->width.computed = MAX(w_orig, 0); + } + rect->height.computed = MAX(h_orig + miny, 0); + + } + + rect->width._set = rect->height._set = true; + + } else { + // move freely + s = snap_knot_position(p, state); + rect->width.computed = MAX(s[Geom::X] - rect->x.computed, 0); + rect->height.computed = MAX(s[Geom::Y] - rect->y.computed, 0); + rect->width._set = rect->height._set = true; + } + + sp_rect_clamp_radii(rect); + + rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void +RectKnotHolderEntityWH::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + set_internal(p, origin, state); + update_knot(); +} + +Geom::Point +RectKnotHolderEntityXY::knot_get() const +{ + SPRect *rect = dynamic_cast<SPRect *>(item); + g_assert(rect != NULL); + + return Geom::Point(rect->x.computed, rect->y.computed); +} + +void +RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + SPRect *rect = dynamic_cast<SPRect *>(item); + g_assert(rect != NULL); + + // opposite corner (unmoved) + gdouble opposite_x = (rect->x.computed + rect->width.computed); + gdouble opposite_y = (rect->y.computed + rect->height.computed); + + // original width/height when drag started + gdouble w_orig = opposite_x - origin[Geom::X]; + gdouble h_orig = opposite_y - origin[Geom::Y]; + + Geom::Point s = p; + Geom::Point p_handle(rect->x.computed, rect->y.computed); + + // mouse displacement since drag started + gdouble minx = p[Geom::X] - origin[Geom::X]; + gdouble miny = p[Geom::Y] - origin[Geom::Y]; + + if (state & GDK_CONTROL_MASK) { + //original ratio + gdouble ratio = (w_orig / h_orig); + + if (fabs(minx) > fabs(miny)) { + // snap to horizontal or diagonal + if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) { + // closer to the diagonal and in same-sign quarters, change both using ratio + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1)), state); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->y.computed = MIN(origin[Geom::Y] + minx / ratio, opposite_y); + rect->height.computed = MAX(h_orig - minx / ratio, 0); + } else { + // closer to the horizontal, change only width, height is h_orig + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-1, 0)), state); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->y.computed = MIN(origin[Geom::Y], opposite_y); + rect->height.computed = MAX(h_orig, 0); + } + rect->x.computed = MIN(s[Geom::X], opposite_x); + rect->width.computed = MAX(w_orig - minx, 0); + } else { + // snap to vertical or diagonal + if (miny != 0 && fabs(minx/miny) > 0.5 *ratio && (SGN(minx) == SGN(miny))) { + // closer to the diagonal and in same-sign quarters, change both using ratio + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1)), state); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->x.computed = MIN(origin[Geom::X] + miny * ratio, opposite_x); + rect->width.computed = MAX(w_orig - miny * ratio, 0); + } else { + // closer to the vertical, change only height, width is w_orig + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(0, -1)), state); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->x.computed = MIN(origin[Geom::X], opposite_x); + rect->width.computed = MAX(w_orig, 0); + } + rect->y.computed = MIN(s[Geom::Y], opposite_y); + rect->height.computed = MAX(h_orig - miny, 0); + } + + rect->width._set = rect->height._set = rect->x._set = rect->y._set = true; + + } else { + // move freely + s = snap_knot_position(p, state); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + + rect->x.computed = MIN(s[Geom::X], opposite_x); + rect->width.computed = MAX(w_orig - minx, 0); + rect->y.computed = MIN(s[Geom::Y], opposite_y); + rect->height.computed = MAX(h_orig - miny, 0); + rect->width._set = rect->height._set = rect->x._set = rect->y._set = true; + } + + sp_rect_clamp_radii(rect); + + update_knot(); + + rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +RectKnotHolder::RectKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + RectKnotHolderEntityRX *entity_rx = new RectKnotHolderEntityRX(); + RectKnotHolderEntityRY *entity_ry = new RectKnotHolderEntityRY(); + RectKnotHolderEntityWH *entity_wh = new RectKnotHolderEntityWH(); + RectKnotHolderEntityXY *entity_xy = new RectKnotHolderEntityXY(); + + entity_rx->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, + _("Adjust the <b>horizontal rounding</b> radius; with <b>Ctrl</b> " + "to make the vertical radius the same"), + SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + + entity_ry->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, + _("Adjust the <b>vertical rounding</b> radius; with <b>Ctrl</b> " + "to make the horizontal radius the same"), + SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + + entity_wh->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, + _("Adjust the <b>width and height</b> of the rectangle; with <b>Ctrl</b> " + "to lock ratio or stretch in one dimension only"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + + entity_xy->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, + _("Adjust the <b>width and height</b> of the rectangle; with <b>Ctrl</b> " + "to lock ratio or stretch in one dimension only"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + + entity.push_back(entity_rx); + entity.push_back(entity_ry); + entity.push_back(entity_wh); + entity.push_back(entity_xy); + + add_pattern_knotholder(); +} + +/* Box3D (= the new 3D box structure) */ + +class Box3DKnotHolderEntity : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const = 0; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) = 0; + + Geom::Point knot_get_generic(SPItem *item, unsigned int knot_id) const; + void knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &p, unsigned int state); +}; + +Geom::Point +Box3DKnotHolderEntity::knot_get_generic(SPItem *item, unsigned int knot_id) const +{ + SPBox3D *box = dynamic_cast<SPBox3D *>(item); + if (box) { + return box3d_get_corner_screen(box, knot_id); + } else { + return Geom::Point(); // TODO investigate proper fallback + } +} + +void +Box3DKnotHolderEntity::knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &new_pos, unsigned int state) +{ + Geom::Point const s = snap_knot_position(new_pos, state); + + g_assert(item != NULL); + SPBox3D *box = dynamic_cast<SPBox3D *>(item); + g_assert(box != NULL); + Geom::Affine const i2dt (item->i2dt_affine ()); + + Box3D::Axis movement; + if ((knot_id < 4) != (state & GDK_SHIFT_MASK)) { + movement = Box3D::XY; + } else { + movement = Box3D::Z; + } + + box3d_set_corner (box, knot_id, s * i2dt, movement, (state & GDK_CONTROL_MASK)); + box3d_set_z_orders(box); + box3d_position_set(box); +} + +class Box3DKnotHolderEntity0 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity1 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity2 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity3 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity4 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity5 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity6 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity7 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntityCenter : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +Geom::Point +Box3DKnotHolderEntity0::knot_get() const +{ + return knot_get_generic(item, 0); +} + +Geom::Point +Box3DKnotHolderEntity1::knot_get() const +{ + return knot_get_generic(item, 1); +} + +Geom::Point +Box3DKnotHolderEntity2::knot_get() const +{ + return knot_get_generic(item, 2); +} + +Geom::Point +Box3DKnotHolderEntity3::knot_get() const +{ + return knot_get_generic(item, 3); +} + +Geom::Point +Box3DKnotHolderEntity4::knot_get() const +{ + return knot_get_generic(item, 4); +} + +Geom::Point +Box3DKnotHolderEntity5::knot_get() const +{ + return knot_get_generic(item, 5); +} + +Geom::Point +Box3DKnotHolderEntity6::knot_get() const +{ + return knot_get_generic(item, 6); +} + +Geom::Point +Box3DKnotHolderEntity7::knot_get() const +{ + return knot_get_generic(item, 7); +} + +Geom::Point +Box3DKnotHolderEntityCenter::knot_get() const +{ + SPBox3D *box = dynamic_cast<SPBox3D *>(item); + if (box) { + return box3d_get_center_screen(box); + } else { + return Geom::Point(); // TODO investigate proper fallback + } +} + +void +Box3DKnotHolderEntity0::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 0, new_pos, state); +} + +void +Box3DKnotHolderEntity1::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 1, new_pos, state); +} + +void +Box3DKnotHolderEntity2::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 2, new_pos, state); +} + +void +Box3DKnotHolderEntity3::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 3, new_pos, state); +} + +void +Box3DKnotHolderEntity4::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 4, new_pos, state); +} + +void +Box3DKnotHolderEntity5::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 5, new_pos, state); +} + +void +Box3DKnotHolderEntity6::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 6, new_pos, state); +} + +void +Box3DKnotHolderEntity7::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 7, new_pos, state); +} + +void +Box3DKnotHolderEntityCenter::knot_set(Geom::Point const &new_pos, Geom::Point const &origin, unsigned int state) +{ + Geom::Point const s = snap_knot_position(new_pos, state); + + SPBox3D *box = dynamic_cast<SPBox3D *>(item); + g_assert(box != NULL); + Geom::Affine const i2dt (item->i2dt_affine ()); + + box3d_set_center(box, s * i2dt, origin * i2dt, !(state & GDK_SHIFT_MASK) ? Box3D::XY : Box3D::Z, + state & GDK_CONTROL_MASK); + + box3d_set_z_orders(box); + box3d_position_set(box); +} + +Box3DKnotHolder::Box3DKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + Box3DKnotHolderEntity0 *entity_corner0 = new Box3DKnotHolderEntity0(); + Box3DKnotHolderEntity1 *entity_corner1 = new Box3DKnotHolderEntity1(); + Box3DKnotHolderEntity2 *entity_corner2 = new Box3DKnotHolderEntity2(); + Box3DKnotHolderEntity3 *entity_corner3 = new Box3DKnotHolderEntity3(); + Box3DKnotHolderEntity4 *entity_corner4 = new Box3DKnotHolderEntity4(); + Box3DKnotHolderEntity5 *entity_corner5 = new Box3DKnotHolderEntity5(); + Box3DKnotHolderEntity6 *entity_corner6 = new Box3DKnotHolderEntity6(); + Box3DKnotHolderEntity7 *entity_corner7 = new Box3DKnotHolderEntity7(); + Box3DKnotHolderEntityCenter *entity_center = new Box3DKnotHolderEntityCenter(); + + entity_corner0->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; " + "with <b>Ctrl</b> to constrain to the directions of edges or diagonals")); + + entity_corner1->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; " + "with <b>Ctrl</b> to constrain to the directions of edges or diagonals")); + + entity_corner2->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; " + "with <b>Ctrl</b> to constrain to the directions of edges or diagonals")); + + entity_corner3->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; " + "with <b>Ctrl</b> to constrain to the directions of edges or diagonals")); + + entity_corner4->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; " + "with <b>Ctrl</b> to constrain to the directions of edges or diagonals")); + + entity_corner5->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; " + "with <b>Ctrl</b> to constrain to the directions of edges or diagonals")); + + entity_corner6->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; " + "with <b>Ctrl</b> to constrain to the directions of edges or diagonals")); + + entity_corner7->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; " + "with <b>Ctrl</b> to constrain to the directions of edges or diagonals")); + + entity_center->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, + _("Move the box in perspective"), + SP_KNOT_SHAPE_CROSS); + + entity.push_back(entity_corner0); + entity.push_back(entity_corner1); + entity.push_back(entity_corner2); + entity.push_back(entity_corner3); + entity.push_back(entity_corner4); + entity.push_back(entity_corner5); + entity.push_back(entity_corner6); + entity.push_back(entity_corner7); + entity.push_back(entity_center); + + add_pattern_knotholder(); +} + +/* SPArc */ + +class ArcKnotHolderEntityStart : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class ArcKnotHolderEntityEnd : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class ArcKnotHolderEntityRX : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class ArcKnotHolderEntityRY : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +/* + * return values: + * 1 : inside + * 0 : on the curves + * -1 : outside + */ +static gint +sp_genericellipse_side(SPGenericEllipse *ellipse, Geom::Point const &p) +{ + gdouble dx = (p[Geom::X] - ellipse->cx.computed) / ellipse->rx.computed; + gdouble dy = (p[Geom::Y] - ellipse->cy.computed) / ellipse->ry.computed; + + gdouble s = dx * dx + dy * dy; + if (s < 1.0) return 1; + if (s > 1.0) return -1; + return 0; +} + +void +ArcKnotHolderEntityStart::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + int snaps = Inkscape::Preferences::get()->getInt("/options/rotationsnapsperpi/value", 12); + + SPGenericEllipse *arc = dynamic_cast<SPGenericEllipse *>(item); + g_assert(arc != NULL); + + arc->setClosed(sp_genericellipse_side(arc, p) == -1); + + Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed); + Geom::Scale sc(arc->rx.computed, arc->ry.computed); + + arc->start = atan2(delta * sc.inverse()); + + if ((state & GDK_CONTROL_MASK) && snaps) { + arc->start = sp_round(arc->start, M_PI / snaps); + } + + arc->normalize(); + arc->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +ArcKnotHolderEntityStart::knot_get() const +{ + SPGenericEllipse const *ge = dynamic_cast<SPGenericEllipse const *>(item); + g_assert(ge != NULL); + + return ge->getPointAtAngle(ge->start); +} + +void +ArcKnotHolderEntityStart::knot_click(unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast<SPGenericEllipse *>(item); + g_assert(ge != NULL); + + if (state & GDK_SHIFT_MASK) { + ge->end = ge->start = 0; + ge->updateRepr(); + } +} + +void +ArcKnotHolderEntityEnd::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + int snaps = Inkscape::Preferences::get()->getInt("/options/rotationsnapsperpi/value", 12); + + SPGenericEllipse *arc = dynamic_cast<SPGenericEllipse *>(item); + g_assert(arc != NULL); + + arc->setClosed(sp_genericellipse_side(arc, p) == -1); + + Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed); + Geom::Scale sc(arc->rx.computed, arc->ry.computed); + + arc->end = atan2(delta * sc.inverse()); + + if ((state & GDK_CONTROL_MASK) && snaps) { + arc->end = sp_round(arc->end, M_PI/snaps); + } + + arc->normalize(); + arc->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +ArcKnotHolderEntityEnd::knot_get() const +{ + SPGenericEllipse const *ge = dynamic_cast<SPGenericEllipse const *>(item); + g_assert(ge != NULL); + + return ge->getPointAtAngle(ge->end); +} + + +void +ArcKnotHolderEntityEnd::knot_click(unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast<SPGenericEllipse *>(item); + g_assert(ge != NULL); + + if (state & GDK_SHIFT_MASK) { + ge->end = ge->start = 0; + ge->updateRepr(); + } +} + + +void +ArcKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast<SPGenericEllipse *>(item); + g_assert(ge != NULL); + + Geom::Point const s = snap_knot_position(p, state); + + ge->rx.computed = fabs( ge->cx.computed - s[Geom::X] ); + + if ( state & GDK_CONTROL_MASK ) { + ge->ry.computed = ge->rx.computed; + } + + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +ArcKnotHolderEntityRX::knot_get() const +{ + SPGenericEllipse const *ge = dynamic_cast<SPGenericEllipse const *>(item); + g_assert(ge != NULL); + + return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(ge->rx.computed, 0)); +} + +void +ArcKnotHolderEntityRX::knot_click(unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast<SPGenericEllipse *>(item); + g_assert(ge != NULL); + + if (state & GDK_CONTROL_MASK) { + ge->ry.computed = ge->rx.computed; + ge->updateRepr(); + } +} + +void +ArcKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast<SPGenericEllipse *>(item); + g_assert(ge != NULL); + + Geom::Point const s = snap_knot_position(p, state); + + ge->ry.computed = fabs( ge->cy.computed - s[Geom::Y] ); + + if ( state & GDK_CONTROL_MASK ) { + ge->rx.computed = ge->ry.computed; + } + + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +ArcKnotHolderEntityRY::knot_get() const +{ + SPGenericEllipse const *ge = dynamic_cast<SPGenericEllipse *>(item); + g_assert(ge != NULL); + + return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(0, ge->ry.computed)); +} + +void +ArcKnotHolderEntityRY::knot_click(unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast<SPGenericEllipse *>(item); + g_assert(ge != NULL); + + if (state & GDK_CONTROL_MASK) { + ge->rx.computed = ge->ry.computed; + ge->updateRepr(); + } +} + +ArcKnotHolder::ArcKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + ArcKnotHolderEntityRX *entity_rx = new ArcKnotHolderEntityRX(); + ArcKnotHolderEntityRY *entity_ry = new ArcKnotHolderEntityRY(); + ArcKnotHolderEntityStart *entity_start = new ArcKnotHolderEntityStart(); + ArcKnotHolderEntityEnd *entity_end = new ArcKnotHolderEntityEnd(); + + entity_rx->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, + _("Adjust ellipse <b>width</b>, with <b>Ctrl</b> to make circle"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + + entity_ry->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, + _("Adjust ellipse <b>height</b>, with <b>Ctrl</b> to make circle"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + + entity_start->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, + _("Position the <b>start point</b> of the arc or segment; with <b>Ctrl</b> " + "to snap angle; drag <b>inside</b> the ellipse for arc, <b>outside</b> for segment"), + SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + + entity_end->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, + _("Position the <b>end point</b> of the arc or segment; with <b>Ctrl</b> to snap angle; " + "drag <b>inside</b> the ellipse for arc, <b>outside</b> for segment"), + SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + + entity.push_back(entity_rx); + entity.push_back(entity_ry); + entity.push_back(entity_start); + entity.push_back(entity_end); + + add_pattern_knotholder(); +} + +/* SPStar */ + +class StarKnotHolderEntity1 : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class StarKnotHolderEntity2 : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +void +StarKnotHolderEntity1::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPStar *star = dynamic_cast<SPStar *>(item); + g_assert(star != NULL); + + Geom::Point const s = snap_knot_position(p, state); + + Geom::Point d = s - star->center; + + double arg1 = atan2(d); + double darg1 = arg1 - star->arg[0]; + + if (state & GDK_MOD1_MASK) { + star->randomized = darg1/(star->arg[0] - star->arg[1]); + } else if (state & GDK_SHIFT_MASK) { + star->rounded = darg1/(star->arg[0] - star->arg[1]); + } else if (state & GDK_CONTROL_MASK) { + star->r[0] = L2(d); + } else { + star->r[0] = L2(d); + star->arg[0] = arg1; + star->arg[1] += darg1; + } + star->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void +StarKnotHolderEntity2::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPStar *star = dynamic_cast<SPStar *>(item); + g_assert(star != NULL); + + Geom::Point const s = snap_knot_position(p, state); + + if (star->flatsided == false) { + Geom::Point d = s - star->center; + + double arg1 = atan2(d); + double darg1 = arg1 - star->arg[1]; + + if (state & GDK_MOD1_MASK) { + star->randomized = darg1/(star->arg[0] - star->arg[1]); + } else if (state & GDK_SHIFT_MASK) { + star->rounded = fabs(darg1/(star->arg[0] - star->arg[1])); + } else if (state & GDK_CONTROL_MASK) { + star->r[1] = L2(d); + star->arg[1] = star->arg[0] + M_PI / star->sides; + } + else { + star->r[1] = L2(d); + star->arg[1] = atan2(d); + } + star->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } +} + +Geom::Point +StarKnotHolderEntity1::knot_get() const +{ + g_assert(item != NULL); + + SPStar const *star = dynamic_cast<SPStar const *>(item); + g_assert(star != NULL); + + return sp_star_get_xy(star, SP_STAR_POINT_KNOT1, 0); + +} + +Geom::Point +StarKnotHolderEntity2::knot_get() const +{ + g_assert(item != NULL); + + SPStar const *star = dynamic_cast<SPStar const *>(item); + g_assert(star != NULL); + + return sp_star_get_xy(star, SP_STAR_POINT_KNOT2, 0); +} + +static void +sp_star_knot_click(SPItem *item, unsigned int state) +{ + SPStar *star = dynamic_cast<SPStar *>(item); + g_assert(star != NULL); + + if (state & GDK_MOD1_MASK) { + star->randomized = 0; + star->updateRepr(); + } else if (state & GDK_SHIFT_MASK) { + star->rounded = 0; + star->updateRepr(); + } else if (state & GDK_CONTROL_MASK) { + star->arg[1] = star->arg[0] + M_PI / star->sides; + star->updateRepr(); + } +} + +void +StarKnotHolderEntity1::knot_click(unsigned int state) +{ + sp_star_knot_click(item, state); +} + +void +StarKnotHolderEntity2::knot_click(unsigned int state) +{ + sp_star_knot_click(item, state); +} + +StarKnotHolder::StarKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + SPStar *star = dynamic_cast<SPStar *>(item); + g_assert(item != NULL); + + StarKnotHolderEntity1 *entity1 = new StarKnotHolderEntity1(); + entity1->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Adjust the <b>tip radius</b> of the star or polygon; " + "with <b>Shift</b> to round; with <b>Alt</b> to randomize")); + + entity.push_back(entity1); + + if (star->flatsided == false) { + StarKnotHolderEntity2 *entity2 = new StarKnotHolderEntity2(); + entity2->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Adjust the <b>base radius</b> of the star; with <b>Ctrl</b> to keep star rays " + "radial (no skew); with <b>Shift</b> to round; with <b>Alt</b> to randomize")); + entity.push_back(entity2); + } + + add_pattern_knotholder(); +} + +/* SPSpiral */ + +class SpiralKnotHolderEntityInner : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class SpiralKnotHolderEntityOuter : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + + +/* + * set attributes via inner (t=t0) knot point: + * [default] increase/decrease inner point + * [shift] increase/decrease inner and outer arg synchronizely + * [control] constrain inner arg to round per PI/4 + */ +void +SpiralKnotHolderEntityInner::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); + + SPSpiral *spiral = dynamic_cast<SPSpiral *>(item); + g_assert(spiral != NULL); + + gdouble dx = p[Geom::X] - spiral->cx; + gdouble dy = p[Geom::Y] - spiral->cy; + + gdouble moved_y = p[Geom::Y] - origin[Geom::Y]; + + if (state & GDK_MOD1_MASK) { + // adjust divergence by vertical drag, relative to rad + if (spiral->rad > 0) { + double exp_delta = 0.1*moved_y/(spiral->rad); // arbitrary multiplier to slow it down + spiral->exp += exp_delta; + if (spiral->exp < 1e-3) + spiral->exp = 1e-3; + } + } else { + // roll/unroll from inside + gdouble arg_t0; + spiral->getPolar(spiral->t0, NULL, &arg_t0); + + gdouble arg_tmp = atan2(dy, dx) - arg_t0; + gdouble arg_t0_new = arg_tmp - floor((arg_tmp+M_PI)/(2.0*M_PI))*2.0*M_PI + arg_t0; + spiral->t0 = (arg_t0_new - spiral->arg) / (2.0*M_PI*spiral->revo); + + /* round inner arg per PI/snaps, if CTRL is pressed */ + if ( ( state & GDK_CONTROL_MASK ) + && ( fabs(spiral->revo) > SP_EPSILON_2 ) + && ( snaps != 0 ) ) { + gdouble arg = 2.0*M_PI*spiral->revo*spiral->t0 + spiral->arg; + spiral->t0 = (sp_round(arg, M_PI/snaps) - spiral->arg)/(2.0*M_PI*spiral->revo); + } + + spiral->t0 = CLAMP(spiral->t0, 0.0, 0.999); + } + + spiral->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +/* + * set attributes via outer (t=1) knot point: + * [default] increase/decrease revolution factor + * [control] constrain inner arg to round per PI/4 + */ +void +SpiralKnotHolderEntityOuter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); + + SPSpiral *spiral = dynamic_cast<SPSpiral *>(item); + g_assert(spiral != NULL); + + gdouble dx = p[Geom::X] - spiral->cx; + gdouble dy = p[Geom::Y] - spiral->cy; + + if (state & GDK_SHIFT_MASK) { // rotate without roll/unroll + spiral->arg = atan2(dy, dx) - 2.0*M_PI*spiral->revo; + if (!(state & GDK_MOD1_MASK)) { + // if alt not pressed, change also rad; otherwise it is locked + spiral->rad = MAX(hypot(dx, dy), 0.001); + } + if ( ( state & GDK_CONTROL_MASK ) + && snaps ) { + spiral->arg = sp_round(spiral->arg, M_PI/snaps); + } + } else { // roll/unroll + // arg of the spiral outer end + double arg_1; + spiral->getPolar(1, NULL, &arg_1); + + // its fractional part after the whole turns are subtracted + double arg_r = arg_1 - sp_round(arg_1, 2.0*M_PI); + + // arg of the mouse point relative to spiral center + double mouse_angle = atan2(dy, dx); + if (mouse_angle < 0) + mouse_angle += 2*M_PI; + + // snap if ctrl + if ( ( state & GDK_CONTROL_MASK ) && snaps ) { + mouse_angle = sp_round(mouse_angle, M_PI/snaps); + } + + // by how much we want to rotate the outer point + double diff = mouse_angle - arg_r; + if (diff > M_PI) + diff -= 2*M_PI; + else if (diff < -M_PI) + diff += 2*M_PI; + + // calculate the new rad; + // the value of t corresponding to the angle arg_1 + diff: + double t_temp = ((arg_1 + diff) - spiral->arg)/(2*M_PI*spiral->revo); + // the rad at that t: + double rad_new = 0; + if (t_temp > spiral->t0) + spiral->getPolar(t_temp, &rad_new, NULL); + + // change the revo (converting diff from radians to the number of turns) + spiral->revo += diff/(2*M_PI); + if (spiral->revo < 1e-3) + spiral->revo = 1e-3; + + // if alt not pressed and the values are sane, change the rad + if (!(state & GDK_MOD1_MASK) && rad_new > 1e-3 && rad_new/spiral->rad < 2) { + // adjust t0 too so that the inner point stays unmoved + double r0; + spiral->getPolar(spiral->t0, &r0, NULL); + spiral->rad = rad_new; + spiral->t0 = pow(r0 / spiral->rad, 1.0/spiral->exp); + } + if (!IS_FINITE(spiral->t0)) spiral->t0 = 0.0; + spiral->t0 = CLAMP(spiral->t0, 0.0, 0.999); + } + + spiral->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +SpiralKnotHolderEntityInner::knot_get() const +{ + SPSpiral const *spiral = dynamic_cast<SPSpiral const *>(item); + g_assert(spiral != NULL); + + return spiral->getXY(spiral->t0); +} + +Geom::Point +SpiralKnotHolderEntityOuter::knot_get() const +{ + SPSpiral const *spiral = dynamic_cast<SPSpiral const *>(item); + g_assert(spiral != NULL); + + return spiral->getXY(1.0); +} + +void +SpiralKnotHolderEntityInner::knot_click(unsigned int state) +{ + SPSpiral *spiral = dynamic_cast<SPSpiral *>(item); + g_assert(spiral != NULL); + + if (state & GDK_MOD1_MASK) { + spiral->exp = 1; + spiral->updateRepr(); + } else if (state & GDK_SHIFT_MASK) { + spiral->t0 = 0; + spiral->updateRepr(); + } +} + +SpiralKnotHolder::SpiralKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + SpiralKnotHolderEntityInner *entity_inner = new SpiralKnotHolderEntityInner(); + SpiralKnotHolderEntityOuter *entity_outer = new SpiralKnotHolderEntityOuter(); + + entity_inner->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Roll/unroll the spiral from <b>inside</b>; with <b>Ctrl</b> to snap angle; " + "with <b>Alt</b> to converge/diverge")); + + entity_outer->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Roll/unroll the spiral from <b>outside</b>; with <b>Ctrl</b> to snap angle; " + "with <b>Shift</b> to scale/rotate; with <b>Alt</b> to lock radius")); + + entity.push_back(entity_inner); + entity.push_back(entity_outer); + + add_pattern_knotholder(); +} + +/* SPOffset */ + +class OffsetKnotHolderEntity : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +void +OffsetKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int /*state*/) +{ + SPOffset *offset = dynamic_cast<SPOffset *>(item); + g_assert(offset != NULL); + + offset->rad = sp_offset_distance_to_original(offset, p); + offset->knot = p; + offset->knotSet = true; + + offset->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + + +Geom::Point +OffsetKnotHolderEntity::knot_get() const +{ + SPOffset const *offset = dynamic_cast<SPOffset const *>(item); + g_assert(offset != NULL); + + Geom::Point np; + sp_offset_top_point(offset,&np); + return np; +} + +OffsetKnotHolder::OffsetKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + OffsetKnotHolderEntity *entity_offset = new OffsetKnotHolderEntity(); + entity_offset->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Adjust the <b>offset distance</b>")); + entity.push_back(entity_offset); + + add_pattern_knotholder(); +} + +// TODO: this is derived from RectKnotHolderEntityWH because it used the same static function +// set_internal as the latter before KnotHolderEntity was C++ified. Check whether this also makes +// sense logically. +class FlowtextKnotHolderEntity : public RectKnotHolderEntityWH { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +Geom::Point +FlowtextKnotHolderEntity::knot_get() const +{ + SPRect const *rect = dynamic_cast<SPRect const *>(item); + g_assert(rect != NULL); + + return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); +} + +void +FlowtextKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + set_internal(p, origin, state); +} + +FlowtextKnotHolder::FlowtextKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + g_assert(item != NULL); + + FlowtextKnotHolderEntity *entity_flowtext = new FlowtextKnotHolderEntity(); + entity_flowtext->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Drag to resize the <b>flowed text frame</b>")); + entity.push_back(entity_flowtext); +} + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/object-edit.h b/src/ui/object-edit.h new file mode 100644 index 000000000..75f3ce12b --- /dev/null +++ b/src/ui/object-edit.h @@ -0,0 +1,84 @@ +#ifndef OBJECT_EDIT_H_SEEN +#define OBJECT_EDIT_H_SEEN + +/* + * Node editing extension to objects + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Mitsuru Oka + * Jon A. Cruz <jon@joncruz.org> + * + * Licensed under GNU GPL + */ + +#include "knotholder.h" + +namespace Inkscape { +namespace UI { + +KnotHolder *createKnotHolder(SPItem *item, SPDesktop *desktop); + +} +} + +class RectKnotHolder : public KnotHolder { +public: + RectKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~RectKnotHolder() {}; +}; + +class Box3DKnotHolder : public KnotHolder { +public: + Box3DKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~Box3DKnotHolder() {}; +}; + +class ArcKnotHolder : public KnotHolder { +public: + ArcKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~ArcKnotHolder() {}; +}; + +class StarKnotHolder : public KnotHolder { +public: + StarKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~StarKnotHolder() {}; +}; + +class SpiralKnotHolder : public KnotHolder { +public: + SpiralKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~SpiralKnotHolder() {}; +}; + +class OffsetKnotHolder : public KnotHolder { +public: + OffsetKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~OffsetKnotHolder() {}; +}; + +class FlowtextKnotHolder : public KnotHolder { +public: + FlowtextKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~FlowtextKnotHolder() {}; +}; + +class MiscKnotHolder : public KnotHolder { +public: + MiscKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~MiscKnotHolder() {}; +}; + +#endif // OBJECT_EDIT_H_SEEN + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/shape-editor.cpp b/src/ui/shape-editor.cpp new file mode 100644 index 000000000..0b9fc24c5 --- /dev/null +++ b/src/ui/shape-editor.cpp @@ -0,0 +1,177 @@ +/* + * Inkscape::ShapeEditor + * + * Authors: + * bulia byak <buliabyak@users.sf.net> + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <glibmm/i18n.h> + +#include "desktop-handles.h" +#include "document.h" +#include "gc-anchored.h" +#include "knotholder.h" +#include "ui/object-edit.h" +#include "sp-item.h" +#include "sp-object.h" +#include "ui/shape-editor.h" +#include "xml/node-event-vector.h" + +//using Inkscape::createKnotHolder; + +namespace Inkscape { +namespace UI { + +bool ShapeEditor::_blockSetItem = false; + +ShapeEditor::ShapeEditor(SPDesktop *dt) { + this->desktop = dt; + this->knotholder = NULL; + this->knotholder_listener_attached_for = NULL; +} + +ShapeEditor::~ShapeEditor() { + unset_item(); +} + +void ShapeEditor::unset_item(bool keep_knotholder) { + if (this->knotholder) { + Inkscape::XML::Node *old_repr = this->knotholder->repr; + if (old_repr && old_repr == knotholder_listener_attached_for) { + sp_repr_remove_listener_by_data(old_repr, this); + Inkscape::GC::release(old_repr); + knotholder_listener_attached_for = NULL; + } + + if (!keep_knotholder) { + delete this->knotholder; + this->knotholder = NULL; + } + } +} + +bool ShapeEditor::has_knotholder() { + return this->knotholder != NULL; +} + +void ShapeEditor::update_knotholder() { + if (this->knotholder) + this->knotholder->update_knots(); +} + +bool ShapeEditor::has_local_change() { + return (this->knotholder && this->knotholder->local_change != 0); +} + +void ShapeEditor::decrement_local_change() { + if (this->knotholder) { + this->knotholder->local_change = FALSE; + } +} + +const SPItem *ShapeEditor::get_item() { + const SPItem *item = NULL; + if (this->has_knotholder()) { + item = this->knotholder->getItem(); + } + return item; +} + +void ShapeEditor::event_attr_changed(Inkscape::XML::Node *, gchar const *name, gchar const *, gchar const *, bool, void *data) +{ + g_assert(data); + ShapeEditor *sh = static_cast<ShapeEditor *>(data); + bool changed_kh = false; + + if (sh->has_knotholder()) + { + changed_kh = !sh->has_local_change(); + sh->decrement_local_change(); + if (changed_kh) { + // this can happen if an LPEItem's knotholder handle was dragged, in which case we want + // to keep the knotholder; in all other cases (e.g., if the LPE itself changes) we delete it + sh->reset_item(!strcmp(name, "d")); + } + } +} + +static Inkscape::XML::NodeEventVector shapeeditor_repr_events = { + NULL, /* child_added */ + NULL, /* child_removed */ + ShapeEditor::event_attr_changed, + NULL, /* content_changed */ + NULL /* order_changed */ +}; + + +void ShapeEditor::set_item(SPItem *item, bool keep_knotholder) { + if (_blockSetItem) { + return; + } + + // this happens (and should only happen) when for an LPEItem having both knotholder and + // nodepath the knotholder is adapted; in this case we don't want to delete the knotholder + // since this freezes the handles + unset_item(keep_knotholder); + + if (item) { + Inkscape::XML::Node *repr; + if (!this->knotholder) { + // only recreate knotholder if none is present + this->knotholder = createKnotHolder(item, desktop); + } + if (this->knotholder) { + this->knotholder->update_knots(); + // setting new listener + repr = this->knotholder->repr; + if (repr != knotholder_listener_attached_for) { + Inkscape::GC::anchor(repr); + sp_repr_add_listener(repr, &shapeeditor_repr_events, this); + knotholder_listener_attached_for = repr; + } + } + } +} + + +/** FIXME: This thing is only called when the item needs to be updated in response to repr change. + Why not make a reload function in KnotHolder? */ +void ShapeEditor::reset_item(bool keep_knotholder) +{ + if (knotholder) { + SPObject *obj = sp_desktop_document(desktop)->getObjectByRepr(knotholder_listener_attached_for); /// note that it is not certain that this is an SPItem; it could be a LivePathEffectObject. + set_item(SP_ITEM(obj), keep_knotholder); + } +} + +/** + * Returns true if this ShapeEditor has a knot above which the mouse currently hovers. + */ +bool ShapeEditor::knot_mouseover() const { + if (this->knotholder) { + return knotholder->knot_mouseover(); + } + + return false; +} + +} // 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 : diff --git a/src/ui/shape-editor.h b/src/ui/shape-editor.h new file mode 100644 index 000000000..142a2493b --- /dev/null +++ b/src/ui/shape-editor.h @@ -0,0 +1,71 @@ +#ifndef SEEN_SHAPE_EDITOR_H +#define SEEN_SHAPE_EDITOR_H + +/* + * Inkscape::ShapeEditor + * + * This is a container class which contains a knotholder for shapes. + * It is attached to a single item. + * + * Authors: + * bulia byak <buliabyak@users.sf.net> + * + */ + +class KnotHolder; +class LivePathEffectObject; +class SPDesktop; +class SPItem; + +namespace Inkscape { namespace XML { class Node; } +namespace UI { + +class ShapeEditor { +public: + + ShapeEditor(SPDesktop *desktop); + ~ShapeEditor(); + + void set_item(SPItem *item, bool keep_knotholder = false); + void unset_item(bool keep_knotholder = false); + + void update_knotholder(); //((deprecated)) + + bool has_local_change(); + void decrement_local_change(); + + bool knot_mouseover() const; + + static void blockSetItem(bool b) { _blockSetItem = b; } // kludge + + static void event_attr_changed(Inkscape::XML::Node * /*repr*/, char const *name, char const * /*old_value*/, + char const * /*new_value*/, bool /*is_interactive*/, void *data); +private: + bool has_knotholder(); + void reset_item (bool keep_knotholder = true); + const SPItem *get_item(); + + static bool _blockSetItem; + + SPDesktop *desktop; + KnotHolder *knotholder; + Inkscape::XML::Node *knotholder_listener_attached_for; +}; + +} // namespace UI +} // namespace Inkscape + +#endif // SEEN_SHAPE_EDITOR_H + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : + diff --git a/src/ui/tool-factory.h b/src/ui/tool-factory.h new file mode 100644 index 000000000..726706732 --- /dev/null +++ b/src/ui/tool-factory.h @@ -0,0 +1,40 @@ +/** @file + * Factory for ToolBase tree + *//* + * Authors: + * Markus Engel + * + * Copyright (C) 2013 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef TOOL_FACTORY_SEEN +#define TOOL_FACTORY_SEEN + +#include "factory.h" + +namespace Inkscape { +namespace UI { +namespace Tools { + +class ToolBase; + +} +} +} + +typedef Singleton< Factory<Inkscape::UI::Tools::ToolBase> > ToolFactory; + + +#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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index d10ed0f0d..998f74ee0 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -74,7 +74,7 @@ ControlPointSelection::~ControlPointSelection() } /** Add a control point to the selection. */ -std::pair<ControlPointSelection::iterator, bool> ControlPointSelection::insert(const value_type &x) +std::pair<ControlPointSelection::iterator, bool> ControlPointSelection::insert(const value_type &x, bool notify) { iterator found = _points.find(x); if (found != _points.end()) { @@ -86,6 +86,10 @@ std::pair<ControlPointSelection::iterator, bool> ControlPointSelection::insert(c x->updateState(); _pointChanged(x, true); + if (notify) { + signal_selection_changed.emit(std::vector<key_type>(1, x), true); + } + return std::pair<iterator, bool>(found, true); } @@ -97,47 +101,75 @@ void ControlPointSelection::erase(iterator pos) erased->updateState(); _pointChanged(erased, false); } -ControlPointSelection::size_type ControlPointSelection::erase(const key_type &k) +ControlPointSelection::size_type ControlPointSelection::erase(const key_type &k, bool notify) { iterator pos = _points.find(k); if (pos == _points.end()) return 0; erase(pos); + + if (notify) { + signal_selection_changed.emit(std::vector<key_type>(1, k), false); + } return 1; } void ControlPointSelection::erase(iterator first, iterator last) { + std::vector<SelectableControlPoint *> out(first, last); while (first != last) erase(first++); + signal_selection_changed.emit(out, false); } /** Remove all points from the selection, making it empty. */ void ControlPointSelection::clear() { + std::vector<SelectableControlPoint *> out(begin(), end()); for (iterator i = begin(); i != end(); ) erase(i++); + if (!out.empty()) + signal_selection_changed.emit(out, false); } /** Select all points that this selection can contain. */ void ControlPointSelection::selectAll() { for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { - insert(*i); + insert(*i, false); } + std::vector<SelectableControlPoint *> out(_all_points.begin(), _all_points.end()); + if (!out.empty()) + signal_selection_changed.emit(out, true); } /** Select all points inside the given rectangle (in desktop coordinates). */ void ControlPointSelection::selectArea(Geom::Rect const &r) { + std::vector<SelectableControlPoint *> out; for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { - if (r.contains(**i)) - insert(*i); + if (r.contains(**i)) { + insert(*i, false); + out.push_back(*i); + } } + if (!out.empty()) + signal_selection_changed.emit(out, true); } /** Unselect all selected points and select all unselected points. */ void ControlPointSelection::invertSelection() { + std::vector<SelectableControlPoint *> in, out; for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { - if ((*i)->selected()) erase(*i); - else insert(*i); + if ((*i)->selected()) { + in.push_back(*i); + erase(*i); + } + else { + out.push_back(*i); + insert(*i, false); + } } + if (!in.empty()) + signal_selection_changed.emit(in, false); + if (!out.empty()) + signal_selection_changed.emit(out, true); } void ControlPointSelection::spatialGrow(SelectableControlPoint *origin, int dir) { @@ -166,6 +198,7 @@ void ControlPointSelection::spatialGrow(SelectableControlPoint *origin, int dir) if (match) { if (grow) insert(match); else erase(match); + signal_selection_changed.emit(std::vector<value_type>(1, match), grow); } } @@ -389,7 +422,7 @@ void ControlPointSelection::_pointChanged(SelectableControlPoint *p, bool select _handles->rotationCenter().move(_bounds->midpoint()); } - signal_point_changed.emit(p, selected); + //signal_point_changed.emit(p, selected); } void ControlPointSelection::_mouseoverChanged() diff --git a/src/ui/tool/control-point-selection.h b/src/ui/tool/control-point-selection.h index a087e0455..2d812c0a3 100644 --- a/src/ui/tool/control-point-selection.h +++ b/src/ui/tool/control-point-selection.h @@ -62,18 +62,19 @@ public: const_iterator end() const { return _points.end(); } // insert - std::pair<iterator, bool> insert(const value_type& x); + std::pair<iterator, bool> insert(const value_type& x, bool notify = true); template <class InputIterator> void insert(InputIterator first, InputIterator last) { for (; first != last; ++first) { - insert(*first); + insert(*first, false); } + signal_selection_changed.emit(std::vector<key_type>(first, last), true); } // erase void clear(); void erase(iterator pos); - size_type erase(const key_type& k); + size_type erase(const key_type& k, bool notify = true); void erase(iterator first, iterator last); // find @@ -108,7 +109,9 @@ public: void toggleTransformHandlesMode(); sigc::signal<void> signal_update; - sigc::signal<void, SelectableControlPoint *, bool> signal_point_changed; + // It turns out that emitting a signal after every point is selected or deselected is not too efficient, + // so this can be done in a massive group once the selection is finally changed. + sigc::signal<void, std::vector<SelectableControlPoint *>, bool> signal_selection_changed; sigc::signal<void, CommitEvent> signal_commit; void getOriginalPoints(std::vector<Inkscape::SnapCandidatePoint> &pts); @@ -166,4 +169,4 @@ private: fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 : diff --git a/src/ui/tool/curve-drag-point.cpp b/src/ui/tool/curve-drag-point.cpp index 4ca736f80..013553410 100644 --- a/src/ui/tool/curve-drag-point.cpp +++ b/src/ui/tool/curve-drag-point.cpp @@ -53,9 +53,11 @@ bool CurveDragPoint::grabbed(GdkEventMotion */*event*/) // delta is a vector equal 1/3 of distance from first to second Geom::Point delta = (second->position() - first->position()) / 3.0; - first->front()->move(first->front()->position() + delta); - second->back()->move(second->back()->position() - delta); - + // only update the nodes if the mode is bspline + if(!_pm.isBSpline(false)){ + first->front()->move(first->front()->position() + delta); + second->back()->move(second->back()->position() - delta); + } _pm.update(); } else { _segment_was_degenerate = false; @@ -88,9 +90,12 @@ void CurveDragPoint::dragged(Geom::Point &new_pos, GdkEventMotion *event) Geom::Point offset0 = ((1-weight)/(3*t*(1-t)*(1-t))) * delta; Geom::Point offset1 = (weight/(3*t*t*(1-t))) * delta; - first->front()->move(first->front()->position() + offset0); - second->back()->move(second->back()->position() + offset1); - + //modified so that, if the trace is bspline, it only acts if the SHIFT key is pressed + if(!_pm.isBSpline(false)){ + first->front()->move(first->front()->position() + offset0); + second->back()->move(second->back()->position() + offset1); + }else if(weight>=0.8 && held_shift(*event))second->back()->move(new_pos); + else if(weight<=0.2 && held_shift(*event))first->front()->move(new_pos); _pm.update(); } diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index 65987ad52..d7b35c974 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -122,7 +122,7 @@ MultiPathManipulator::MultiPathManipulator(PathSharedData &data, sigc::connectio { _selection.signal_commit.connect( sigc::mem_fun(*this, &MultiPathManipulator::_commit)); - _selection.signal_point_changed.connect( + _selection.signal_selection_changed.connect( sigc::hide( sigc::hide( signal_coords_changed.make_slot()))); } @@ -182,7 +182,7 @@ void MultiPathManipulator::setItems(std::set<ShapeRecord> const &s) ShapeRecord const &r = *i; if (!SP_IS_PATH(r.item) && !IS_LIVEPATHEFFECT(r.item)) continue; boost::shared_ptr<PathManipulator> newpm(new PathManipulator(*this, (SPPath*) r.item, - r.edit_transform, _getOutlineColor(r.role), r.lpe_key)); + r.edit_transform, _getOutlineColor(r.role, r.item), r.lpe_key)); newpm->showHandles(_show_handles); // always show outlines for clips and masks newpm->showOutline(_show_outline || r.role != SHAPE_ROLE_NORMAL); @@ -670,7 +670,18 @@ bool MultiPathManipulator::event(Inkscape::UI::Tools::ToolBase *event_context, G // a) del preserves shape, and control is not pressed // b) ctrl+del preserves shape (del_preserves_shape is false), and control is pressed // Hence xor - deleteNodes(del_preserves_shape ^ held_control(event->key)); + guint mode = prefs->getInt("/tools/freehand/pen/freehand-mode", 0); + + //if the trace is bspline ( mode 2) + if(mode==2){ + // is this correct ? + if(del_preserves_shape ^ held_control(event->key)) + deleteNodes(false); + else + deleteNodes(true); + } + else + deleteNodes(del_preserves_shape ^ held_control(event->key)); // Delete any selected gradient nodes as well event_context->deleteSelectedDrag(held_control(event->key)); @@ -833,7 +844,7 @@ void MultiPathManipulator::_doneWithCleanup(gchar const *reason, bool alert_LPE) } /** Get an outline color based on the shape's role (normal, mask, LPE parameter, etc.). */ -guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role) +guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role, SPItem *item) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); switch(role) { @@ -845,7 +856,7 @@ guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role) return prefs->getColor("/tools/nodes/lpe_param_color", 0x009000ff); case SHAPE_ROLE_NORMAL: default: - return prefs->getColor("/tools/nodes/outline_color", 0xff0000ff); + return item->highlight_color(); } } diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h index 1328372c6..cdbf34e9d 100644 --- a/src/ui/tool/multi-path-manipulator.h +++ b/src/ui/tool/multi-path-manipulator.h @@ -53,6 +53,7 @@ public: void insertNodesAtExtrema(ExtremumType extremum); void insertNodes(); + void alertLPE(); void duplicateNodes(); void joinNodes(); void breakNodes(); @@ -104,9 +105,9 @@ private: } void _commit(CommitEvent cps); - void _done(gchar const *reason, bool alert_LPE = false); + void _done(gchar const *reason, bool alert_LPE = true); void _doneWithCleanup(gchar const *reason, bool alert_LPE = false); - guint32 _getOutlineColor(ShapeRole role); + guint32 _getOutlineColor(ShapeRole role, SPItem *item); MapType _mmap; public: diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index fbbc4be64..4abc901d2 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -13,7 +13,6 @@ #include <glib/gi18n.h> #include <2geom/bezier-utils.h> #include <2geom/transforms.h> - #include "display/sp-ctrlline.h" #include "display/sp-canvas.h" #include "display/sp-canvas-util.h" @@ -28,7 +27,10 @@ #include "ui/tool/event-utils.h" #include "ui/tool/node.h" #include "ui/tool/path-manipulator.h" +#include "ui/tools/node-tool.h" +#include "ui/tools-switch.h" #include <gdk/gdkkeysyms.h> +#include <cmath> namespace { @@ -58,6 +60,11 @@ Inkscape::ControlType nodeTypeToCtrlType(Inkscape::UI::NodeType type) namespace Inkscape { namespace UI { +const double handleCubicGap = 0.01; +const double noPower = 0.0; +const double defaultStartPower = 0.3334; +const double defaultEndPower = 0.6667; + ControlPoint::ColorSet Node::node_colors = { {0xbfbfbf00, 0x000000ff}, // normal fill, stroke {0xff000000, 0x000000ff}, // mouseover fill, stroke @@ -166,6 +173,12 @@ void Handle::move(Geom::Point const &new_pos) } } setPosition(new_pos); + + //move the handler and its oposite the same proportion + if(_pm().isBSpline()){ + setPosition(_pm().BSplineHandleReposition(this,this)); + this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this)); + } return; } @@ -177,6 +190,13 @@ void Handle::move(Geom::Point const &new_pos) Geom::Point new_delta = (Geom::dot(delta, direction) / Geom::L2sq(direction)) * direction; setRelativePos(new_delta); + + //move the handler and its oposite the same proportion + if(_pm().isBSpline()){ + setPosition(_pm().BSplineHandleReposition(this,this)); + this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this)); + } + return; } @@ -195,8 +215,14 @@ void Handle::move(Geom::Point const &new_pos) break; default: break; } - setPosition(new_pos); + + // moves the handler and its oposite the same proportion + if(_pm().isBSpline()){ + setPosition(_pm().BSplineHandleReposition(this,this)); + this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this)); + } + } void Handle::setPosition(Geom::Point const &p) @@ -273,12 +299,27 @@ bool Handle::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEven break; default: break; } + break; + // new double click event to set the handlers of a node to the default proportion, defaultStartPower% + case GDK_2BUTTON_PRESS: + handle_2button_press(); + break; + default: break; } return ControlPoint::_eventHandler(event_context, event); } +//this function moves the handler and its oposite to the default proportion of defaultStartPower +void Handle::handle_2button_press(){ + if(_pm().isBSpline()){ + setPosition(_pm().BSplineHandleReposition(this,defaultStartPower)); + this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),defaultStartPower)); + _pm().update(); + } +} + bool Handle::grabbed(GdkEventMotion *) { _saved_other_pos = other()->position(); @@ -289,6 +330,10 @@ bool Handle::grabbed(GdkEventMotion *) void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) { + if (tools_isactive(_desktop, TOOLS_NODES)) { + Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(_desktop->event_context); + nt->update_helperpath(); + } Geom::Point parent_pos = _parent->position(); Geom::Point origin = _last_drag_origin(); SnapManager &sm = _desktop->namedview->snap_manager; @@ -326,10 +371,18 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) ctrl_constraint = Inkscape::Snapper::SnapConstraint(parent_pos, parent_pos - perp_pos); } new_pos = result; + // moves the handler and its oposite in X fixed positions depending on parameter "steps with control" + // by default in live BSpline + if(_pm().isBSpline()){ + setPosition(new_pos); + int steps = _pm().BSplineGetSteps(); + new_pos=_pm().BSplineHandleReposition(this,ceilf(_pm().BSplineHandlePosition(this,this)*steps)/steps); + } } std::vector<Inkscape::SnapCandidatePoint> unselected; - if (snap) { + //if the snap adjustment is activated and it is not bspline + if (snap && !_pm().isBSpline(false)) { ControlPointSelection::Set &nodes = _parent->_selection.allPoints(); for (ControlPointSelection::Set::iterator i = nodes.begin(); i != nodes.end(); ++i) { Node *n = static_cast<Node*>(*i); @@ -369,6 +422,10 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) other()->setPosition(_saved_other_pos); } } + //if it is bspline but SHIFT or CONTROL are not pressed it fixes it in the original position + if(_pm().isBSpline() && !held_shift(*event) && !held_control(*event)){ + new_pos=_last_drag_origin(); + } move(new_pos); // needed for correct update, even though it's redundant _pm().update(); } @@ -427,13 +484,19 @@ static double snap_increment_degrees() { Glib::ustring Handle::_getTip(unsigned state) const { char const *more; + // a trick to mark as bspline if the node has no strength, we are going to use it later + // to show the appropiate messages. We cannot do it in any different way becasue the function is constant + + bool isBSpline = _pm().isBSpline(); bool can_shift_rotate = _parent->type() == NODE_CUSP && !other()->isDegenerate(); - if (can_shift_rotate) { + if (can_shift_rotate && !isBSpline) { more = C_("Path handle tip", "more: Shift, Ctrl, Alt"); - } else { + } else if(isBSpline){ + more = C_("Path handle tip", "more: Ctrl"); + }else { more = C_("Path handle tip", "more: Ctrl, Alt"); } - if (state_held_alt(state)) { + if (state_held_alt(state) && !isBSpline) { if (state_held_control(state)) { if (state_held_shift(state) && can_shift_rotate) { return format_tip(C_("Path handle tip", @@ -456,18 +519,24 @@ Glib::ustring Handle::_getTip(unsigned state) const } } else { if (state_held_control(state)) { - if (state_held_shift(state) && can_shift_rotate) { + if (state_held_shift(state) && can_shift_rotate && !isBSpline) { return format_tip(C_("Path handle tip", "<b>Shift+Ctrl</b>: snap rotation angle to %g° increments and rotate both handles"), snap_increment_degrees()); - } else { + } else if(isBSpline){ + return format_tip(C_("Path handle tip", + "<b>Ctrl</b>: Move handle by his actual steps in BSpline Live Effect")); + }else{ return format_tip(C_("Path handle tip", "<b>Ctrl</b>: snap rotation angle to %g° increments, click to retract"), snap_increment_degrees()); } - } else if (state_held_shift(state) && can_shift_rotate) { + } else if (state_held_shift(state) && can_shift_rotate && !isBSpline) { return C_("Path hande tip", "<b>Shift</b>: rotate both handles by the same angle"); + } else if(state_held_shift(state) && isBSpline){ + return C_("Path hande tip", + "<b>Shift</b>: move handle"); } } @@ -476,9 +545,13 @@ Glib::ustring Handle::_getTip(unsigned state) const return format_tip(C_("Path handle tip", "<b>Auto node handle</b>: drag to convert to smooth node (%s)"), more); default: - return format_tip(C_("Path handle tip", - "<b>%s</b>: drag to shape the segment (%s)"), - handle_type_to_localized_string(_parent->type()), more); + if(!isBSpline){ + return format_tip(C_("Path handle tip", + "<b>Auto node handle</b>: drag to convert to smooth node (%s)"), more); + }else{ + return format_tip(C_("Path handle tip", + "<b>BSpline node handle</b>: Shift to drag, double click to reset (%s)"), more); + } } } @@ -553,18 +626,80 @@ void Node::move(Geom::Point const &new_pos) // move handles when the node moves. Geom::Point old_pos = position(); Geom::Point delta = new_pos - position(); + + // save the previous nodes strength to apply it again once the node is moved + double nodeWeight = noPower; + double nextNodeWeight = noPower; + double prevNodeWeight = noPower; + Node *n = this; + Node * nextNode = n->nodeToward(n->front()); + Node * prevNode = n->nodeToward(n->back()); + nodeWeight = fmax(_pm().BSplineHandlePosition(n->front()),_pm().BSplineHandlePosition(n->back())); + if(prevNode){ + if(prevNode->isEndNode()){ + prevNodeWeight = _pm().BSplineHandlePosition(prevNode->front(),prevNode->front()); + } + } + if(nextNode){ + if(nextNode->isEndNode()){ + nextNodeWeight = _pm().BSplineHandlePosition(nextNode->back(),nextNode->back()); + } + } + setPosition(new_pos); + _front.setPosition(_front.position() + delta); _back.setPosition(_back.position() + delta); // if the node has a smooth handle after a line segment, it should be kept colinear // with the segment _fixNeighbors(old_pos, new_pos); + + // move the affected handlers. First the node ones, later the adjoining ones. + if(_pm().isBSpline()){ + _front.setPosition(_pm().BSplineHandleReposition(this->front(),nodeWeight)); + _back.setPosition(_pm().BSplineHandleReposition(this->back(),nodeWeight)); + if(prevNode){ + if(prevNode->isEndNode()){ + prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNodeWeight)); + }else{ + prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNode->back())); + } + } + if(nextNode){ + if(nextNode->isEndNode()){ + nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNodeWeight)); + }else{ + nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNode->front())); + } + } + } } void Node::transform(Geom::Affine const &m) { + Geom::Point old_pos = position(); + + // save the previous nodes strength to apply it again once the node is moved + double nodeWeight = noPower; + double nextNodeWeight = noPower; + double prevNodeWeight = noPower; + Node *n = this; + Node * nextNode = n->nodeToward(n->front()); + Node * prevNode = n->nodeToward(n->back()); + nodeWeight = _pm().BSplineHandlePosition(n->front()); + if(prevNode){ + if(prevNode->isEndNode()){ + prevNodeWeight = _pm().BSplineHandlePosition(prevNode->front(),prevNode->front()); + } + } + if(nextNode){ + if(nextNode->isEndNode()){ + nextNodeWeight = _pm().BSplineHandlePosition(nextNode->back(),nextNode->back()); + } + } + setPosition(position() * m); _front.setPosition(_front.position() * m); _back.setPosition(_back.position() * m); @@ -572,6 +707,26 @@ void Node::transform(Geom::Affine const &m) /* Affine transforms keep handle invariants for smooth and symmetric nodes, * but smooth nodes at ends of linear segments and auto nodes need special treatment */ _fixNeighbors(old_pos, position()); + + // move the involved handlers, first the node ones, later the adjoining ones + if(_pm().isBSpline()){ + _front.setPosition(_pm().BSplineHandleReposition(this->front(),nodeWeight)); + _back.setPosition(_pm().BSplineHandleReposition(this->back(),nodeWeight)); + if(prevNode){ + if(prevNode->isEndNode()){ + prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNodeWeight)); + }else{ + prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNode->back())); + } + } + if(nextNode){ + if(nextNode->isEndNode()){ + nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNodeWeight)); + }else{ + nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNode->front())); + } + } + } } Geom::Rect Node::bounds() const @@ -657,6 +812,7 @@ void Node::showHandles(bool v) if (!_back.isDegenerate()) { _back.setVisible(v); } + } void Node::updateHandles() @@ -758,6 +914,16 @@ void Node::setType(NodeType type, bool update_handles) break; default: break; } + /* in node type changes, about bspline traces, we can mantain them with noPower power in border mode, + or we give them the default power in curve mode */ + if(_pm().isBSpline()){ + double weight = noPower; + if(_pm().BSplineHandlePosition(this->front()) != noPower ){ + weight = defaultStartPower; + } + _front.setPosition(_pm().BSplineHandleReposition(this->front(),weight)); + _back.setPosition(_pm().BSplineHandleReposition(this->back(),weight)); + } } _type = type; _setControlType(nodeTypeToCtrlType(_type)); @@ -870,6 +1036,7 @@ bool Node::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent _selection.spatialGrow(this, dir); } return true; + default: break; } @@ -1004,6 +1171,11 @@ void Node::_setState(State state) case STATE_CLICKED: mgr.setActive(_canvas_item, true); mgr.setPrelight(_canvas_item, false); + //this shows the handlers when selecting the nodes + if(_pm().isBSpline()){ + this->front()->setPosition(_pm().BSplineHandleReposition(this->front())); + this->back()->setPosition(_pm().BSplineHandleReposition(this->back())); + } break; } SelectableControlPoint::_setState(state); @@ -1055,6 +1227,10 @@ bool Node::grabbed(GdkEventMotion *event) void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) { + if (tools_isactive(_desktop, TOOLS_NODES)) { + Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(_desktop->event_context); + nt->update_helperpath(); + } // For a note on how snapping is implemented in Inkscape, see snap.h. SnapManager &sm = _desktop->namedview->snap_manager; // even if we won't really snap, we might still call the one of the @@ -1258,6 +1434,7 @@ Node *Node::nodeAwayFrom(Handle *h) Glib::ustring Node::_getTip(unsigned state) const { + bool isBSpline = _pm().isBSpline(); if (state_held_shift(state)) { bool can_drag_out = (_next() && _front.isDegenerate()) || (_prev() && _back.isDegenerate()); if (can_drag_out) { @@ -1287,15 +1464,24 @@ Glib::ustring Node::_getTip(unsigned state) const // No modifiers: assemble tip from node type char const *nodetype = node_type_to_localized_string(_type); if (_selection.transformHandlesEnabled() && selected()) { - if (_selection.size() == 1) { + if (_selection.size() == 1 && !isBSpline) { return format_tip(C_("Path node tip", "<b>%s</b>: drag to shape the path (more: Shift, Ctrl, Alt)"), nodetype); + }else if(_selection.size() == 1){ + return format_tip(C_("Path node tip", + "<b>BSpline node</b>: %g weight, drag to shape the path (more: Shift, Ctrl, Alt)"),noPower/*this->bsplineWeight*/); } return format_tip(C_("Path node tip", "<b>%s</b>: drag to shape the path, click to toggle scale/rotation handles (more: Shift, Ctrl, Alt)"), nodetype); } - return format_tip(C_("Path node tip", - "<b>%s</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype); + if (!isBSpline) { + return format_tip(C_("Path node tip", + "<b>%s</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype); + }else{ + return format_tip(C_("Path node tip", + "<b>BSpline node</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)")); + + } } Glib::ustring Node::_getDragTip(GdkEventMotion */*event*/) const @@ -1452,7 +1638,37 @@ void NodeList::reverse() void NodeList::clear() { - for (iterator i = begin(); i != end();) erase (i++); + // ugly but more efficient clearing mechanism + std::vector<ControlPointSelection *> to_clear; + std::vector<std::pair<SelectableControlPoint *, long> > nodes; + long in = -1; + for (iterator i = begin(); i != end(); ++i) { + SelectableControlPoint *rm = static_cast<Node*>(i._node); + if (std::find(to_clear.begin(), to_clear.end(), &rm->_selection) == to_clear.end()) { + to_clear.push_back(&rm->_selection); + ++in; + } + nodes.push_back(std::make_pair(rm, in)); + } + for (size_t i = 0, e = nodes.size(); i != e; ++i) { + to_clear[nodes[i].second]->erase(nodes[i].first, false); + } + std::vector<std::vector<SelectableControlPoint *> > emission; + for (long i = 0, e = to_clear.size(); i != e; ++i) { + emission.push_back(std::vector<SelectableControlPoint *>()); + for (size_t j = 0, f = nodes.size(); j != f; ++j) { + if (nodes[j].second != i) + break; + emission[i].push_back(nodes[j].first); + } + } + + for (size_t i = 0, e = emission.size(); i != e; ++i) { + to_clear[i]->signal_selection_changed.emit(emission[i], false); + } + + for (iterator i = begin(); i != end();) + erase (i++); } NodeList::iterator NodeList::erase(iterator i) diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h index b2f338e2c..101af4817 100644 --- a/src/ui/tool/node.h +++ b/src/ui/tool/node.h @@ -41,6 +41,17 @@ template <typename> class NodeIterator; } } +/* +#if HAVE_TR1_UNORDERED_SET +namespace std { +namespace tr1 { +template <typename N> struct hash< Inkscape::UI::NodeIterator<N> >; +} +} +#endif +#endif +*/ + namespace Inkscape { namespace UI { @@ -109,7 +120,7 @@ public: protected: Handle(NodeSharedData const &data, Geom::Point const &initial_pos, Node *parent); - + virtual void handle_2button_press(); virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event); virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event); virtual bool grabbed(GdkEventMotion *event); @@ -123,6 +134,7 @@ protected: private: inline PathManipulator &_pm(); + inline PathManipulator &_pm() const; Node *_parent; // the handle's lifetime does not extend beyond that of the parent node, // so a naked pointer is OK and allows setting it during Node's construction SPCtrlLine *_handle_line; @@ -209,6 +221,7 @@ public: Node *nodeAwayFrom(Handle *h); NodeList &nodeList() { return *(static_cast<ListNode*>(this)->ln_list); } + NodeList &nodeList() const { return *(static_cast<ListNode const*>(this)->ln_list); } /** * Move the node to the bottom of its canvas group. @@ -255,6 +268,7 @@ private: Inkscape::SnapSourceType _snapSourceType() const; Inkscape::SnapTargetType _snapTargetType() const; inline PathManipulator &_pm(); + inline PathManipulator &_pm() const; /** Determine whether two nodes are joined by a linear segment. */ static bool _is_line_segment(Node *first, Node *second); @@ -482,10 +496,17 @@ inline double Handle::length() const { inline PathManipulator &Handle::_pm() { return _parent->_pm(); } +inline PathManipulator &Handle::_pm() const { + return _parent->_pm(); +} inline PathManipulator &Node::_pm() { return nodeList().subpathList().pm(); } +inline PathManipulator &Node::_pm() const { + return nodeList().subpathList().pm(); +} + // definitions for node iterator template <typename N> NodeIterator<N>::operator bool() const { diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index 338499672..52ff5d42c 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -11,6 +11,7 @@ */ #include "live_effects/lpe-powerstroke.h" +#include "live_effects/lpe-fillet-chamfer.h" #include <string> #include <sstream> #include <deque> @@ -42,6 +43,7 @@ #include "ui/tool/multi-path-manipulator.h" #include "xml/node.h" #include "xml/node-observer.h" +#include "live_effects/lpe-bspline.h" namespace Inkscape { namespace UI { @@ -54,6 +56,10 @@ enum PathChange { }; } // anonymous namespace +const double handleCubicGap = 0.01; +const double noPower = 0.0; +const double defaultStartPower = 0.3334; +const double defaultEndPower = 0.6667; /** * Notifies the path manipulator when something changes the path being edited @@ -102,7 +108,6 @@ private: }; void build_segment(Geom::PathBuilder &, Node *, Node *); - PathManipulator::PathManipulator(MultiPathManipulator &mpm, SPPath *path, Geom::Affine const &et, guint32 outline_color, Glib::ustring lpe_key) : PointManipulator(mpm._path_data.node_data.desktop, *mpm._path_data.node_data.selection) @@ -139,12 +144,14 @@ PathManipulator::PathManipulator(MultiPathManipulator &mpm, SPPath *path, _selection.signal_update.connect( sigc::bind(sigc::mem_fun(*this, &PathManipulator::update), false)); - _selection.signal_point_changed.connect( - sigc::mem_fun(*this, &PathManipulator::_selectionChanged)); + _selection.signal_selection_changed.connect( + sigc::mem_fun(*this, &PathManipulator::_selectionChangedM)); _desktop->signal_zoom_changed.connect( sigc::hide( sigc::mem_fun(*this, &PathManipulator::_updateOutlineOnZoomChange))); _createControlPointsFromGeometry(); + //Define if the path is BSpline on construction + isBSpline(true); } PathManipulator::~PathManipulator() @@ -662,6 +669,15 @@ unsigned PathManipulator::_deleteStretch(NodeList::iterator start, NodeList::ite nl.erase(start); start = next; } + // if we are removing, we readjust the handlers + if(isBSpline()){ + if(start.prev()){ + start.prev()->front()->setPosition(BSplineHandleReposition(start.prev()->front(),start.prev()->back())); + } + if(end){ + end->back()->setPosition(BSplineHandleReposition(end->back(),end->front())); + } + } return del_len; } @@ -816,7 +832,6 @@ void PathManipulator::scaleHandle(Node *n, int which, int dir, bool pixel) } h->setRelativePos(relpos); update(); - gchar const *key = which < 0 ? "handle:scale:left" : "handle:scale:right"; _commit(_("Scale handle"), key); } @@ -980,9 +995,37 @@ NodeList::iterator PathManipulator::subdivideSegment(NodeList::iterator first, d // set new handle positions Node *n = new Node(_multi_path_manipulator._path_data.node_data, seg2[0]); - n->back()->setPosition(seg1[2]); - n->front()->setPosition(seg2[1]); - n->setType(NODE_SMOOTH, false); + if(!isBSpline()){ + n->back()->setPosition(seg1[2]); + n->front()->setPosition(seg2[1]); + n->setType(NODE_SMOOTH, false); + } else { + const double handleCubicGap = 0.01; + Geom::D2< Geom::SBasis > SBasisInsideNodes; + SPCurve *lineInsideNodes = new SPCurve(); + if(second->back()->isDegenerate()){ + lineInsideNodes->moveto(n->position()); + lineInsideNodes->lineto(second->position()); + SBasisInsideNodes = lineInsideNodes->first_segment()->toSBasis(); + Geom::Point next = SBasisInsideNodes.valueAt(defaultStartPower); + next = Geom::Point(next[Geom::X] + handleCubicGap,next[Geom::Y] + handleCubicGap); + lineInsideNodes->reset(); + n->front()->setPosition(next); + }else{ + n->front()->setPosition(seg2[1]); + } + if(first->front()->isDegenerate()){ + lineInsideNodes->moveto(n->position()); + lineInsideNodes->lineto(first->position()); + SBasisInsideNodes = lineInsideNodes->first_segment()->toSBasis(); + Geom::Point previous = SBasisInsideNodes.valueAt(defaultStartPower); + previous = Geom::Point(previous[Geom::X] + handleCubicGap,previous[Geom::Y] + handleCubicGap); + n->back()->setPosition(previous); + }else{ + n->back()->setPosition(seg1[2]); + } + n->setType(NODE_CUSP, false); + } inserted = list.insert(insert_at, n); first->front()->move(seg1[1]); @@ -1104,7 +1147,6 @@ void PathManipulator::_createControlPointsFromGeometry() Geom::Curve const &cseg = pit->back_closed(); bool fuse_ends = pit->closed() && Geom::are_near(cseg.initialPoint(), cseg.finalPoint()); - for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) { Geom::Point pos = cit->finalPoint(); Node *current_node; @@ -1171,6 +1213,93 @@ void PathManipulator::_createControlPointsFromGeometry() } } +//determines if the trace has a bspline effect and the number of steps that it takes +int PathManipulator::BSplineGetSteps() const { + + LivePathEffect::LPEBSpline const *lpe_bsp = NULL; + + if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()){ + Inkscape::LivePathEffect::Effect const *thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::BSPLINE); + if(thisEffect){ + lpe_bsp = dynamic_cast<LivePathEffect::LPEBSpline const*>(thisEffect->getLPEObj()->get_lpe()); + } + } + int steps = 0; + if(lpe_bsp){ + steps = lpe_bsp->steps+1; + } + return steps; +} + +// determines if the trace has bspline effect +bool PathManipulator::isBSpline(bool recalculate){ + if(recalculate){ + _is_bspline = this->BSplineGetSteps() > 0; + } + return _is_bspline; +} + +bool PathManipulator::isBSpline() const { + return BSplineGetSteps() > 0; +} + +// returns the corresponding strength to the position of the handlers +double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2){ + using Geom::X; + using Geom::Y; + if(h2){ + h = h2; + } + double pos = noPower; + const double handleCubicGap = 0.01; + Node *n = h->parent(); + Node * nextNode = NULL; + nextNode = n->nodeToward(h); + if(nextNode){ + SPCurve *lineInsideNodes = new SPCurve(); + lineInsideNodes->moveto(n->position()); + lineInsideNodes->lineto(nextNode->position()); + if(!are_near(h->position(), n->position())){ + pos = Geom::nearest_point(Geom::Point(h->position()[X] - handleCubicGap, h->position()[Y] - handleCubicGap), *lineInsideNodes->first_segment()); + } + } + if (pos == noPower && !h2){ + return BSplineHandlePosition(h, h->other()); + } + return pos; +} + +// give the location for the handler in the corresponding position +Geom::Point PathManipulator::BSplineHandleReposition(Handle *h, Handle *h2){ + double pos = this->BSplineHandlePosition(h, h2); + return BSplineHandleReposition(h,pos); +} + +// give the location for the handler to the specified position +Geom::Point PathManipulator::BSplineHandleReposition(Handle *h,double pos){ + using Geom::X; + using Geom::Y; + const double handleCubicGap = 0.01; + Geom::Point ret = h->position(); + Node *n = h->parent(); + Geom::D2< Geom::SBasis > SBasisInsideNodes; + SPCurve *lineInsideNodes = new SPCurve(); + Node * nextNode = NULL; + nextNode = n->nodeToward(h); + if(nextNode && pos != noPower){ + lineInsideNodes->moveto(n->position()); + lineInsideNodes->lineto(nextNode->position()); + SBasisInsideNodes = lineInsideNodes->first_segment()->toSBasis(); + ret = SBasisInsideNodes.valueAt(pos); + ret = Geom::Point(ret[X] + handleCubicGap,ret[Y] + handleCubicGap); + }else{ + if(pos == noPower){ + ret = n->position(); + } + } + return ret; +} + /** Construct the geometric representation of nodes and handles, update the outline * and display * \param alert_LPE if true, first the LPE is warned what the new path is going to be before updating it @@ -1178,6 +1307,8 @@ void PathManipulator::_createControlPointsFromGeometry() void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE) { Geom::PathBuilder builder; + //Refresh if is bspline some times -think on path change selection, this value get lost + isBSpline(true); for (std::list<SubpathPtr>::iterator spi = _subpaths.begin(); spi != _subpaths.end(); ) { SubpathPtr subpath = *spi; if (subpath->empty()) { @@ -1186,7 +1317,6 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE) } NodeList::iterator prev = subpath->begin(); builder.moveTo(prev->position()); - for (NodeList::iterator i = ++subpath->begin(); i != subpath->end(); ++i) { build_segment(builder, prev.ptr(), i.ptr()); prev = i; @@ -1207,11 +1337,20 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE) _spcurve->set_pathvector(pathv); if (alert_LPE) { /// \todo note that _path can be an Inkscape::LivePathEffect::Effect* too, kind of confusing, rework member naming? - if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()) { - PathEffectList effect_list = _path->getEffectList(); - LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast<LivePathEffect::LPEPowerStroke*>( effect_list.front()->lpeobject->get_lpe() ); - if (lpe_pwr) { - lpe_pwr->adjustForNewPath(pathv); + if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()){ + Inkscape::LivePathEffect::Effect* thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::POWERSTROKE); + if(thisEffect){ + LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast<LivePathEffect::LPEPowerStroke*>(thisEffect->getLPEObj()->get_lpe()); + if (lpe_pwr) { + lpe_pwr->adjustForNewPath(pathv); + } + } + thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::FILLET_CHAMFER); + if(thisEffect){ + LivePathEffect::LPEFilletChamfer *lpe_fll = dynamic_cast<LivePathEffect::LPEFilletChamfer*>(thisEffect->getLPEObj()->get_lpe()); + if (lpe_fll) { + lpe_fll->adjustForNewPath(pathv); + } } } } @@ -1419,6 +1558,12 @@ bool PathManipulator::_handleClicked(Handle *h, GdkEventButton *event) return false; } +void PathManipulator::_selectionChangedM(std::vector<SelectableControlPoint *> pvec, bool selected) { + for (size_t n = 0, e = pvec.size(); n < e; ++n) { + _selectionChanged(pvec[n], selected); + } +} + void PathManipulator::_selectionChanged(SelectableControlPoint *p, bool selected) { if (selected) ++_num_selected; diff --git a/src/ui/tool/path-manipulator.h b/src/ui/tool/path-manipulator.h index 7a13ce723..4d2bf4300 100644 --- a/src/ui/tool/path-manipulator.h +++ b/src/ui/tool/path-manipulator.h @@ -19,6 +19,7 @@ #include <boost/weak_ptr.hpp> #include "ui/tool/node.h" #include "ui/tool/manipulator.h" +#include "live_effects/lpe-bspline.h" struct SPCanvasItem; class SPCurve; @@ -95,6 +96,7 @@ public: NodeList::iterator extremeNode(NodeList::iterator origin, bool search_selected, bool search_unselected, bool closest); + int BSplineGetSteps() const; // this is necessary for Tab-selection in MultiPathManipulator SubpathList &subpathList() { return _subpaths; } @@ -104,6 +106,12 @@ private: typedef boost::shared_ptr<NodeList> SubpathPtr; void _createControlPointsFromGeometry(); + + bool isBSpline(bool recalculate = false); + bool isBSpline() const; + double BSplineHandlePosition(Handle *h, Handle *h2 = NULL); + Geom::Point BSplineHandleReposition(Handle *h, Handle *h2 = NULL); + Geom::Point BSplineHandleReposition(Handle *h, double pos); void _createGeometryFromControlPoints(bool alert_LPE = false); unsigned _deleteStretch(NodeList::iterator first, NodeList::iterator last, bool keep_shape); std::string _createTypeString(); @@ -114,7 +122,8 @@ private: Glib::ustring _nodetypesKey(); Inkscape::XML::Node *_getXMLNode(); - void _selectionChanged(SelectableControlPoint *p, bool selected); + void _selectionChangedM(std::vector<SelectableControlPoint *> pvec, bool selected); + void _selectionChanged(SelectableControlPoint * p, bool selected); bool _nodeClicked(Node *, GdkEventButton *); void _handleGrabbed(); bool _handleClicked(Handle *, GdkEventButton *); @@ -145,6 +154,7 @@ private: bool _show_path_direction; bool _live_outline; bool _live_objects; + bool _is_bspline; Glib::ustring _lpe_key; friend class PathManipulatorObserver; diff --git a/src/ui/tool/selectable-control-point.h b/src/ui/tool/selectable-control-point.h index 8acfc1168..362d4addc 100644 --- a/src/ui/tool/selectable-control-point.h +++ b/src/ui/tool/selectable-control-point.h @@ -28,6 +28,7 @@ public: virtual Geom::Rect bounds() const { return Geom::Rect(position(), position()); } + friend class NodeList; protected: diff --git a/src/ui/tools-switch.cpp b/src/ui/tools-switch.cpp new file mode 100644 index 000000000..47b9d2832 --- /dev/null +++ b/src/ui/tools-switch.cpp @@ -0,0 +1,201 @@ +/* + * Utility functions for switching tools (= contexts) + * + * Authors: + * bulia byak <buliabyak@users.sf.net> + * Josh Andler <scislac@users.sf.net> + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) 2003-2007 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <gtkmm.h> // prevents deprecation warnings + +#include <cstring> +#include <string> + +#include "inkscape-private.h" +#include "desktop.h" +#include "desktop-handles.h" +#include <glibmm/i18n.h> + +#include <xml/repr.h> + +#include "ui/tools-switch.h" + +#include "box3d.h" +#include "sp-ellipse.h" +#include "sp-flowtext.h" +#include "sp-offset.h" +#include "sp-path.h" +#include "sp-rect.h" +#include "sp-star.h" +#include "sp-spiral.h" +#include "sp-text.h" + +// TODO: How many of these are actually needed? +#include "ui/tools/arc-tool.h" +#include "ui/tools/box3d-tool.h" +#include "ui/tools/calligraphic-tool.h" +#include "ui/tools/connector-tool.h" +#include "ui/tools/dropper-tool.h" +#include "ui/tools/eraser-tool.h" +#include "ui/tools/flood-tool.h" +#include "ui/tools/gradient-tool.h" +#include "ui/tools/lpe-tool.h" +#include "ui/tools/measure-tool.h" +#include "ui/tools/mesh-tool.h" +#include "ui/tools/node-tool.h" +#include "ui/tools/pen-tool.h" +#include "ui/tools/pencil-tool.h" +#include "ui/tools/rect-tool.h" +#include "ui/tools/select-tool.h" +#include "ui/tools/spiral-tool.h" +#include "ui/tools/spray-tool.h" +#include "ui/tools/text-tool.h" +#include "ui/tools/tweak-tool.h" +#include "ui/tools/zoom-tool.h" + +#include "message-context.h" + +using Inkscape::UI::Tools::ToolBase; + +static char const *const tool_names[] = { + NULL, + "/tools/select", + "/tools/nodes", + "/tools/tweak", + "/tools/spray", + "/tools/shapes/rect", + "/tools/shapes/3dbox", + "/tools/shapes/arc", + "/tools/shapes/star", + "/tools/shapes/spiral", + "/tools/freehand/pencil", + "/tools/freehand/pen", + "/tools/calligraphic", + "/tools/text", + "/tools/gradient", + "/tools/mesh", + "/tools/zoom", + "/tools/measure", + "/tools/dropper", + "/tools/connector", + "/tools/paintbucket", + "/tools/eraser", + "/tools/lpetool", + NULL +}; + +// TODO: HEY! these belong to the tools themselves! +static char const *const tool_msg[] = { + NULL, + N_("<b>Click</b> to Select and Transform objects, <b>Drag</b> to select many objects."), + N_("Modify selected path points (nodes) directly."), + N_("To tweak a path by pushing, select it and drag over it."), + N_("<b>Drag</b>, <b>click</b> or <b>click and scroll</b> to spray the selected objects."), + N_("<b>Drag</b> to create a rectangle. <b>Drag controls</b> to round corners and resize. <b>Click</b> to select."), + N_("<b>Drag</b> to create a 3D box. <b>Drag controls</b> to resize in perspective. <b>Click</b> to select (with <b>Ctrl+Alt</b> for single faces)."), + N_("<b>Drag</b> to create an ellipse. <b>Drag controls</b> to make an arc or segment. <b>Click</b> to select."), + N_("<b>Drag</b> to create a star. <b>Drag controls</b> to edit the star shape. <b>Click</b> to select."), + N_("<b>Drag</b> to create a spiral. <b>Drag controls</b> to edit the spiral shape. <b>Click</b> to select."), + N_("<b>Drag</b> to create a freehand line. <b>Shift</b> appends to selected path, <b>Alt</b> activates sketch mode."), + N_("<b>Click</b> or <b>click and drag</b> to start a path; with <b>Shift</b> to append to selected path. <b>Ctrl+click</b> to create single dots (straight line modes only)."), + N_("<b>Drag</b> to draw a calligraphic stroke; with <b>Ctrl</b> to track a guide path. <b>Arrow keys</b> adjust width (left/right) and angle (up/down)."), + N_("<b>Click</b> to select or create text, <b>drag</b> to create flowed text; then type."), + N_("<b>Drag</b> or <b>double click</b> to create a gradient on selected objects, <b>drag handles</b> to adjust gradients."), + N_("<b>Drag</b> or <b>double click</b> to create a mesh on selected objects, <b>drag handles</b> to adjust meshes."), + N_("<b>Click</b> or <b>drag around an area</b> to zoom in, <b>Shift+click</b> to zoom out."), + N_("<b>Drag</b> to measure the dimensions of objects."), + N_("<b>Click</b> to set fill, <b>Shift+click</b> to set stroke; <b>drag</b> to average color in area; with <b>Alt</b> to pick inverse color; <b>Ctrl+C</b> to copy the color under mouse to clipboard"), + N_("<b>Click and drag</b> between shapes to create a connector."), + N_("<b>Click</b> to paint a bounded area, <b>Shift+click</b> to union the new fill with the current selection, <b>Ctrl+click</b> to change the clicked object's fill and stroke to the current setting."), + N_("<b>Drag</b> to erase."), + N_("Choose a subtool from the toolbar"), +}; + +static int +tools_prefpath2num(char const *id) +{ + int i = 1; + while (tool_names[i]) { + if (strcmp(tool_names[i], id) == 0) + return i; + else i++; + } + g_assert( 0 == TOOLS_INVALID ); + return 0; //nothing found +} + +int +tools_isactive(SPDesktop *dt, unsigned num) +{ + g_assert( num < G_N_ELEMENTS(tool_names) ); + if (dynamic_cast<ToolBase *>(dt->event_context)) { + return dt->event_context->pref_observer->observed_path == tool_names[num]; + } else { + return FALSE; + } +} + +int +tools_active(SPDesktop *dt) +{ + return tools_prefpath2num(dt->event_context->pref_observer->observed_path.data()); +} + +void +tools_switch(SPDesktop *dt, int num) +{ + dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, gettext( tool_msg[num] ) ); + if (dt) { + // This event may change the above message + dt->_tool_changed.emit(num); + } + + dt->set_event_context2(tool_names[num]); + /* fixme: This is really ugly hack. We should bind and unbind class methods */ + /* First 4 tools use guides, first is undefined but we don't care */ + dt->activate_guides(num < 5); + inkscape_eventcontext_set(dt->getEventContext()); +} + +void tools_switch_by_item(SPDesktop *dt, SPItem *item, Geom::Point const p) +{ + if (dynamic_cast<SPRect *>(item)) { + tools_switch(dt, TOOLS_SHAPES_RECT); + } else if (dynamic_cast<SPBox3D *>(item)) { + tools_switch(dt, TOOLS_SHAPES_3DBOX); + } else if (dynamic_cast<SPGenericEllipse *>(item)) { + tools_switch(dt, TOOLS_SHAPES_ARC); + } else if (dynamic_cast<SPStar *>(item)) { + tools_switch(dt, TOOLS_SHAPES_STAR); + } else if (dynamic_cast<SPSpiral *>(item)) { + tools_switch(dt, TOOLS_SHAPES_SPIRAL); + } else if (dynamic_cast<SPPath *>(item)) { + if (Inkscape::UI::Tools::cc_item_is_connector(item)) { + tools_switch(dt, TOOLS_CONNECTOR); + } + else { + tools_switch(dt, TOOLS_NODES); + } + } else if (dynamic_cast<SPText *>(item) || dynamic_cast<SPFlowtext *>(item)) { + tools_switch(dt, TOOLS_TEXT); + sp_text_context_place_cursor_at (SP_TEXT_CONTEXT(dt->event_context), item, p); + } else if (dynamic_cast<SPOffset *>(item)) { + tools_switch(dt, TOOLS_NODES); + } +} + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/tools-switch.h b/src/ui/tools-switch.h new file mode 100644 index 000000000..280837e87 --- /dev/null +++ b/src/ui/tools-switch.h @@ -0,0 +1,64 @@ +/* + * Utility functions for switching tools (= contexts) + * + * Authors: + * bulia byak <bulia@dr.com> + * + * Copyright (C) 2003 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_TOOLS_SWITCH_H +#define SEEN_TOOLS_SWITCH_H + +class SPDesktop; +class SPItem; +namespace Geom { +class Point; +} + + +enum { + TOOLS_INVALID, + TOOLS_SELECT, + TOOLS_NODES, + TOOLS_TWEAK, + TOOLS_SPRAY, + TOOLS_SHAPES_RECT, + TOOLS_SHAPES_3DBOX, + TOOLS_SHAPES_ARC, + TOOLS_SHAPES_STAR, + TOOLS_SHAPES_SPIRAL, + TOOLS_FREEHAND_PENCIL, + TOOLS_FREEHAND_PEN, + TOOLS_CALLIGRAPHIC, + TOOLS_TEXT, + TOOLS_GRADIENT, + TOOLS_MESH, + TOOLS_ZOOM, + TOOLS_MEASURE, + TOOLS_DROPPER, + TOOLS_CONNECTOR, + TOOLS_PAINTBUCKET, + TOOLS_ERASER, + TOOLS_LPETOOL +}; + +int tools_isactive(SPDesktop *dt, unsigned num); +int tools_active(SPDesktop *dt); +void tools_switch(SPDesktop *dt, int num); +void tools_switch_by_item (SPDesktop *dt, SPItem *item, Geom::Point const p); + +#endif // !SEEN_TOOLS_SWITCH_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/ui/tools/arc-tool.cpp b/src/ui/tools/arc-tool.cpp index 43f8eb9e1..4f64ade25 100644 --- a/src/ui/tools/arc-tool.cpp +++ b/src/ui/tools/arc-tool.cpp @@ -40,7 +40,7 @@ #include "desktop-style.h" #include "context-fns.h" #include "verbs.h" -#include "shape-editor.h" +#include "ui/shape-editor.h" #include "ui/tools/tool-base.h" #include "ui/tools/arc-tool.h" @@ -48,7 +48,7 @@ using Inkscape::DocumentUndo; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { @@ -102,8 +102,8 @@ ArcTool::~ArcTool() { * destroys old and creates new knotholder. */ void ArcTool::selection_changed(Inkscape::Selection* selection) { - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); } void ArcTool::setup() { @@ -115,7 +115,7 @@ void ArcTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } this->sel_changed_connection.disconnect(); diff --git a/src/ui/tools/arc-tool.h b/src/ui/tools/arc-tool.h index c4c67660d..55cbaf78c 100644 --- a/src/ui/tools/arc-tool.h +++ b/src/ui/tools/arc-tool.h @@ -30,7 +30,7 @@ namespace Inkscape { } #define SP_ARC_CONTEXT(obj) (dynamic_cast<Inkscape::UI::Tools::ArcTool*>((Inkscape::UI::Tools::ToolBase*)obj)) -#define SP_IS_ARC_CONTEXT(obj) (dynamic_cast<const Inkscape::UI::Tools::ArcTool*>(const Inkscape::UI::Tools::ToolBase*(obj)) != NULL) +#define SP_IS_ARC_CONTEXT(obj) (dynamic_cast<const Inkscape::UI::Tools::ArcTool*>(obj) != NULL) namespace Inkscape { namespace UI { diff --git a/src/ui/tools/box3d-tool.cpp b/src/ui/tools/box3d-tool.cpp index e00894d55..0a20a0842 100644 --- a/src/ui/tools/box3d-tool.cpp +++ b/src/ui/tools/box3d-tool.cpp @@ -47,12 +47,12 @@ #include "box3d-side.h" #include "document-private.h" #include "line-geometry.h" -#include "shape-editor.h" +#include "ui/shape-editor.h" #include "verbs.h" using Inkscape::DocumentUndo; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { @@ -112,8 +112,8 @@ Box3dTool::~Box3dTool() { * destroys old and creates new knotholder. */ void Box3dTool::selection_changed(Inkscape::Selection* selection) { - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); if (selection->perspList().size() == 1) { // selecting a single box changes the current perspective @@ -147,7 +147,7 @@ void Box3dTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } this->sel_changed_connection.disconnect(); diff --git a/src/ui/tools/calligraphic-tool.cpp b/src/ui/tools/calligraphic-tool.cpp index 64097e834..d297fe5e1 100644 --- a/src/ui/tools/calligraphic-tool.cpp +++ b/src/ui/tools/calligraphic-tool.cpp @@ -82,7 +82,7 @@ using Inkscape::DocumentUndo; #define DYNA_MIN_WIDTH 1.0e-6 -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { diff --git a/src/ui/tools/connector-tool.cpp b/src/ui/tools/connector-tool.cpp index d1355e807..7b5c84c16 100644 --- a/src/ui/tools/connector-tool.cpp +++ b/src/ui/tools/connector-tool.cpp @@ -109,7 +109,7 @@ using Inkscape::DocumentUndo; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { diff --git a/src/ui/tools/dropper-tool.cpp b/src/ui/tools/dropper-tool.cpp index 99d42a211..6c55f7484 100644 --- a/src/ui/tools/dropper-tool.cpp +++ b/src/ui/tools/dropper-tool.cpp @@ -52,7 +52,7 @@ using Inkscape::DocumentUndo; static GdkCursor *cursor_dropper_fill = NULL; static GdkCursor *cursor_dropper_stroke = NULL; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { diff --git a/src/ui/tools/eraser-tool.cpp b/src/ui/tools/eraser-tool.cpp index 4106785e7..bf4015b4c 100644 --- a/src/ui/tools/eraser-tool.cpp +++ b/src/ui/tools/eraser-tool.cpp @@ -84,7 +84,7 @@ using Inkscape::DocumentUndo; #define DRAG_DEFAULT 1.0 #define DRAG_MAX 1.0 -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { diff --git a/src/ui/tools/flood-tool.cpp b/src/ui/tools/flood-tool.cpp index d74848dc6..5745fc9cc 100644 --- a/src/ui/tools/flood-tool.cpp +++ b/src/ui/tools/flood-tool.cpp @@ -50,7 +50,7 @@ #include "preferences.h" #include "rubberband.h" #include "selection.h" -#include "shape-editor.h" +#include "ui/shape-editor.h" #include "sp-defs.h" #include "sp-item.h" #include "splivarot.h" @@ -74,7 +74,7 @@ using Inkscape::Display::ExtractARGB32; using Inkscape::Display::ExtractRGB32; using Inkscape::Display::AssembleARGB32; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { @@ -119,8 +119,8 @@ FloodTool::~FloodTool() { * destroys old and creates new knotholder. */ void FloodTool::selection_changed(Inkscape::Selection* selection) { - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); } void FloodTool::setup() { @@ -130,7 +130,7 @@ void FloodTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } this->sel_changed_connection.disconnect(); diff --git a/src/ui/tools/flood-tool.h b/src/ui/tools/flood-tool.h index 3ed670e8b..5104a42e9 100644 --- a/src/ui/tools/flood-tool.h +++ b/src/ui/tools/flood-tool.h @@ -11,9 +11,7 @@ * Released under GNU GPL */ -#include <stddef.h> -#include <sigc++/sigc++.h> -#include <gtk/gtk.h> +#include <sigc++/connection.h> #include "ui/tools/tool-base.h" #define SP_FLOOD_CONTEXT(obj) (dynamic_cast<Inkscape::UI::Tools::FloodTool*>((Inkscape::UI::Tools::ToolBase*)obj)) diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index 37a170bc9..6434c30d2 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -30,7 +30,7 @@ #include "desktop-handles.h" #include "desktop-style.h" #include "document.h" -#include "draw-anchor.h" +#include "ui/draw-anchor.h" #include "macros.h" #include "message-stack.h" #include "ui/tools/pen-tool.h" @@ -44,6 +44,8 @@ #include "live_effects/lpe-powerstroke.h" #include "style.h" #include "ui/control-manager.h" +// clipboard support +#include "ui/clipboard.h" #include "ui/tools/freehand-base.h" #include <gdk/gdkkeysyms.h> @@ -78,6 +80,7 @@ FreehandBase::FreehandBase(gchar const *const *cursor_shape, gint hot_x, gint ho , red_color(0xff00007f) , blue_color(0x0000ff7f) , green_color(0x00ff007f) + , highlight_color(0x0000007f) , red_bpath(NULL) , red_curve(NULL) , blue_bpath(NULL) @@ -89,6 +92,7 @@ FreehandBase::FreehandBase(gchar const *const *cursor_shape, gint hot_x, gint ho , white_item(NULL) , white_curves(NULL) , white_anchors(NULL) + , overwriteCurve(NULL) , sa(NULL) , ea(NULL) , waiting_LPE_type(Inkscape::LivePathEffect::INVALID_LPE) @@ -144,6 +148,9 @@ void FreehandBase::setup() { this->green_anchor = NULL; this->green_closed = FALSE; + // Create start anchor alternative curve + this->overwriteCurve = new SPCurve(); + this->attach = TRUE; spdc_attach_selection(this, this->selection); } @@ -270,8 +277,18 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, if (prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 1) { Effect::createAndApply(SPIRO, dc->desktop->doc(), item); } + //add the bspline node in the waiting effects + if (prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 2) { + Effect::createAndApply(BSPLINE, dc->desktop->doc(), item); + } + + //Store the clipboard path to apply in the future without the use of clipboard + static Geom::PathVector previous_shape_pathv; + enum shapeType { NONE, TRIANGLE_IN, TRIANGLE_OUT, ELLIPSE, CLIPBOARD, LAST_APPLIED }; + static shapeType previous_shape_type = NONE; - int shape = prefs->getInt(tool_name(dc) + "/shape", 0); + + shapeType shape = (shapeType)prefs->getInt(tool_name(dc) + "/shape", 0); bool shape_applied = false; SPCSSAttr *css_item = sp_css_attr_from_object(item, SP_STYLE_FLAG_ALWAYS); const char *cstroke = sp_repr_css_property(css_item, "stroke", "none"); @@ -279,11 +296,18 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, #define SHAPE_LENGTH 10 #define SHAPE_HEIGHT 10 + if(shape == LAST_APPLIED){ + shape = previous_shape_type; + if(shape == CLIPBOARD){ + shape = LAST_APPLIED; + } + } + switch (shape) { - case 0: + case NONE: // don't apply any shape break; - case 1: + case TRIANGLE_IN: { // "triangle in" std::vector<Geom::Point> points(1); @@ -293,7 +317,7 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, shape_applied = true; break; } - case 2: + case TRIANGLE_OUT: { // "triangle out" guint curve_length = curve->get_segment_count(); @@ -304,7 +328,7 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, shape_applied = true; break; } - case 3: + case ELLIPSE: { // "ellipse" SPCurve *c = new SPCurve(); @@ -317,22 +341,40 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, c->closepath(); spdc_paste_curve_as_freehand_shape(c, dc, item); c->unref(); + shape_applied = true; break; } - case 4: + case CLIPBOARD: { // take shape from clipboard; TODO: catch the case where clipboard is empty Effect::createAndApply(PATTERN_ALONG_PATH, dc->desktop->doc(), item); Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); static_cast<LPEPatternAlongPath*>(lpe)->pattern.on_paste_button_click(); + Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); + Glib::ustring svgd = cm->getPathParameter(SP_ACTIVE_DESKTOP); + previous_shape_pathv = sp_svg_read_pathv(svgd.data()); shape_applied = true; break; } + case LAST_APPLIED: + { + if(previous_shape_pathv.size() != 0){ + SPCurve * c = new SPCurve(); + c->set_pathvector(previous_shape_pathv); + spdc_paste_curve_as_freehand_shape(c, dc, item); + c->unref(); + + shape_applied = true; + } + break; + } default: break; } + previous_shape_type = shape; + if (shape_applied) { // apply original stroke color as fill and unset stroke; then return SPCSSAttr *css = sp_repr_css_attr_new(); @@ -486,7 +528,7 @@ void spdc_concat_colors_and_flush(FreehandBase *dc, gboolean forceclosed) { // Concat RBG SPCurve *c = dc->green_curve; - + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); // Green dc->green_curve = new SPCurve(); while (dc->green_bpaths) { @@ -533,9 +575,20 @@ void spdc_concat_colors_and_flush(FreehandBase *dc, gboolean forceclosed) if (dc->sa->start && !(dc->sa->curve->is_closed()) ) { c = reverse_then_unref(c); } - dc->sa->curve->append_continuous(c, 0.0625); - c->unref(); - dc->sa->curve->closepath_current(); + if(prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 1 || + prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 2){ + dc->overwriteCurve->append_continuous(c, 0.0625); + c->unref(); + dc->overwriteCurve->closepath_current(); + if(dc->sa){ + dc->white_curves = g_slist_remove(dc->white_curves, dc->sa->curve); + dc->white_curves = g_slist_append(dc->white_curves, dc->overwriteCurve); + } + }else{ + dc->sa->curve->append_continuous(c, 0.0625); + c->unref(); + dc->sa->curve->closepath_current(); + } spdc_flush_white(dc, NULL); return; } @@ -544,6 +597,10 @@ void spdc_concat_colors_and_flush(FreehandBase *dc, gboolean forceclosed) if (dc->sa) { SPCurve *s = dc->sa->curve; dc->white_curves = g_slist_remove(dc->white_curves, s); + if(prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 1 || + prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 2){ + s = dc->overwriteCurve; + } if (dc->sa->start) { s = reverse_then_unref(s); } @@ -556,6 +613,25 @@ void spdc_concat_colors_and_flush(FreehandBase *dc, gboolean forceclosed) if (!dc->ea->start) { e = reverse_then_unref(e); } + if(prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 1 || + prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 2){ + e = reverse_then_unref(e); + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*e->last_segment()); + SPCurve *lastSeg = new SPCurve(); + if(cubic){ + lastSeg->moveto((*cubic)[0]); + lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]); + if( e->get_segment_count() == 1){ + e = lastSeg; + }else{ + //we eliminate the last segment + e->backspace(); + //and we add it again with the recreation + e->append_continuous(lastSeg, 0.0625); + } + } + e = reverse_then_unref(e); + } c->append_continuous(e, 0.0625); e->unref(); } @@ -599,6 +675,7 @@ static void spdc_flush_white(FreehandBase *dc, SPCurve *gc) bool has_lpe = false; Inkscape::XML::Node *repr; + if (dc->white_item) { repr = dc->white_item->getRepr(); has_lpe = SP_LPE_ITEM(dc->white_item)->hasPathEffectRecursive(); @@ -662,7 +739,6 @@ SPDrawAnchor *spdc_test_inside(FreehandBase *dc, Geom::Point p) active = na; } } - return active; } diff --git a/src/ui/tools/freehand-base.h b/src/ui/tools/freehand-base.h index 5a4b91800..6b4265215 100644 --- a/src/ui/tools/freehand-base.h +++ b/src/ui/tools/freehand-base.h @@ -55,6 +55,7 @@ public: guint32 red_color; guint32 blue_color; guint32 green_color; + guint32 highlight_color; // Red SPCanvasItem *red_bpath; @@ -75,12 +76,17 @@ public: GSList *white_curves; GSList *white_anchors; + //ALternative curve to use on continuing exisiting curve in case of bspline or spirolive + //because usigh anchor curves give memory and random bugs, - and obscure code- in some plataform reported by su_v in mac + SPCurve *overwriteCurve; + // Start anchor SPDrawAnchor *sa; // End anchor SPDrawAnchor *ea; + /* type of the LPE that is to be applied automatically to a finished path (if any) */ Inkscape::LivePathEffect::EffectType waiting_LPE_type; diff --git a/src/ui/tools/gradient-tool.cpp b/src/ui/tools/gradient-tool.cpp index a0bbfbaf1..9c853917e 100644 --- a/src/ui/tools/gradient-tool.cpp +++ b/src/ui/tools/gradient-tool.cpp @@ -51,7 +51,7 @@ using Inkscape::DocumentUndo; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { @@ -213,17 +213,20 @@ sp_gradient_context_is_over_line (GradientTool *rc, SPItem *item, Geom::Point ev //Translate mouse point into proper coord system rc->mousepoint_doc = desktop->w2d(event_p); - SPCtrlLine* line = SP_CTRLLINE(item); + if (SP_IS_CTRLLINE(item)) { + SPCtrlLine* line = SP_CTRLLINE(item); - Geom::LineSegment ls(line->s, line->e); - Geom::Point nearest = ls.pointAt(ls.nearestPoint(rc->mousepoint_doc)); - double dist_screen = Geom::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom(); + Geom::LineSegment ls(line->s, line->e); + Geom::Point nearest = ls.pointAt(ls.nearestPoint(rc->mousepoint_doc)); + double dist_screen = Geom::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom(); - double tolerance = (double) SP_EVENT_CONTEXT(rc)->tolerance; + double tolerance = (double) SP_EVENT_CONTEXT(rc)->tolerance; - bool close = (dist_screen < tolerance); + bool close = (dist_screen < tolerance); - return close; + return close; + } + return false; } static std::vector<Geom::Point> diff --git a/src/ui/tools/lpe-tool.cpp b/src/ui/tools/lpe-tool.cpp index 9ab6d7814..1fd1ebf8c 100644 --- a/src/ui/tools/lpe-tool.cpp +++ b/src/ui/tools/lpe-tool.cpp @@ -28,7 +28,7 @@ #include "desktop.h" #include "message-context.h" #include "preferences.h" -#include "shape-editor.h" +#include "ui/shape-editor.h" #include "selection.h" #include "desktop-handles.h" #include "document.h" @@ -59,7 +59,7 @@ SubtoolEntry lpesubtools[] = { }; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { @@ -129,8 +129,7 @@ void LpeTool::setup() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (item) { - this->shape_editor->set_item(item, SH_NODEPATH); - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } if (prefs->getBool("/tools/lpetool/selcue")) { @@ -146,9 +145,9 @@ void sp_lpetool_context_selection_changed(Inkscape::Selection *selection, gpoint { LpeTool *lc = SP_LPETOOL_CONTEXT(data); - lc->shape_editor->unset_item(SH_KNOTHOLDER); + lc->shape_editor->unset_item(); SPItem *item = selection->singleItem(); - lc->shape_editor->set_item(item, SH_KNOTHOLDER); + lc->shape_editor->set_item(item); } void LpeTool::set(const Inkscape::Preferences::Entry& val) { diff --git a/src/ui/tools/measure-tool.cpp b/src/ui/tools/measure-tool.cpp index feeb68288..6b5cbeccd 100644 --- a/src/ui/tools/measure-tool.cpp +++ b/src/ui/tools/measure-tool.cpp @@ -49,7 +49,7 @@ using Inkscape::ControlManager; using Inkscape::CTLINE_SECONDARY; using Inkscape::Util::unit_table; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { diff --git a/src/ui/tools/mesh-tool.cpp b/src/ui/tools/mesh-tool.cpp index 7d6d3bc23..8a1fb7c72 100644 --- a/src/ui/tools/mesh-tool.cpp +++ b/src/ui/tools/mesh-tool.cpp @@ -53,7 +53,7 @@ using Inkscape::DocumentUndo; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { diff --git a/src/ui/tools/node-tool.cpp b/src/ui/tools/node-tool.cpp index 4be547506..e2bb85d11 100644 --- a/src/ui/tools/node-tool.cpp +++ b/src/ui/tools/node-tool.cpp @@ -23,7 +23,9 @@ #include "live_effects/lpeobject.h" #include "message-context.h" #include "selection.h" -#include "shape-editor.h" // temporary! +#include "ui/shape-editor.h" // temporary! +#include "live_effects/effect.h" +#include "display/curve.h" #include "sp-clippath.h" #include "sp-item-group.h" #include "sp-mask.h" @@ -105,7 +107,7 @@ using Inkscape::ControlManager; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { @@ -171,6 +173,10 @@ NodeTool::~NodeTool() { this->desktop->remove_temporary_canvasitem(this->helperpath_tmpitem); } + if (this->helperpath_tmpitem) { + this->desktop->remove_temporary_canvasitem(this->helperpath_tmpitem); + } + this->_selection_changed_connection.disconnect(); //this->_selection_modified_connection.disconnect(); this->_mouseover_changed_connection.disconnect(); @@ -239,7 +245,7 @@ void NodeTool::setup() { ) ); - this->_selected_nodes->signal_point_changed.connect( + this->_selected_nodes->signal_selection_changed.connect( // Hide both signal parameters and bind the function parameter to 0 // sigc::signal<void, SelectableControlPoint *, bool> // <=> @@ -257,6 +263,7 @@ void NodeTool::setup() { this->flash_tempitem = NULL; this->flashed_item = NULL; this->_last_over = NULL; + this->helperpath_tmpitem = NULL; // read prefs before adding items to selection to prevent momentarily showing the outline sp_event_context_read(this, "show_handles"); @@ -435,7 +442,7 @@ void NodeTool::selection_changed(Inkscape::Selection *sel) { this->_shape_editors.find(r.item) == this->_shape_editors.end()) { ShapeEditor *si = new ShapeEditor(this->desktop); - si->set_item(r.item, SH_KNOTHOLDER); + si->set_item(r.item); this->_shape_editors.insert(const_cast<SPItem*&>(r.item), si); } } @@ -455,7 +462,7 @@ bool NodeTool::root_handler(GdkEvent* event) { Inkscape::Selection *selection = desktop->selection; static Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - + if (this->_multipath->event(this, event)) { return true; } @@ -473,6 +480,7 @@ bool NodeTool::root_handler(GdkEvent* event) { case GDK_MOTION_NOTIFY: { this->update_helperpath(); combine_motion_events(desktop->canvas, event->motion, 0); + this->update_helperpath(); SPItem *over_item = sp_event_context_find_item (desktop, event_point(event->button), FALSE, TRUE); @@ -481,7 +489,6 @@ bool NodeTool::root_handler(GdkEvent* event) { //ink_node_tool_update_tip(nt, event); this->update_tip(event); } - // create pathflash outline if (prefs->getBool("/tools/nodes/pathflash_enabled")) { if (over_item == this->flashed_item) { @@ -513,7 +520,8 @@ bool NodeTool::root_handler(GdkEvent* event) { SPCanvasItem *flash = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), c); sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(flash), - prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff), 1.0, + //prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff), 1.0, + over_item->highlight_color(), 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(flash), 0, SP_WIND_RULE_NONZERO); diff --git a/src/ui/tools/node-tool.h b/src/ui/tools/node-tool.h index 459ecd0a7..ab72f3632 100644 --- a/src/ui/tools/node-tool.h +++ b/src/ui/tools/node-tool.h @@ -15,6 +15,9 @@ #include <glib.h> #include "ui/tools/tool-base.h" +// we need it to call it from Live Effect +#include "selection.h" + namespace Inkscape { namespace Display { class TemporaryItem; diff --git a/src/ui/tools/pen-tool.cpp b/src/ui/tools/pen-tool.cpp index 649c64034..92937a135 100644 --- a/src/ui/tools/pen-tool.cpp +++ b/src/ui/tools/pen-tool.cpp @@ -26,7 +26,7 @@ #include "desktop-handles.h" #include "selection.h" #include "selection-chemistry.h" -#include "draw-anchor.h" +#include "ui/draw-anchor.h" #include "message-stack.h" #include "message-context.h" #include "preferences.h" @@ -40,9 +40,40 @@ #include <glibmm/i18n.h> #include "macros.h" #include "context-fns.h" -#include "tools-switch.h" +#include "ui/tools-switch.h" #include "ui/control-manager.h" -#include "tool-factory.h" +// we include the necessary files for BSpline & Spiro +#include "live_effects/effect.h" +#include "live_effects/lpeobject.h" +#include "live_effects/lpeobject-reference.h" +#include "live_effects/parameter/path.h" +#define INKSCAPE_LPE_SPIRO_C +#include "live_effects/lpe-spiro.h" + + +#include <typeinfo> +#include <2geom/pathvector.h> +#include <2geom/affine.h> +#include <2geom/bezier-curve.h> +#include <2geom/hvlinesegment.h> +#include "helper/geom-nodetype.h" +#include "helper/geom-curves.h" + +// For handling un-continuous paths: +#include "message-stack.h" +#include "inkscape.h" +#include "desktop.h" + +#include "live_effects/spiro.h" + +#define INKSCAPE_LPE_BSPLINE_C +#include "live_effects/lpe-bspline.h" +#include <2geom/nearest-point.h> + +#include "ui/tool-factory.h" + +#include "live_effects/effect.h" + using Inkscape::ControlManager; @@ -53,7 +84,7 @@ namespace Tools { static Geom::Point pen_drag_origin_w(0, 0); static bool pen_within_tolerance = false; static int pen_last_paraxial_dir = 0; // last used direction in horizontal/vertical mode; 0 = horizontal, 1 = vertical - +const double handleCubicGap = 0.01; namespace { ToolBase* createPenContext() { return new PenTool(); @@ -135,8 +166,22 @@ PenTool::~PenTool() { void PenTool::setPolylineMode() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); guint mode = prefs->getInt("/tools/freehand/pen/freehand-mode", 0); - this->polylines_only = (mode == 2 || mode == 3); - this->polylines_paraxial = (mode == 3); + // change the nodes to make space for bspline mode + this->polylines_only = (mode == 3 || mode == 4); + this->polylines_paraxial = (mode == 4); + //we call the function which defines the Spiro modes and the BSpline + //todo: merge to one function only + this->_pen_context_set_mode(mode); +} + +/* +*.Set the mode of draw spiro, and bsplines +*/ +void PenTool::_pen_context_set_mode(guint mode) { + // define the nodes + this->spiro = (mode == 1); + this->bspline = (mode == 2); + this->_bspline_spiro_color(); } /** @@ -144,7 +189,6 @@ void PenTool::setPolylineMode() { */ void PenTool::setup() { FreehandBase::setup(); - ControlManager &mgr = ControlManager::getManager(); // Pen indicators @@ -195,7 +239,9 @@ void PenTool::finish() { sp_event_context_discard_delayed_snap_event(this); if (this->npoints != 0) { - this->_cancel(); + // switching context - finish path + this->ea = NULL; // unset end anchor if set (otherwise crashes) + this->_finish(false); } FreehandBase::finish(); @@ -332,9 +378,20 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) { Geom::Point const event_w(bevent.x, bevent.y); Geom::Point event_dt(desktop->w2d(event_w)); - + //Test whether we hit any anchor. + SPDrawAnchor * const anchor = spdc_test_inside(this, event_w); + + //with this we avoid creating a new point over the existing one + if(bevent.button != 3 && (this->spiro || this->bspline) && this->npoints > 0 && this->p[0] == this->p[3]){ + if( anchor && anchor == this->sa && this->green_curve->is_empty()){ + //remove the following line to avoid having one node on top of another + _finishSegment(event_dt, bevent.state); + _finish( false); + return true; + } + return false; + } bool ret = false; - if (bevent.button == 1 && !this->space_panning // make sure this is not the last click for a waiting LPE (otherwise we want to finish the path) && this->expecting_clicks_for_LPE != 1) { @@ -355,10 +412,8 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) { pen_drag_origin_w = event_w; pen_within_tolerance = true; - // Test whether we hit any anchor. - SPDrawAnchor * const anchor = spdc_test_inside(this, event_w); - switch (this->mode) { + case PenTool::MODE_CLICK: // In click mode we add point on release switch (this->state) { @@ -380,7 +435,7 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) { // This is allowed, if we just canceled curve case PenTool::POINT: if (this->npoints == 0) { - + this->_bspline_spiro_color(); Geom::Point p; if ((bevent.state & GDK_CONTROL_MASK) && (this->polylines_only || this->polylines_paraxial)) { p = event_dt; @@ -399,8 +454,12 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) { // distinction so that the case of a waiting LPE is treated separately // Set start anchor + this->sa = anchor; - if (anchor && !this->hasWaitingLPE()) { + if(anchor){ + this->_bspline_spiro_start_anchor(bevent.state & GDK_SHIFT_MASK); + } + if (anchor && (!this->hasWaitingLPE()|| this->bspline || this->spiro)) { // Adjust point to anchor if needed; if we have a waiting LPE, we need // a fresh path to be created so don't continue an existing one p = anchor->dp; @@ -422,7 +481,7 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) { // Create green anchor p = event_dt; this->_endpointSnap(p, bevent.state); - this->green_anchor = sp_draw_anchor_new(this, this->green_curve, TRUE, p); + this->green_anchor = sp_draw_anchor_new(this, this->green_curve, true, p); } this->_setInitialPoint(p); } else { @@ -439,7 +498,7 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) { if (this->green_anchor && this->green_anchor->active) { // we clicked on the current curve start, so close it even if // we drag a handle away from it - this->green_closed = TRUE; + this->green_closed = true; } ret = true; break; @@ -450,8 +509,8 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) { this->_setSubsequentPoint(p, true); } } - - this->state = this->polylines_only ? PenTool::POINT : PenTool::CONTROL; + // avoid the creation of a control point so a node is created in the release event + this->state = (this->spiro || this->bspline || this->polylines_only) ? PenTool::POINT : PenTool::CONTROL; ret = true; break; case PenTool::CONTROL: @@ -511,9 +570,11 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) { Geom::Point const event_w(mevent.x, mevent.y); + //we take out the function the const "tolerance" because we need it later + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gint const tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + if (pen_within_tolerance) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - gint const tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); if ( Geom::LInfty( event_w - pen_drag_origin_w ) < tolerance ) { return false; // Do not drag if we're within tolerance from origin. } @@ -537,7 +598,7 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) { // Only set point, if we are already appending this->_endpointSnap(p, mevent.state); this->_setSubsequentPoint(p, true); - ret = TRUE; + ret = true; } else if (!this->sp_event_context_knot_mouseover()) { SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop); @@ -573,7 +634,11 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) { } if (anchor && !this->anchor_statusbar) { - this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to close and finish the path.")); + if(!this->spiro && !this->bspline){ + this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to close and finish the path.")); + }else{ + this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to close and finish the path. Shift+Click make a cusp node")); + } this->anchor_statusbar = true; } else if (!anchor && this->anchor_statusbar) { this->message_context->clear(); @@ -583,11 +648,16 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) { ret = true; } else { if (anchor && !this->anchor_statusbar) { - this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to continue the path from this point.")); + if(!this->spiro && !this->bspline){ + this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to continue the path from this point.")); + }else{ + this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to continue the path from this point. Shift+Click make a cusp node")); + } this->anchor_statusbar = true; } else if (!anchor && this->anchor_statusbar) { this->message_context->clear(); this->anchor_statusbar = false; + } if (!this->sp_event_context_knot_mouseover()) { SnapManager &m = desktop->namedview->snap_manager; @@ -602,6 +672,7 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) { // Placing controls is last operation in CLOSE state // snap the handle + this->_endpointSnapHandle(p, mevent.state); if (!this->polylines_only) { @@ -609,6 +680,7 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) { } else { this->_setCtrl(this->p[1], mevent.state); } + gobble_motion_events(GDK_BUTTON1_MASK); ret = true; break; @@ -628,6 +700,16 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) { default: break; } + // calls the function "bspline_spiro_motion" when the mouse starts or stops moving + if(this->bspline){ + this->_bspline_spiro_motion(mevent.state & GDK_SHIFT_MASK); + }else{ + if ( Geom::LInfty( event_w - pen_drag_origin_w ) > (tolerance/2) || mevent.time == 0) { + this->_bspline_spiro_motion(mevent.state & GDK_SHIFT_MASK); + pen_drag_origin_w = event_w; + } + } + return ret; } @@ -649,7 +731,12 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) { Geom::Point p = this->desktop->w2d(event_w); // Test whether we hit any anchor. + SPDrawAnchor *anchor = spdc_test_inside(this, event_w); + // if we try to create a node in the same place as another node, we skip + if((!anchor || anchor == this->sa) && (this->spiro || this->bspline) && this->npoints > 0 && this->p[0] == this->p[3]){ + return true; + } switch (this->mode) { case PenTool::MODE_CLICK: @@ -657,10 +744,17 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) { case PenTool::POINT: if ( this->npoints == 0 ) { // Start new thread only with button release + this->_bspline_spiro_color(); if (anchor) { p = anchor->dp; } this->sa = anchor; + // continue the existing curve + if (anchor) { + if(this->bspline || this->spiro){ + this->_bspline_spiro_start_anchor(revent.state & GDK_SHIFT_MASK);; + } + } this->_setInitialPoint(p); } else { // Set end anchor here @@ -685,6 +779,10 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) { this->_endpointSnap(p, revent.state); } this->_finishSegment(p, revent.state); + // hude the guide of the penultimate node when closing the curve + if(this->spiro){ + sp_canvas_item_hide(this->c1); + } this->_finish(true); this->state = PenTool::POINT; ret = true; @@ -708,6 +806,10 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) { case PenTool::CLOSE: this->_endpointSnap(p, revent.state); this->_finishSegment(p, revent.state); + // hide the penultimate node guide when closing the curve + if(this->spiro){ + sp_canvas_item_hide(this->c1); + } if (this->green_closed) { // finishing at the start anchor, close curve this->_finish(true); @@ -728,7 +830,6 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) { default: break; } - if (this->grab) { // Release grab now sp_canvas_item_ungrab(this->grab, revent.time); @@ -737,7 +838,7 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) { ret = true; - this->green_closed = FALSE; + this->green_closed = false; } // TODO: can we be sure that the path was created correctly? @@ -765,7 +866,7 @@ bool PenTool::_handle2ButtonPress(GdkEventButton const &bevent) { bool ret = false; // only end on LMB double click. Otherwise horizontal scrolling causes ending of the path if (this->npoints != 0 && bevent.button == 1) { - this->_finish(FALSE); + this->_finish(false); ret = true; } return ret; @@ -786,7 +887,6 @@ void PenTool::_redrawAll() { this->green_bpaths = g_slist_prepend(this->green_bpaths, cshape); } - if (this->green_anchor) SP_CTRL(this->green_anchor->ctrl)->moveto(this->green_anchor->dp); @@ -796,7 +896,8 @@ void PenTool::_redrawAll() { sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve); // handles - if (this->p[0] != this->p[1]) { + // hide the handlers in bspline and spiro modes + if (this->p[0] != this->p[1] && !this->spiro && !this->bspline) { SP_CTRL(this->c1)->moveto(this->p[1]); this->cl1->setCoords(this->p[0], this->p[1]); sp_canvas_item_show(this->c1); @@ -809,8 +910,9 @@ void PenTool::_redrawAll() { Geom::Curve const * last_seg = this->green_curve->last_segment(); if (last_seg) { Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const *>( last_seg ); + // hide the handlers in bspline and spiro modes if ( cubic && - (*cubic)[2] != this->p[0] ) + (*cubic)[2] != this->p[0] && !this->spiro && !this->bspline ) { Geom::Point p2 = (*cubic)[2]; SP_CTRL(this->c0)->moveto(p2); @@ -822,6 +924,10 @@ void PenTool::_redrawAll() { sp_canvas_item_hide(this->cl0); } } + + // simply redraw the spiro. because its a redrawing, we don't call the global function, + // but we call the redrawing at the ending. + this->_bspline_spiro_build(); } void PenTool::_lastpointMove(gdouble x, gdouble y) { @@ -839,6 +945,7 @@ void PenTool::_lastpointMove(gdouble x, gdouble y) { } // red + this->p[0] += Geom::Point(x, y); this->p[1] += Geom::Point(x, y); this->_redrawAll(); @@ -849,25 +956,105 @@ void PenTool::_lastpointMoveScreen(gdouble x, gdouble y) { } void PenTool::_lastpointToCurve() { - if (this->npoints != 5) + // avoid that if the "red_curve" contains only two points ( rect ), it doesn't stop here. + if (this->npoints != 5 && !this->spiro && !this->bspline) return; - Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const *>( this->green_curve->last_segment() ); - if ( cubic ) { - this->p[1] = this->p[0] + (Geom::Point)( (*cubic)[3] - (*cubic)[2] ); - } else { - this->p[1] = this->p[0] + (1./3)*(this->p[3] - this->p[0]); + this->p[1] = this->red_curve->last_segment()->initialPoint() + (1./3.)*(this->red_curve->last_segment()->finalPoint() - this->red_curve->last_segment()->initialPoint()); + //modificate the last segment of the green curve so it creates the type of node we need + if (this->spiro||this->bspline) { + if (!this->green_curve->is_empty()) { + Geom::Point A(0,0); + Geom::Point B(0,0); + Geom::Point C(0,0); + Geom::Point D(0,0); + Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>( this->green_curve->last_segment() ); + //We obtain the last segment 4 points in the previous curve + if ( cubic ){ + A = (*cubic)[0]; + B = (*cubic)[1]; + if (this->spiro) { + C = this->p[0] + (this->p[0] - this->p[1]); + } else { + C = this->green_curve->last_segment()->finalPoint() + (1./3.)*(this->green_curve->last_segment()->initialPoint() - this->green_curve->last_segment()->finalPoint()); + } + D = (*cubic)[3]; + } else { + A = this->green_curve->last_segment()->initialPoint(); + B = this->green_curve->last_segment()->initialPoint(); + if (this->spiro) { + C = this->p[0] + (this->p[0] - this->p[1]); + } else { + C = this->green_curve->last_segment()->finalPoint() + (1./3.)*(this->green_curve->last_segment()->initialPoint() - this->green_curve->last_segment()->finalPoint()); + } + D = this->green_curve->last_segment()->finalPoint(); + } + SPCurve *previous = new SPCurve(); + previous->moveto(A); + previous->curveto(B, C, D); + if ( this->green_curve->get_segment_count() == 1) { + this->green_curve = previous; + } else { + //we eliminate the last segment + this->green_curve->backspace(); + //and we add it again with the recreation + this->green_curve->append_continuous(previous, 0.0625); + } + } + //if the last node is an union with another curve + if (this->green_curve->is_empty() && this->sa && !this->sa->curve->is_empty()) { + this->_bspline_spiro_start_anchor(false); + } } this->_redrawAll(); } + void PenTool::_lastpointToLine() { - if (this->npoints != 5) + // avoid that if the "red_curve" contains only two points ( rect) it doesn't stop here. + if (this->npoints != 5 && !this->bspline) return; + // modify the last segment of the green curve so the type of node we want is created. + if(this->spiro || this->bspline){ + if(!this->green_curve->is_empty()){ + Geom::Point A(0,0); + Geom::Point B(0,0); + Geom::Point C(0,0); + Geom::Point D(0,0); + SPCurve * previous = new SPCurve(); + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const *>( this->green_curve->last_segment() ); + if ( cubic ) { + A = this->green_curve->last_segment()->initialPoint(); + B = (*cubic)[1]; + C = this->green_curve->last_segment()->finalPoint(); + D = C; + } else { + //We obtain the last segment 4 points in the previous curve + A = this->green_curve->last_segment()->initialPoint(); + B = A; + C = this->green_curve->last_segment()->finalPoint(); + D = C; + } + previous->moveto(A); + previous->curveto(B, C, D); + if( this->green_curve->get_segment_count() == 1){ + this->green_curve = previous; + }else{ + //we eliminate the last segment + this->green_curve->backspace(); + //and we add it again with the recreation + this->green_curve->append_continuous(previous, 0.0625); + } + } + // if the last node is an union with another curve + if(this->green_curve->is_empty() && this->sa && !this->sa->curve->is_empty()){ + this->_bspline_spiro_start_anchor(true); + } + } + this->p[1] = this->p[0]; - this->_redrawAll(); } @@ -972,7 +1159,7 @@ bool PenTool::_handleKeyPress(GdkEvent *event) { case GDK_KEY_p: if (MOD__SHIFT_ONLY(event)) { sp_pen_context_wait_for_LPE_mouse_clicks(pc, Inkscape::LivePathEffect::PARALLEL, 2); - ret = TRUE; + ret = true; } break; @@ -980,7 +1167,7 @@ bool PenTool::_handleKeyPress(GdkEvent *event) { case GDK_KEY_c: if (MOD__SHIFT_ONLY(event)) { sp_pen_context_wait_for_LPE_mouse_clicks(pc, Inkscape::LivePathEffect::CIRCLE_3PTS, 3); - ret = TRUE; + ret = true; } break; @@ -988,7 +1175,7 @@ bool PenTool::_handleKeyPress(GdkEvent *event) { case GDK_KEY_b: if (MOD__SHIFT_ONLY(event)) { sp_pen_context_wait_for_LPE_mouse_clicks(pc, Inkscape::LivePathEffect::PERP_BISECTOR, 2); - ret = TRUE; + ret = true; } break; @@ -996,7 +1183,7 @@ bool PenTool::_handleKeyPress(GdkEvent *event) { case GDK_KEY_a: if (MOD__SHIFT_ONLY(event)) { sp_pen_context_wait_for_LPE_mouse_clicks(pc, Inkscape::LivePathEffect::ANGLE_BISECTOR, 3); - ret = TRUE; + ret = true; } break; */ @@ -1071,15 +1258,23 @@ bool PenTool::_handleKeyPress(GdkEvent *event) { g_warning("pen_handle_key_press, case GDK_KP_Delete: Green curve is empty"); break; } - // The code below assumes that pc->green_curve has only ONE path ! + // The code below assumes that this->green_curve has only ONE path ! Geom::Curve const * crv = this->green_curve->last_segment(); this->p[0] = crv->initialPoint(); if ( Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const *>(crv)) { this->p[1] = (*cubic)[1]; + } else { this->p[1] = this->p[0]; } + + // asign the value in a third of the distance of the last segment. + if (this->bspline){ + this->p[1] = this->p[0] + (1./3)*(this->p[3] - this->p[0]); + } + Geom::Point const pt( (this->npoints < 4) ? crv->finalPoint() : this->p[3] ); + this->npoints = 2; // delete the last segment of the green curve if (this->green_curve->get_segment_count() == 1) { @@ -1094,6 +1289,18 @@ bool PenTool::_handleKeyPress(GdkEvent *event) { } else { this->green_curve->backspace(); } + + // assign the value of this->p[1] to the oposite of the green line last segment + if (this->spiro){ + Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(this->green_curve->last_segment()); + if ( cubic ) { + this->p[1] = (*cubic)[3] + (*cubic)[3] - (*cubic)[2]; + SP_CTRL(this->c1)->moveto(this->p[0]); + } else { + this->p[1] = this->p[0]; + } + } + sp_canvas_item_hide(this->c0); sp_canvas_item_hide(this->c1); sp_canvas_item_hide(this->cl0); @@ -1101,6 +1308,9 @@ bool PenTool::_handleKeyPress(GdkEvent *event) { this->state = PenTool::POINT; this->_setSubsequentPoint(pt, true); pen_last_paraxial_dir = !pen_last_paraxial_dir; + + //redraw + this->_bspline_spiro_build(); ret = true; } break; @@ -1167,11 +1377,686 @@ void PenTool::_setAngleDistanceStatusMessage(Geom::Point const p, int pc_point_t } this->message_context->setF(Inkscape::IMMEDIATE_MESSAGE, message, angle, dist->str); - g_string_free(dist, FALSE); + g_string_free(dist, false); +} + +// this function changes the colors red, green and blue making them transparent or not, depending on if spiro is being used. +void PenTool::_bspline_spiro_color() +{ + static Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if(this->spiro){ + this->red_color = 0xff00000; + this->green_color = 0x00ff000; + }else if(this->bspline){ + this->highlight_color = SP_ITEM(this->desktop->currentLayer())->highlight_color(); + if((unsigned int)prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff) == this->highlight_color){ + this->green_color = 0xff00007f; + this->red_color = 0xff00007f; + } else { + this->green_color = this->highlight_color; + this->red_color = this->highlight_color; + } + }else{ + this->highlight_color = SP_ITEM(this->desktop->currentLayer())->highlight_color(); + this->red_color = 0xff00007f; + if((unsigned int)prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff) == this->highlight_color){ + this->green_color = 0x00ff007f; + } else { + this->green_color = this->highlight_color; + } + sp_canvas_item_hide(this->blue_bpath); + } + //We erase all the "green_bpaths" to recreate them after with the colour + //transparency recently modified + if (this->green_bpaths) { + // remove old piecewise green canvasitems + while (this->green_bpaths) { + sp_canvas_item_destroy(SP_CANVAS_ITEM(this->green_bpaths->data)); + this->green_bpaths = g_slist_remove(this->green_bpaths, this->green_bpaths->data); + } + // one canvas bpath for all of green_curve + SPCanvasItem *cshape = sp_canvas_bpath_new(sp_desktop_sketch(this->desktop), this->green_curve); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cshape), this->green_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(cshape), 0, SP_WIND_RULE_NONZERO); + this->green_bpaths = g_slist_prepend(this->green_bpaths, cshape); + } + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(this->red_bpath), this->red_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); +} + + +void PenTool::_bspline_spiro(bool shift) +{ + if(!this->spiro && !this->bspline) + return; + + shift?this->_bspline_spiro_off():this->_bspline_spiro_on(); + this->_bspline_spiro_build(); +} + +void PenTool::_bspline_spiro_on() +{ + if(!this->red_curve->is_empty()){ + using Geom::X; + using Geom::Y; + this->npoints = 5; + this->p[0] = this->red_curve->first_segment()->initialPoint(); + this->p[3] = this->red_curve->first_segment()->finalPoint(); + this->p[2] = this->p[3] + (1./3)*(this->p[0] - this->p[3]); + this->p[2] = Geom::Point(this->p[2][X] + handleCubicGap,this->p[2][Y] + handleCubicGap); + } +} + +void PenTool::_bspline_spiro_off() +{ + if(!this->red_curve->is_empty()){ + this->npoints = 5; + this->p[0] = this->red_curve->first_segment()->initialPoint(); + this->p[3] = this->red_curve->first_segment()->finalPoint(); + this->p[2] = this->p[3]; + } +} + +void PenTool::_bspline_spiro_start_anchor(bool shift) +{ + if(this->sa->curve->is_empty()){ + return; + } + + LivePathEffect::LPEBSpline *lpe_bsp = NULL; + + if (SP_IS_LPE_ITEM(this->white_item) && SP_LPE_ITEM(this->white_item)->hasPathEffect()){ + Inkscape::LivePathEffect::Effect* thisEffect = SP_LPE_ITEM(this->white_item)->getPathEffectOfType(Inkscape::LivePathEffect::BSPLINE); + if(thisEffect){ + lpe_bsp = dynamic_cast<LivePathEffect::LPEBSpline*>(thisEffect->getLPEObj()->get_lpe()); + } + } + if(lpe_bsp){ + this->bspline = true; + }else{ + this->bspline = false; + } + LivePathEffect::LPESpiro *lpe_spi = NULL; + + if (SP_IS_LPE_ITEM(this->white_item) && SP_LPE_ITEM(this->white_item)->hasPathEffect()){ + Inkscape::LivePathEffect::Effect* thisEffect = SP_LPE_ITEM(this->white_item)->getPathEffectOfType(Inkscape::LivePathEffect::SPIRO); + if(thisEffect){ + lpe_spi = dynamic_cast<LivePathEffect::LPESpiro*>(thisEffect->getLPEObj()->get_lpe()); + } + } + if(lpe_spi){ + this->spiro = true; + }else{ + this->spiro = false; + } + if(!this->spiro && !this->bspline) + return; + + if(shift) + this->_bspline_spiro_start_anchor_off(); + else + this->_bspline_spiro_start_anchor_on(); +} + +void PenTool::_bspline_spiro_start_anchor_on() +{ + using Geom::X; + using Geom::Y; + SPCurve *tmpCurve = new SPCurve(); + tmpCurve = this->sa->curve->copy(); + if(this->sa->start) + tmpCurve = tmpCurve->create_reverse(); + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + SPCurve *lastSeg = new SPCurve(); + Geom::Point A = tmpCurve->last_segment()->initialPoint(); + Geom::Point D = tmpCurve->last_segment()->finalPoint(); + Geom::Point C = D + (1./3)*(A - D); + C = Geom::Point(C[X] + handleCubicGap,C[Y] + handleCubicGap); + if(cubic){ + lastSeg->moveto(A); + lastSeg->curveto((*cubic)[1],C,D); + }else{ + lastSeg->moveto(A); + lastSeg->curveto(A,C,D); + } + if( tmpCurve->get_segment_count() == 1){ + tmpCurve = lastSeg; + }else{ + //we eliminate the last segment + tmpCurve->backspace(); + //and we add it again with the recreation + tmpCurve->append_continuous(lastSeg, 0.0625); + } + if (this->sa->start) { + tmpCurve = tmpCurve->create_reverse(); + } + this->overwriteCurve = tmpCurve; +} + +void PenTool::_bspline_spiro_start_anchor_off() +{ + SPCurve *tmpCurve = new SPCurve(); + tmpCurve = this->sa->curve->copy(); + if(this->sa->start) + tmpCurve = tmpCurve->create_reverse(); + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + if(cubic){ + SPCurve *lastSeg = new SPCurve(); + lastSeg->moveto((*cubic)[0]); + lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]); + if( tmpCurve->get_segment_count() == 1){ + tmpCurve = lastSeg; + }else{ + //we eliminate the last segment + tmpCurve->backspace(); + //and we add it again with the recreation + tmpCurve->append_continuous(lastSeg, 0.0625); + } + } + if (this->sa->start) { + tmpCurve = tmpCurve->create_reverse(); + } + this->overwriteCurve = tmpCurve; +} + +void PenTool::_bspline_spiro_motion(bool shift){ + if(!this->spiro && !this->bspline) + return; + + using Geom::X; + using Geom::Y; + if(this->red_curve->is_empty()) return; + this->npoints = 5; + SPCurve *tmpCurve = new SPCurve(); + this->p[2] = this->p[3] + (1./3)*(this->p[0] - this->p[3]); + this->p[2] = Geom::Point(this->p[2][X] + handleCubicGap,this->p[2][Y] + handleCubicGap); + if(this->green_curve->is_empty() && !this->sa){ + this->p[1] = this->p[0] + (1./3)*(this->p[3] - this->p[0]); + this->p[1] = Geom::Point(this->p[1][X] + handleCubicGap,this->p[1][Y] + handleCubicGap); + if(shift){ + this->p[2] = this->p[3]; + } + }else if(!this->green_curve->is_empty()){ + tmpCurve = this->green_curve->copy(); + }else{ + tmpCurve = this->overwriteCurve->copy(); + if(this->sa->start) + tmpCurve = tmpCurve->create_reverse(); + } + + if(!tmpCurve->is_empty()){ + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + if(cubic){ + if(this->bspline){ + SPCurve * WPower = new SPCurve(); + Geom::D2< Geom::SBasis > SBasisWPower; + WPower->moveto(tmpCurve->last_segment()->finalPoint()); + WPower->lineto(tmpCurve->last_segment()->initialPoint()); + float WP = Geom::nearest_point((*cubic)[2],*WPower->first_segment()); + WPower->reset(); + WPower->moveto(this->red_curve->last_segment()->initialPoint()); + WPower->lineto(this->red_curve->last_segment()->finalPoint()); + SBasisWPower = WPower->first_segment()->toSBasis(); + WPower->reset(); + this->p[1] = SBasisWPower.valueAt(WP); + if(!Geom::are_near(this->p[1],this->p[0])){ + this->p[1] = Geom::Point(this->p[1][X] + handleCubicGap,this->p[1][Y] + handleCubicGap); + } else { + this->p[1] = this->p[0]; + } + if(shift) + this->p[2] = this->p[3]; + }else{ + this->p[1] = (*cubic)[3] + ((*cubic)[3] - (*cubic)[2] ); + } + }else{ + this->p[1] = this->p[0]; + if(shift) + this->p[2] = this->p[3]; + } + } + + if(this->anchor_statusbar && !this->red_curve->is_empty()){ + if(shift){ + this->_bspline_spiro_end_anchor_off(); + }else{ + this->_bspline_spiro_end_anchor_on(); + } + } + + this->_bspline_spiro_build(); +} + +void PenTool::_bspline_spiro_end_anchor_on() +{ + + using Geom::X; + using Geom::Y; + this->p[2] = this->p[3] + (1./3)*(this->p[0] - this->p[3]); + this->p[2] = Geom::Point(this->p[2][X] + handleCubicGap,this->p[2][Y] + handleCubicGap); + SPCurve *tmpCurve = new SPCurve(); + SPCurve *lastSeg = new SPCurve(); + Geom::Point C(0,0); + bool reverse = false; + if( this->green_anchor && this->green_anchor->active ){ + tmpCurve = this->green_curve->create_reverse(); + if(this->green_curve->get_segment_count()==0){ + return; + } + reverse = true; + } else if(this->sa){ + tmpCurve = this->overwriteCurve->copy(); + if(!this->sa->start){ + tmpCurve = tmpCurve->create_reverse(); + reverse = true; + } + }else{ + return; + } + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + if(this->bspline){ + C = tmpCurve->last_segment()->finalPoint() + (1./3)*(tmpCurve->last_segment()->initialPoint() - tmpCurve->last_segment()->finalPoint()); + C = Geom::Point(C[X] + handleCubicGap,C[Y] + handleCubicGap); + }else{ + C = this->p[3] + this->p[3] - this->p[2]; + } + if(cubic){ + lastSeg->moveto((*cubic)[0]); + lastSeg->curveto((*cubic)[1],C,(*cubic)[3]); + }else{ + lastSeg->moveto(tmpCurve->last_segment()->initialPoint()); + lastSeg->lineto(tmpCurve->last_segment()->finalPoint()); + } + if( tmpCurve->get_segment_count() == 1){ + tmpCurve = lastSeg; + }else{ + //we eliminate the last segment + tmpCurve->backspace(); + //and we add it again with the recreation + tmpCurve->append_continuous(lastSeg, 0.0625); + } + if (reverse) { + tmpCurve = tmpCurve->create_reverse(); + } + if( this->green_anchor && this->green_anchor->active ) + { + this->green_curve->reset(); + this->green_curve = tmpCurve; + }else{ + this->overwriteCurve->reset(); + this->overwriteCurve = tmpCurve; + } +} + +void PenTool::_bspline_spiro_end_anchor_off() +{ + + SPCurve *tmpCurve = new SPCurve(); + SPCurve *lastSeg = new SPCurve(); + bool reverse = false; + this->p[2] = this->p[3]; + if( this->green_anchor && this->green_anchor->active ){ + tmpCurve = this->green_curve->create_reverse(); + if(this->green_curve->get_segment_count()==0){ + return; + } + reverse = true; + } else if(this->sa){ + tmpCurve = this->overwriteCurve->copy(); + if(!this->sa->start){ + tmpCurve = tmpCurve->create_reverse(); + reverse = true; + } + }else{ + return; + } + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + if(cubic){ + lastSeg->moveto((*cubic)[0]); + lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]); + }else{ + lastSeg->moveto(tmpCurve->last_segment()->initialPoint()); + lastSeg->lineto(tmpCurve->last_segment()->finalPoint()); + } + if( tmpCurve->get_segment_count() == 1){ + tmpCurve = lastSeg; + }else{ + //we eliminate the last segment + tmpCurve->backspace(); + //and we add it again with the recreation + tmpCurve->append_continuous(lastSeg, 0.0625); + } + if (reverse) { + tmpCurve = tmpCurve->create_reverse(); + } + if( this->green_anchor && this->green_anchor->active ) + { + this->green_curve->reset(); + this->green_curve = tmpCurve; + }else{ + this->overwriteCurve->reset(); + this->overwriteCurve = tmpCurve; + } +} + +//prepares the curves for its transformation into BSpline curve. +void PenTool::_bspline_spiro_build() +{ + if(!this->spiro && !this->bspline){ + return; + } + + //We create the base curve + SPCurve *curve = new SPCurve(); + //If we continuate the existing curve we add it at the start + if(this->sa && !this->sa->curve->is_empty()){ + curve = this->overwriteCurve->copy(); + if (this->sa->start) { + curve = curve->create_reverse(); + } + } + + if (!this->green_curve->is_empty()){ + curve->append_continuous(this->green_curve, 0.0625); + } + + //and the red one + if (!this->red_curve->is_empty()){ + this->red_curve->reset(); + this->red_curve->moveto(this->p[0]); + if(this->anchor_statusbar && !this->sa && !(this->green_anchor && this->green_anchor->active)){ + this->red_curve->curveto(this->p[1],this->p[3],this->p[3]); + }else{ + this->red_curve->curveto(this->p[1],this->p[2],this->p[3]); + } + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve); + curve->append_continuous(this->red_curve, 0.0625); + } + + if(!curve->is_empty()){ + // close the curve if the final points of the curve are close enough + if(Geom::are_near(curve->first_path()->initialPoint(), curve->last_path()->finalPoint())){ + curve->closepath_current(); + } + //TODO: CALL TO CLONED FUNCTION SPIRO::doEffect IN lpe-spiro.cpp + //For example + //using namespace Inkscape::LivePathEffect; + //LivePathEffectObject *lpeobj = static_cast<LivePathEffectObject*> (curve); + //Effect *spr = static_cast<Effect*> ( new LPEbspline(lpeobj) ); + //spr->doEffect(curve); + if(this->bspline){ + this->_bspline_doEffect(curve); + }else{ + this->_spiro_doEffect(curve); + } + + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->blue_bpath), curve); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(this->blue_bpath), this->blue_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_item_show(this->blue_bpath); + curve->unref(); + this->blue_curve->reset(); + //We hide the holders that doesn't contribute anything + if(this->spiro){ + sp_canvas_item_show(this->c1); + SP_CTRL(this->c1)->moveto(this->p[0]); + }else + sp_canvas_item_hide(this->c1); + sp_canvas_item_hide(this->cl1); + sp_canvas_item_hide(this->c0); + sp_canvas_item_hide(this->cl0); + }else{ + //if the curve is empty + sp_canvas_item_hide(this->blue_bpath); + + } +} + +void PenTool::_bspline_doEffect(SPCurve * curve) +{ + // commenting the function doEffect in src/live_effects/lpe-bspline.cpp + if (curve->get_segment_count() < 1){ + return; + } + // Make copy of old path as it is changed during processing + Geom::PathVector const original_pathv = curve->get_pathvector(); + curve->reset(); + + //Recorremos todos los paths a los que queremos aplicar el efecto, hasta el + //penúltimo + for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); + path_it != original_pathv.end(); ++path_it) { + //Si está vacÃo... + if (path_it->empty()) + continue; + //Itreadores + + Geom::Path::const_iterator curve_it1 = path_it->begin(); + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); + Geom::Path::const_iterator curve_endit = path_it->end_default(); + SPCurve *nCurve = new SPCurve(); + Geom::Point previousNode(0, 0); + Geom::Point node(0, 0); + Geom::Point pointAt1(0, 0); + Geom::Point pointAt2(0, 0); + Geom::Point nextPointAt1(0, 0); + Geom::D2<Geom::SBasis> SBasisIn; + Geom::D2<Geom::SBasis> SBasisOut; + Geom::D2<Geom::SBasis> SBasisHelper; + Geom::CubicBezier const *cubic = NULL; + if (path_it->closed()) { + const Geom::Curve &closingline = + path_it->back_closed(); // the closing line segment is always of type + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { + curve_endit = path_it->end_open(); + } + } + nCurve->moveto(curve_it1->initialPoint()); + while (curve_it1 != curve_endit) { + SPCurve *in = new SPCurve(); + in->moveto(curve_it1->initialPoint()); + in->lineto(curve_it1->finalPoint()); + cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); + if (cubic) { + SBasisIn = in->first_segment()->toSBasis(); + if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) { + pointAt1 = SBasisIn.valueAt(0.3334); + } else { + pointAt1 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[1], *in->first_segment())); + } + if(are_near((*cubic)[2],(*cubic)[3]) && !are_near((*cubic)[1],(*cubic)[0])) { + pointAt2 = SBasisIn.valueAt(0.6667); + } else { + pointAt2 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[2], *in->first_segment())); + } + } else { + pointAt1 = in->first_segment()->initialPoint(); + pointAt2 = in->first_segment()->finalPoint(); + } + in->reset(); + delete in; + if ( curve_it2 != curve_endit ) { + SPCurve *out = new SPCurve(); + out->moveto(curve_it2->initialPoint()); + out->lineto(curve_it2->finalPoint()); + cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it2); + if (cubic) { + SBasisOut = out->first_segment()->toSBasis(); + if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) { + nextPointAt1 = SBasisIn.valueAt(0.3334); + } else { + nextPointAt1 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[1], *out->first_segment())); + } + } else { + nextPointAt1 = out->first_segment()->initialPoint(); + } + out->reset(); + delete out; + } + Geom::Point startNode = path_it->begin()->initialPoint(); + if (path_it->closed() && curve_it2 == curve_endit) { + SPCurve *start = new SPCurve(); + start->moveto(path_it->begin()->initialPoint()); + start->lineto(path_it->begin()->finalPoint()); + Geom::D2<Geom::SBasis> SBasisStart = start->first_segment()->toSBasis(); + SPCurve *lineHelper = new SPCurve(); + cubic = dynamic_cast<Geom::CubicBezier const *>(&*path_it->begin()); + if (cubic) { + lineHelper->moveto(SBasisStart.valueAt( + Geom::nearest_point((*cubic)[1], *start->first_segment()))); + } else { + lineHelper->moveto(start->first_segment()->initialPoint()); + } + start->reset(); + delete start; + + SPCurve *end = new SPCurve(); + end->moveto(curve_it1->initialPoint()); + end->lineto(curve_it1->finalPoint()); + Geom::D2<Geom::SBasis> SBasisEnd = end->first_segment()->toSBasis(); + cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); + if (cubic) { + lineHelper->lineto(SBasisEnd.valueAt( + Geom::nearest_point((*cubic)[2], *end->first_segment()))); + } else { + lineHelper->lineto(end->first_segment()->finalPoint()); + } + end->reset(); + delete end; + SBasisHelper = lineHelper->first_segment()->toSBasis(); + lineHelper->reset(); + delete lineHelper; + startNode = SBasisHelper.valueAt(0.5); + nCurve->curveto(pointAt1, pointAt2, startNode); + nCurve->move_endpoints(startNode, startNode); + } else if ( curve_it2 == curve_endit) { + nCurve->curveto(pointAt1, pointAt2, curve_it1->finalPoint()); + nCurve->move_endpoints(path_it->begin()->initialPoint(), curve_it1->finalPoint()); + } else { + SPCurve *lineHelper = new SPCurve(); + lineHelper->moveto(pointAt2); + lineHelper->lineto(nextPointAt1); + SBasisHelper = lineHelper->first_segment()->toSBasis(); + lineHelper->reset(); + delete lineHelper; + previousNode = node; + node = SBasisHelper.valueAt(0.5); + Geom::CubicBezier const *cubic2 = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); + if((cubic && are_near((*cubic)[0],(*cubic)[1])) || (cubic2 && are_near((*cubic2)[2],(*cubic2)[3]))) { + node = curve_it1->finalPoint(); + } + nCurve->curveto(pointAt1, pointAt2, node); + } + ++curve_it1; + ++curve_it2; + } + if (path_it->closed()) { + nCurve->closepath_current(); + } + curve->append(nCurve, false); + nCurve->reset(); + delete nCurve; + } +} + +//Spiro function cloned from lpe-spiro.cpp +// commenting the function "doEffect" from src/live_effects/lpe-spiro.cpp +void PenTool::_spiro_doEffect(SPCurve * curve) +{ + using Geom::X; + using Geom::Y; + + Geom::PathVector const original_pathv = curve->get_pathvector(); + guint len = curve->get_segment_count() + 2; + + curve->reset(); + Spiro::spiro_cp *path = g_new (Spiro::spiro_cp, len); + int ip = 0; + + for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { + if (path_it->empty()) + continue; + + { + Geom::Point p = path_it->front().pointAt(0); + path[ip].x = p[X]; + path[ip].y = p[Y]; + path[ip].ty = '{' ; + ip++; + } + + Geom::Path::const_iterator curve_it1 = path_it->begin(); + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); + + Geom::Path::const_iterator curve_endit = path_it->end_default(); + if (path_it->closed()) { + const Geom::Curve &closingline = path_it->back_closed(); + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { + curve_endit = path_it->end_open(); + } + } + + while ( curve_it2 != curve_endit ) + { + Geom::Point p = curve_it1->finalPoint(); + path[ip].x = p[X]; + path[ip].y = p[Y]; + + bool this_is_line = is_straight_curve(*curve_it1); + bool next_is_line = is_straight_curve(*curve_it2); + + Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2); + + if ( nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM ) + { + if (this_is_line && !next_is_line) { + path[ip].ty = ']'; + } else if (next_is_line && !this_is_line) { + path[ip].ty = '['; + } else { + path[ip].ty = 'c'; + } + } else { + path[ip].ty = 'v'; + } + + ++curve_it1; + ++curve_it2; + ip++; + } + + Geom::Point p = curve_it1->finalPoint(); + path[ip].x = p[X]; + path[ip].y = p[Y]; + if (path_it->closed()) { + Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, path_it->front()); + switch (nodetype) { + case Geom::NODE_NONE: + path[ip].ty = '}'; + ip++; + break; + case Geom::NODE_CUSP: + path[0].ty = path[ip].ty = 'v'; + break; + case Geom::NODE_SMOOTH: + case Geom::NODE_SYMM: + path[0].ty = path[ip].ty = 'c'; + break; + } + } else { + path[ip].ty = '}'; + ip++; + } + + int sp_len = ip; + Spiro::spiro_run(path, sp_len, *curve); + ip = 0; + } + + g_free (path); } void PenTool::_setSubsequentPoint(Geom::Point const p, bool statusbar, guint status) { g_assert( this->npoints != 0 ); + // todo: Check callers to see whether 2 <= npoints is guaranteed. this->p[2] = p; @@ -1195,7 +2080,7 @@ void PenTool::_setSubsequentPoint(Geom::Point const p, bool statusbar, guint sta is_curve = false; } else { // one of the 'regular' modes - if (this->p[1] != this->p[0]) { + if (this->p[1] != this->p[0] || this->spiro) { this->red_curve->curveto(this->p[1], p, p); is_curve = true; } else { @@ -1210,10 +2095,16 @@ void PenTool::_setSubsequentPoint(Geom::Point const p, bool statusbar, guint sta gchar *message = is_curve ? _("<b>Curve segment</b>: angle %3.2f°, distance %s; with <b>Ctrl</b> to snap angle, <b>Enter</b> to finish the path" ): _("<b>Line segment</b>: angle %3.2f°, distance %s; with <b>Ctrl</b> to snap angle, <b>Enter</b> to finish the path"); + if(this->spiro || this->bspline){ + message = is_curve ? + _("<b>Curve segment</b>: angle %3.2f°, distance %s; with <b>Shift+Click</b> make a cusp node, <b>Enter</b> to finish the path" ): + _("<b>Line segment</b>: angle %3.2f°, distance %s; with <b>Shift+Click</b> make a cusp node, <b>Enter</b> to finish the path"); + } this->_setAngleDistanceStatusMessage(p, 0, message); } } + void PenTool::_setCtrl(Geom::Point const p, guint const state) { sp_canvas_item_show(this->c1); sp_canvas_item_show(this->cl1); @@ -1224,7 +2115,6 @@ void PenTool::_setCtrl(Geom::Point const p, guint const state) { sp_canvas_item_hide(this->cl0); SP_CTRL(this->c1)->moveto(this->p[1]); this->cl1->setCoords(this->p[0], this->p[1]); - this->_setAngleDistanceStatusMessage(p, 0, _("<b>Curve handle</b>: angle %3.2f°, length %s; with <b>Ctrl</b> to snap angle")); } else if ( this->npoints == 5 ) { this->p[4] = p; @@ -1246,6 +2136,8 @@ void PenTool::_setCtrl(Geom::Point const p, guint const state) { SP_CTRL(this->c1)->moveto(this->p[4]); this->cl1->setCoords(this->p[3], this->p[4]); + + gchar *message = is_symm ? _("<b>Curve handle, symmetric</b>: angle %3.2f°, length %s; with <b>Ctrl</b> to snap angle, with <b>Shift</b> to move this handle only") : _("<b>Curve handle</b>: angle %3.2f°, length %s; with <b>Ctrl</b> to snap angle, with <b>Shift</b> to move this handle only"); @@ -1260,11 +2152,14 @@ void PenTool::_finishSegment(Geom::Point const p, guint const state) { pen_last_paraxial_dir = this->nextParaxialDirection(p, this->p[0], state); } - ++this->num_clicks; + ++num_clicks; + if (!this->red_curve->is_empty()) { + this->_bspline_spiro(state & GDK_SHIFT_MASK); this->green_curve->append_continuous(this->red_curve, 0.0625); SPCurve *curve = this->red_curve->copy(); + /// \todo fixme: SPCanvasItem *cshape = sp_canvas_bpath_new(sp_desktop_sketch(this->desktop), curve); curve->unref(); @@ -1286,15 +2181,19 @@ void PenTool::_finish(gboolean const closed) { return; } + this->num_clicks = 0; this->_disableEvents(); this->message_context->clear(); + desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Drawing finished")); + // cancelate line without a created segment this->red_curve->reset(); spdc_concat_colors_and_flush(this, closed); + this->overwriteCurve = NULL; this->sa = NULL; this->ea = NULL; @@ -1343,7 +2242,7 @@ int PenTool::nextParaxialDirection(Geom::Point const &pt, Geom::Point const &ori // // num_clicks is not reliable because spdc_pen_finish_segment is sometimes called too early // (on first mouse release), in which case num_clicks immediately becomes 1. - // if (pc->num_clicks == 0) { + // if (this->num_clicks == 0) { if (this->green_curve->is_empty()) { // first mouse click diff --git a/src/ui/tools/pen-tool.h b/src/ui/tools/pen-tool.h index 4dec7b4fe..98fd0a43e 100644 --- a/src/ui/tools/pen-tool.h +++ b/src/ui/tools/pen-tool.h @@ -49,6 +49,9 @@ public: bool polylines_only; bool polylines_paraxial; + // propiety which saves if Spiro mode is active or not + bool spiro; + bool bspline; int num_clicks; unsigned int expecting_clicks_for_LPE; // if positive, finish the path after this many clicks @@ -85,6 +88,34 @@ private: bool _handleButtonRelease(GdkEventButton const &revent); bool _handle2ButtonPress(GdkEventButton const &bevent); bool _handleKeyPress(GdkEvent *event); + //adds spiro & bspline modes + void _pen_context_set_mode(guint mode); + //this function changes the colors red, green and blue making them transparent or not depending on if the function uses spiro + void _bspline_spiro_color(); + //creates a node in bspline or spiro modes + void _bspline_spiro(bool shift); + //creates a node in bspline or spiro modes + void _bspline_spiro_on(); + //creates a CUSP node + void _bspline_spiro_off(); + //continues the existing curve in bspline or spiro mode + void _bspline_spiro_start_anchor(bool shift); + //continues the existing curve with the union node in bspline or spiro modes + void _bspline_spiro_start_anchor_on(); + //continues an existing curve with the union node in CUSP mode + void _bspline_spiro_start_anchor_off(); + //modifies the "red_curve" when it detects movement + void _bspline_spiro_motion(bool shift); + //closes the curve with the last node in bspline or spiro mode + void _bspline_spiro_end_anchor_on(); + //closes the curve with the last node in CUSP mode + void _bspline_spiro_end_anchor_off(); + //CHECK: join all the curves "in game" and we call doEffect function + void _bspline_spiro_build(); + //function bspline cloned from lpe-bspline.cpp + void _bspline_doEffect(SPCurve * curve); + //function spiro cloned from lpe-spiro.cpp + void _spiro_doEffect(SPCurve * curve); void _setInitialPoint(Geom::Point const p); void _setSubsequentPoint(Geom::Point const p, bool statusbar, guint status = 0); diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index fb4e82c32..3ea2ae843 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -23,7 +23,7 @@ #include "desktop-handles.h" #include "selection.h" #include "selection-chemistry.h" -#include "draw-anchor.h" +#include "ui/draw-anchor.h" #include "message-stack.h" #include "message-context.h" #include "sp-path.h" @@ -43,7 +43,7 @@ #include "display/sp-canvas.h" #include "display/curve.h" #include "livarot/Path.h" -#include "tool-factory.h" +#include "ui/tool-factory.h" #include "ui/tool/event-utils.h" namespace Inkscape { @@ -200,6 +200,7 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) { } if (anchor) { p = anchor->dp; + this->overwriteCurve = anchor->curve; desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Continuing selected path")); } else { m.setup(desktop); @@ -301,7 +302,7 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { if ( this->npoints != 0) { // buttonpress may have happened before we entered draw context! if (this->ps.empty()) { - // Only in freehand mode we have to add the first point also to pc->ps (apparently) + // Only in freehand mode we have to add the first point also to this->ps (apparently) // - We cannot add this point in spdc_set_startpoint, because we only need it for freehand // - We cannot do this in the button press handler because at that point we don't know yet // wheter we're going into freehand mode or not @@ -640,6 +641,9 @@ void PencilTool::_interpolate() { return; } + using Geom::X; + using Geom::Y; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); double const tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0) * 0.4; double const tolerance_sq = 0.02 * square(this->desktop->w2d().descrim() * tol) * exp(0.2 * tol - 2); @@ -661,10 +665,21 @@ void PencilTool::_interpolate() { if (n_segs > 0) { /* Fit and draw and reset state */ - this->green_curve->moveto(b[0]); + this->green_curve->moveto(b[0]); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + guint mode = prefs->getInt("/tools/freehand/pencil/freehand-mode", 0); for (int c = 0; c < n_segs; c++) { - this->green_curve->curveto(b[4 * c + 1], b[4 * c + 2], b[4 * c + 3]); + // if we are in BSpline we modify the trace to create adhoc nodes + if(mode == 2){ + Geom::Point BP = b[4*c+0] + (1./3)*(b[4*c+3] - b[4*c+0]); + BP = Geom::Point(BP[X] + 0.0001,BP[Y] + 0.0001); + Geom::Point CP = b[4*c+3] + (1./3)*(b[4*c+0] - b[4*c+3]); + CP = Geom::Point(CP[X] + 0.0001,CP[Y] + 0.0001); + this->green_curve->curveto(BP,CP,b[4*c+3]); + }else{ + this->green_curve->curveto(b[4 * c + 1], b[4 * c + 2], b[4 * c + 3]); + } } sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->green_curve); @@ -788,6 +803,7 @@ void PencilTool::_fitAndSplit() { g_assert(is_zero(this->req_tangent) || is_unit_vector(this->req_tangent)); Geom::Point const tHatEnd(0, 0); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int const n_segs = Geom::bezier_fit_cubic_full(b, NULL, this->p, this->npoints, this->req_tangent, tHatEnd, tolerance_sq, 1); @@ -795,9 +811,22 @@ void PencilTool::_fitAndSplit() { && unsigned(this->npoints) < G_N_ELEMENTS(this->p) ) { /* Fit and draw and reset state */ + this->red_curve->reset(); this->red_curve->moveto(b[0]); - this->red_curve->curveto(b[1], b[2], b[3]); + using Geom::X; + using Geom::Y; + // if we are in BSpline we modify the trace to create adhoc nodes + guint mode = prefs->getInt("/tools/freehand/pencil/freehand-mode", 0); + if(mode == 2){ + Geom::Point B = b[0] + (1./3)*(b[3] - b[0]); + B = Geom::Point(B[X] + 0.0001,B[Y] + 0.0001); + Geom::Point C = b[3] + (1./3)*(b[0] - b[3]); + C = Geom::Point(C[X] + 0.0001,C[Y] + 0.0001); + this->red_curve->curveto(B,C,b[3]); + }else{ + this->red_curve->curveto(b[1], b[2], b[3]); + } sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve); this->red_curve_is_valid = true; } else { @@ -826,6 +855,13 @@ void PencilTool::_fitAndSplit() { /// \todo fixme: SPCanvasItem *cshape = sp_canvas_bpath_new(sp_desktop_sketch(this->desktop), curve); curve->unref(); + + this->highlight_color = SP_ITEM(this->desktop->currentLayer())->highlight_color(); + if((unsigned int)prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff) == this->highlight_color){ + this->green_color = 0x00ff007f; + } else { + this->green_color = this->highlight_color; + } sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cshape), this->green_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); this->green_bpaths = g_slist_prepend(this->green_bpaths, cshape); diff --git a/src/ui/tools/rect-tool.cpp b/src/ui/tools/rect-tool.cpp index 39f422c1a..de91dcff4 100644 --- a/src/ui/tools/rect-tool.cpp +++ b/src/ui/tools/rect-tool.cpp @@ -40,13 +40,13 @@ #include "xml/node-event-vector.h" #include "preferences.h" #include "context-fns.h" -#include "shape-editor.h" +#include "ui/shape-editor.h" #include "verbs.h" #include "display/sp-canvas-item.h" using Inkscape::DocumentUndo; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { @@ -102,8 +102,8 @@ RectTool::~RectTool() { * destroys old and creates new knotholder. */ void RectTool::selection_changed(Inkscape::Selection* selection) { - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); } void RectTool::setup() { @@ -113,7 +113,7 @@ void RectTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } this->sel_changed_connection.disconnect(); diff --git a/src/ui/tools/select-tool.cpp b/src/ui/tools/select-tool.cpp index 21f37e83d..21459e5d0 100644 --- a/src/ui/tools/select-tool.cpp +++ b/src/ui/tools/select-tool.cpp @@ -41,7 +41,7 @@ #include "desktop-handles.h" #include "sp-root.h" #include "preferences.h" -#include "tools-switch.h" +#include "ui/tools-switch.h" #include "message-stack.h" #include "selection-describer.h" #include "seltrans.h" @@ -49,7 +49,7 @@ #include "display/sp-canvas.h" #include "display/sp-canvas-item.h" #include "display/drawing-item.h" -#include "tool-factory.h" +#include "ui/tool-factory.h" using Inkscape::DocumentUndo; diff --git a/src/ui/tools/select-tool.h b/src/ui/tools/select-tool.h index edc4069a2..5af99a56a 100644 --- a/src/ui/tools/select-tool.h +++ b/src/ui/tools/select-tool.h @@ -13,7 +13,6 @@ */ #include "ui/tools/tool-base.h" -#include <gtk/gtk.h> #define SP_SELECT_CONTEXT(obj) (dynamic_cast<Inkscape::UI::Tools::SelectTool*>((Inkscape::UI::Tools::ToolBase*)obj)) #define SP_IS_SELECT_CONTEXT(obj) (dynamic_cast<const Inkscape::UI::Tools::SelectTool*>((const Inkscape::UI::Tools::ToolBase*)obj) != NULL) diff --git a/src/ui/tools/spiral-tool.cpp b/src/ui/tools/spiral-tool.cpp index 5ae229df8..18c3e4e2d 100644 --- a/src/ui/tools/spiral-tool.cpp +++ b/src/ui/tools/spiral-tool.cpp @@ -39,13 +39,13 @@ #include "xml/node-event-vector.h" #include "preferences.h" #include "context-fns.h" -#include "shape-editor.h" +#include "ui/shape-editor.h" #include "verbs.h" #include "display/sp-canvas-item.h" using Inkscape::DocumentUndo; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { @@ -104,8 +104,8 @@ SpiralTool::~SpiralTool() { * destroys old and creates new knotholder. */ void SpiralTool::selection_changed(Inkscape::Selection *selection) { - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); } void SpiralTool::setup() { @@ -119,7 +119,7 @@ void SpiralTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } Inkscape::Selection *selection = sp_desktop_selection(this->desktop); diff --git a/src/ui/tools/spiral-tool.h b/src/ui/tools/spiral-tool.h index 416aeb7e4..add92342d 100644 --- a/src/ui/tools/spiral-tool.h +++ b/src/ui/tools/spiral-tool.h @@ -15,17 +15,15 @@ * Released under GNU GPL */ -#include <gtk/gtk.h> -#include <stddef.h> -#include <sigc++/sigc++.h> +#include <sigc++/connection.h> #include <2geom/point.h> #include "ui/tools/tool-base.h" -#include "sp-spiral.h" - #define SP_SPIRAL_CONTEXT(obj) (dynamic_cast<Inkscape::UI::Tools::SpiralTool*>((Inkscape::UI::Tools::ToolBase*)obj)) #define SP_IS_SPIRAL_CONTEXT(obj) (dynamic_cast<const Inkscape::UI::Tools::SpiralTool*>((const Inkscape::UI::Tools::ToolBase*)obj) != NULL) +class SPSpiral; + namespace Inkscape { namespace UI { namespace Tools { diff --git a/src/ui/tools/spray-tool.cpp b/src/ui/tools/spray-tool.cpp index e15dbb59f..cdc608558 100644 --- a/src/ui/tools/spray-tool.cpp +++ b/src/ui/tools/spray-tool.cpp @@ -84,7 +84,7 @@ using namespace std; // Please enable again when working on 1.0 #define ENABLE_SPRAY_MODE_SINGLE_PATH -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { diff --git a/src/ui/tools/star-tool.cpp b/src/ui/tools/star-tool.cpp index 68f998920..7604ba04e 100644 --- a/src/ui/tools/star-tool.cpp +++ b/src/ui/tools/star-tool.cpp @@ -41,7 +41,7 @@ #include "xml/repr.h" #include "xml/node-event-vector.h" #include "context-fns.h" -#include "shape-editor.h" +#include "ui/shape-editor.h" #include "verbs.h" #include "display/sp-canvas-item.h" @@ -49,7 +49,7 @@ using Inkscape::DocumentUndo; -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { @@ -112,8 +112,8 @@ StarTool::~StarTool() { void StarTool::selection_changed(Inkscape::Selection* selection) { g_assert (selection != NULL); - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); } void StarTool::setup() { @@ -129,7 +129,7 @@ void StarTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } Inkscape::Selection *selection = sp_desktop_selection(this->desktop); diff --git a/src/ui/tools/text-tool.cpp b/src/ui/tools/text-tool.cpp index ac830fe6b..a72748733 100644 --- a/src/ui/tools/text-tool.cpp +++ b/src/ui/tools/text-tool.cpp @@ -40,7 +40,7 @@ #include "rubberband.h" #include "selection-chemistry.h" #include "selection.h" -#include "shape-editor.h" +#include "ui/shape-editor.h" #include "sp-flowtext.h" #include "sp-namedview.h" #include "sp-text.h" @@ -52,7 +52,7 @@ #include "xml/node-event-vector.h" #include "xml/repr.h" #include <gtk/gtk.h> -#include "tool-factory.h" +#include "ui/tool-factory.h" using Inkscape::ControlManager; using Inkscape::DocumentUndo; @@ -177,7 +177,7 @@ void TextTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item && SP_IS_FLOWTEXT(item) && SP_FLOWTEXT(item)->has_internal_frame()) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } this->sel_changed_connection = sp_desktop_selection(desktop)->connectChangedFirst( @@ -1411,10 +1411,10 @@ void TextTool::_selectionChanged(Inkscape::Selection *selection) ToolBase *ec = SP_EVENT_CONTEXT(this); - ec->shape_editor->unset_item(SH_KNOTHOLDER); + ec->shape_editor->unset_item(); SPItem *item = selection->singleItem(); if (item && SP_IS_FLOWTEXT(item) && SP_FLOWTEXT(item)->has_internal_frame()) { - ec->shape_editor->set_item(item, SH_KNOTHOLDER); + ec->shape_editor->set_item(item); } if (this->text && (item != this->text)) { diff --git a/src/ui/tools/text-tool.h b/src/ui/tools/text-tool.h index ca2b3d19a..289ee180d 100644 --- a/src/ui/tools/text-tool.h +++ b/src/ui/tools/text-tool.h @@ -14,10 +14,7 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -/* #include <gdk/gdkic.h> */ -#include <stddef.h> -#include <sigc++/sigc++.h> -#include <gtk/gtk.h> +#include <sigc++/connection.h> #include "ui/tools/tool-base.h" #include <2geom/point.h> @@ -26,6 +23,8 @@ #define SP_TEXT_CONTEXT(obj) (dynamic_cast<Inkscape::UI::Tools::TextTool*>((Inkscape::UI::Tools::ToolBase*)obj)) #define SP_IS_TEXT_CONTEXT(obj) (dynamic_cast<const Inkscape::UI::Tools::TextTool*>((const Inkscape::UI::Tools::ToolBase*)obj) != NULL) +typedef struct _GtkIMContext GtkIMContext; + struct SPCtrlLine; namespace Inkscape { diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp index 1c1e8a17a..37ca5eeea 100644 --- a/src/ui/tools/tool-base.cpp +++ b/src/ui/tools/tool-base.cpp @@ -44,9 +44,9 @@ #include "desktop-style.h" #include "sp-namedview.h" #include "selection.h" -#include "interface.h" +#include "ui/interface.h" #include "macros.h" -#include "tools-switch.h" +#include "ui/tools-switch.h" #include "preferences.h" #include "message-context.h" #include "gradient-drag.h" @@ -55,10 +55,11 @@ #include "selcue.h" #include "ui/tools/lpe-tool.h" #include "ui/tool/control-point.h" -#include "shape-editor.h" +#include "ui/shape-editor.h" #include "sp-guide.h" #include "color.h" #include "knot.h" +#include "knot-ptr.h" // globals for temporary switching to selector by space static bool selector_toggled = FALSE; @@ -1360,6 +1361,7 @@ gboolean sp_event_context_snap_watchdog_callback(gpointer data) { break; case DelayedSnapEvent::KNOT_HANDLER: { gpointer knot = dse->getItem2(); + check_if_knot_deleted(knot); if (knot && SP_IS_KNOT(knot)) { sp_knot_handler_request_position(dse->getEvent(), SP_KNOT(knot)); } diff --git a/src/ui/tools/tool-base.h b/src/ui/tools/tool-base.h index b27de9030..7a6ab83e7 100644 --- a/src/ui/tools/tool-base.h +++ b/src/ui/tools/tool-base.h @@ -30,7 +30,6 @@ namespace Glib { class GrDrag; class SPDesktop; class SPItem; -class ShapeEditor; namespace Inkscape { class MessageContext; @@ -42,6 +41,9 @@ namespace Inkscape { namespace Inkscape { namespace UI { + +class ShapeEditor; + namespace Tools { class ToolBase; diff --git a/src/ui/tools/tweak-tool.cpp b/src/ui/tools/tweak-tool.cpp index 571a17b70..f56975de2 100644 --- a/src/ui/tools/tweak-tool.cpp +++ b/src/ui/tools/tweak-tool.cpp @@ -91,7 +91,7 @@ using Inkscape::DocumentUndo; #define DYNA_MIN_WIDTH 1.0e-6 -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { diff --git a/src/ui/tools/zoom-tool.cpp b/src/ui/tools/zoom-tool.cpp index 9f99cfe2e..b3fb78c8f 100644 --- a/src/ui/tools/zoom-tool.cpp +++ b/src/ui/tools/zoom-tool.cpp @@ -25,7 +25,7 @@ #include "selection-chemistry.h" #include "ui/tools/zoom-tool.h" -#include "tool-factory.h" +#include "ui/tool-factory.h" namespace Inkscape { namespace UI { diff --git a/src/ui/view/view-widget.cpp b/src/ui/view/view-widget.cpp index 5cb0df215..671a4afe6 100644 --- a/src/ui/view/view-widget.cpp +++ b/src/ui/view/view-widget.cpp @@ -15,32 +15,9 @@ //using namespace Inkscape::UI::View; // SPViewWidget - -static void sp_view_widget_class_init(SPViewWidgetClass *vwc); -static void sp_view_widget_init(SPViewWidget *widget); static void sp_view_widget_dispose(GObject *object); -static GtkEventBoxClass *widget_parent_class; - -GType sp_view_widget_get_type(void) -{ - static GType type = 0; - if (!type) { - GTypeInfo info = { - sizeof(SPViewWidgetClass), - NULL, NULL, - (GClassInitFunc) sp_view_widget_class_init, - NULL, NULL, - sizeof(SPViewWidget), - 0, - (GInstanceInitFunc) sp_view_widget_init, - NULL - }; - type = g_type_register_static (GTK_TYPE_EVENT_BOX, "SPViewWidget", &info, (GTypeFlags)0); - } - - return type; -} +G_DEFINE_TYPE(SPViewWidget, sp_view_widget, GTK_TYPE_EVENT_BOX); /** * Callback to initialize the SPViewWidget vtable. @@ -48,8 +25,6 @@ GType sp_view_widget_get_type(void) static void sp_view_widget_class_init(SPViewWidgetClass *vwc) { GObjectClass *object_class = G_OBJECT_CLASS(vwc); - - widget_parent_class = (GtkEventBoxClass*) g_type_class_peek_parent(vwc); object_class->dispose = sp_view_widget_dispose; } @@ -77,8 +52,8 @@ static void sp_view_widget_dispose(GObject *object) vw->view = NULL; } - if (((GObjectClass *) (widget_parent_class))->dispose) { - (* ((GObjectClass *) (widget_parent_class))->dispose)(object); + if (G_OBJECT_CLASS(sp_view_widget_parent_class)->dispose) { + G_OBJECT_CLASS(sp_view_widget_parent_class)->dispose(object); } Inkscape::GC::request_early_collection(); diff --git a/src/ui/widget/Makefile_insert b/src/ui/widget/Makefile_insert index 608dd5334..e18b790bd 100644 --- a/src/ui/widget/Makefile_insert +++ b/src/ui/widget/Makefile_insert @@ -83,5 +83,14 @@ ink_common_sources += \ ui/widget/unit-menu.cpp \ ui/widget/unit-menu.h \ ui/widget/unit-tracker.h \ - ui/widget/unit-tracker.cpp - + ui/widget/unit-tracker.cpp \ + ui/widget/clipmaskicon.cpp \ + ui/widget/clipmaskicon.h \ + ui/widget/highlight-picker.cpp \ + ui/widget/highlight-picker.h \ + ui/widget/layertypeicon.cpp \ + ui/widget/layertypeicon.h \ + ui/widget/insertordericon.cpp \ + ui/widget/insertordericon.h \ + ui/widget/addtoicon.cpp \ + ui/widget/addtoicon.h diff --git a/src/ui/widget/addtoicon.cpp b/src/ui/widget/addtoicon.cpp new file mode 100644 index 000000000..ce665295b --- /dev/null +++ b/src/ui/widget/addtoicon.cpp @@ -0,0 +1,157 @@ +/* + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H +# include <glibmm/threads.h> +#endif + +#include "ui/widget/addtoicon.h" + +#include <gtkmm/icontheme.h> + +#include "widgets/icon.h" +#include "widgets/toolbox.h" +#include "ui/icon-names.h" +#include "layertypeicon.h" +#include "addtoicon.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +AddToIcon::AddToIcon() : + Glib::ObjectBase(typeid(AddToIcon)), + Gtk::CellRendererPixbuf(), +// _pixAddName(INKSCAPE_ICON("layer-new")), + _property_active(*this, "active", false) +// _property_pixbuf_add(*this, "pixbuf_on", Glib::RefPtr<Gdk::Pixbuf>(0)) +{ + property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE; + phys = sp_icon_get_phys_size((int)Inkscape::ICON_SIZE_BUTTON); +// Glib::RefPtr<Gtk::IconTheme> icon_theme = Gtk::IconTheme::get_default(); +// +// if (!icon_theme->has_icon(_pixAddName)) { +// Inkscape::queueIconPrerender( INKSCAPE_ICON(_pixAddName.data()), Inkscape::ICON_SIZE_DECORATION ); +// } +// if (icon_theme->has_icon(_pixAddName)) { +// _property_pixbuf_add = icon_theme->load_icon(_pixAddName, phys, (Gtk::IconLookupFlags)0); +// } +// +// _property_pixbuf_add = Gtk::Widget:: + + property_stock_id() = GTK_STOCK_ADD; +} + + +#if WITH_GTKMM_3_0 +void AddToIcon::get_preferred_height_vfunc(Gtk::Widget& widget, + int& min_h, + int& nat_h) const +{ + Gtk::CellRendererPixbuf::get_preferred_height_vfunc(widget, min_h, nat_h); + + if (min_h) { + min_h += (min_h) >> 1; + } + + if (nat_h) { + nat_h += (nat_h) >> 1; + } +} + +void AddToIcon::get_preferred_width_vfunc(Gtk::Widget& widget, + int& min_w, + int& nat_w) const +{ + Gtk::CellRendererPixbuf::get_preferred_width_vfunc(widget, min_w, nat_w); + + if (min_w) { + min_w += (min_w) >> 1; + } + + if (nat_w) { + nat_w += (nat_w) >> 1; + } +} +#else +void AddToIcon::get_size_vfunc(Gtk::Widget& widget, + const Gdk::Rectangle* cell_area, + int* x_offset, + int* y_offset, + int* width, + int* height ) const +{ + Gtk::CellRendererPixbuf::get_size_vfunc( widget, cell_area, x_offset, y_offset, width, height ); + + if ( width ) { + *width = phys;//+= (*width) >> 1; + } + if ( height ) { + *height =phys;//+= (*height) >> 1; + } +} +#endif + +#if WITH_GTKMM_3_0 +void AddToIcon::render_vfunc( const Cairo::RefPtr<Cairo::Context>& cr, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + Gtk::CellRendererState flags ) +#else +void AddToIcon::render_vfunc( const Glib::RefPtr<Gdk::Drawable>& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags ) +#endif +{ + property_stock_id() = property_active().get_value() ? GTK_STOCK_ADD : GTK_STOCK_DELETE; + +#if WITH_GTKMM_3_0 + Gtk::CellRendererPixbuf::render_vfunc( cr, widget, background_area, cell_area, flags ); +#else + Gtk::CellRendererPixbuf::render_vfunc( window, widget, background_area, cell_area, expose_area, flags ); +#endif +} + +bool +AddToIcon::activate_vfunc(GdkEvent* event, + Gtk::Widget& /*widget*/, + const Glib::ustring& path, + const Gdk::Rectangle& /*background_area*/, + const Gdk::Rectangle& /*cell_area*/, + Gtk::CellRendererState /*flags*/) +{ + return false; +} + + +} // namespace Widget +} // 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:fileencoding=utf-8:textwidth=99 : + + diff --git a/src/ui/widget/addtoicon.h b/src/ui/widget/addtoicon.h new file mode 100644 index 000000000..9c134d231 --- /dev/null +++ b/src/ui/widget/addtoicon.h @@ -0,0 +1,98 @@ +#ifndef __UI_DIALOG_ADDTOICON_H__ +#define __UI_DIALOG_ADDTOICON_H__ +/* + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gtkmm/cellrendererpixbuf.h> +#include <gtkmm/widget.h> +#include <glibmm/property.h> + +namespace Inkscape { +namespace UI { +namespace Widget { + +class AddToIcon : public Gtk::CellRendererPixbuf { +public: + AddToIcon(); + virtual ~AddToIcon() {}; + + Glib::PropertyProxy<bool> property_active() { return _property_active.get_proxy(); } + Glib::PropertyProxy< Glib::RefPtr<Gdk::Pixbuf> > property_pixbuf_on(); + Glib::PropertyProxy< Glib::RefPtr<Gdk::Pixbuf> > property_pixbuf_off(); + +protected: + +#if WITH_GTKMM_3_0 + virtual void render_vfunc( const Cairo::RefPtr<Cairo::Context>& cr, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + Gtk::CellRendererState flags ); + + virtual void get_preferred_width_vfunc(Gtk::Widget& widget, + int& min_w, + int& nat_w) const; + + virtual void get_preferred_height_vfunc(Gtk::Widget& widget, + int& min_h, + int& nat_h) const; +#else + virtual void render_vfunc( const Glib::RefPtr<Gdk::Drawable>& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags ); + + virtual void get_size_vfunc( Gtk::Widget &widget, + Gdk::Rectangle const *cell_area, + int *x_offset, int *y_offset, int *width, int *height ) const; +#endif + + virtual bool activate_vfunc(GdkEvent *event, + Gtk::Widget &widget, + const Glib::ustring &path, + const Gdk::Rectangle &background_area, + const Gdk::Rectangle &cell_area, + Gtk::CellRendererState flags); + + +private: + int phys; + +// Glib::ustring _pixAddName; + + Glib::Property<bool> _property_active; +// Glib::Property< Glib::RefPtr<Gdk::Pixbuf> > _property_pixbuf_add; + +}; + + + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + + +#endif /* __UI_DIALOG_IMAGETOGGLER_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/anchor-selector.cpp b/src/ui/widget/anchor-selector.cpp index 82e27ee89..df00b786a 100644 --- a/src/ui/widget/anchor-selector.cpp +++ b/src/ui/widget/anchor-selector.cpp @@ -28,7 +28,11 @@ void AnchorSelector::setupButton(const Glib::ustring& icon, Gtk::ToggleButton& b AnchorSelector::AnchorSelector() : Gtk::Alignment(0.5, 0, 0, 0), +#if WITH_GTKMM_3_0 + _container() +#else _container(3, 3, true) +#endif { setupButton(INKSCAPE_ICON("boundingbox_top_left"), _buttons[0]); setupButton(INKSCAPE_ICON("boundingbox_top"), _buttons[1]); @@ -40,10 +44,20 @@ AnchorSelector::AnchorSelector() setupButton(INKSCAPE_ICON("boundingbox_bottom"), _buttons[7]); setupButton(INKSCAPE_ICON("boundingbox_bottom_right"), _buttons[8]); +#if WITH_GTKMM_3_0 + _container.set_row_homogeneous(); + _container.set_column_homogeneous(true); +#endif + for(int i = 0; i < 9; ++i) { _buttons[i].signal_clicked().connect( sigc::bind(sigc::mem_fun(*this, &AnchorSelector::btn_activated), i)); + +#if WITH_GTKMM_3_0 + _container.attach(_buttons[i], i % 3, i / 3, 1, 1); +#else _container.attach(_buttons[i], i % 3, i % 3+1, i / 3, i / 3+1, Gtk::FILL, Gtk::FILL); +#endif } _selection = 4; _buttons[4].set_active(); @@ -94,4 +108,4 @@ void AnchorSelector::setAlignment(int horizontal, int vertical) fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/ui/widget/anchor-selector.h b/src/ui/widget/anchor-selector.h index 2263438e3..0a702d296 100644 --- a/src/ui/widget/anchor-selector.h +++ b/src/ui/widget/anchor-selector.h @@ -10,7 +10,18 @@ #ifndef ANCHOR_SELECTOR_H_ #define ANCHOR_SELECTOR_H_ -#include <gtkmm.h> +#if HAVE_CONFIG_H + #include "config.h" +#endif + +#include <gtkmm/alignment.h> +#include <gtkmm/togglebutton.h> + +#if WITH_GTKMM_3_0 + #include <gtkmm/grid.h> +#else + #include <gtkmm/table.h> +#endif namespace Inkscape { namespace UI { @@ -21,7 +32,12 @@ class AnchorSelector : public Gtk::Alignment private: Gtk::ToggleButton _buttons[9]; int _selection; + +#if WITH_GTKMM_3_0 + Gtk::Grid _container; +#else Gtk::Table _container; +#endif sigc::signal<void> _selectionChanged; @@ -56,4 +72,4 @@ public: fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/ui/widget/button.cpp b/src/ui/widget/button.cpp index bac866920..6c2d419cf 100644 --- a/src/ui/widget/button.cpp +++ b/src/ui/widget/button.cpp @@ -51,13 +51,13 @@ RadioButton::RadioButton(Glib::ustring const &label, Glib::ustring const &toolti } // namespace UI } // namespace Inkscape -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/button.h b/src/ui/widget/button.h index a214dd881..471b7d8a2 100644 --- a/src/ui/widget/button.h +++ b/src/ui/widget/button.h @@ -67,13 +67,13 @@ public: #endif // INKSCAPE_UI_WIDGET_BUTTON_H -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/clipmaskicon.cpp b/src/ui/widget/clipmaskicon.cpp new file mode 100644 index 000000000..6331d70d8 --- /dev/null +++ b/src/ui/widget/clipmaskicon.cpp @@ -0,0 +1,184 @@ +/* + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H +# include <glibmm/threads.h> +#endif + +#include "ui/widget/clipmaskicon.h" + +#include <gtkmm/icontheme.h> + +#include "widgets/icon.h" +#include "widgets/toolbox.h" +#include "ui/icon-names.h" +#include "layertypeicon.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +ClipMaskIcon::ClipMaskIcon() : + Glib::ObjectBase(typeid(ClipMaskIcon)), + Gtk::CellRendererPixbuf(), + _pixClipName(INKSCAPE_ICON("path-intersection")), + _pixInverseName(INKSCAPE_ICON("path-difference")), + _pixMaskName(INKSCAPE_ICON("mask-intersection")), + _property_active(*this, "active", 0), + _property_pixbuf_clip(*this, "pixbuf_on", Glib::RefPtr<Gdk::Pixbuf>(0)), + _property_pixbuf_inverse(*this, "pixbuf_on", Glib::RefPtr<Gdk::Pixbuf>(0)), + _property_pixbuf_mask(*this, "pixbuf_off", Glib::RefPtr<Gdk::Pixbuf>(0)) +{ + + property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE; + phys = sp_icon_get_phys_size((int)Inkscape::ICON_SIZE_DECORATION); + Glib::RefPtr<Gtk::IconTheme> icon_theme = Gtk::IconTheme::get_default(); + + if (!icon_theme->has_icon(_pixClipName)) { + Inkscape::queueIconPrerender( INKSCAPE_ICON(_pixClipName.data()), Inkscape::ICON_SIZE_DECORATION ); + } + if (!icon_theme->has_icon(_pixInverseName)) { + Inkscape::queueIconPrerender( INKSCAPE_ICON(_pixInverseName.data()), Inkscape::ICON_SIZE_DECORATION ); + } + if (!icon_theme->has_icon(_pixMaskName)) { + Inkscape::queueIconPrerender( INKSCAPE_ICON(_pixMaskName.data()), Inkscape::ICON_SIZE_DECORATION ); + } + + if (icon_theme->has_icon(_pixClipName)) { + _property_pixbuf_clip = icon_theme->load_icon(_pixClipName, phys, (Gtk::IconLookupFlags)0); + } + if (icon_theme->has_icon(_pixInverseName)) { + _property_pixbuf_inverse = icon_theme->load_icon(_pixInverseName, phys, (Gtk::IconLookupFlags)0); + } + if (icon_theme->has_icon(_pixMaskName)) { + _property_pixbuf_mask = icon_theme->load_icon(_pixMaskName, phys, (Gtk::IconLookupFlags)0); + } + + property_pixbuf() = Glib::RefPtr<Gdk::Pixbuf>(0); +} + + +#if WITH_GTKMM_3_0 +void ClipMaskIcon::get_preferred_height_vfunc(Gtk::Widget& widget, + int& min_h, + int& nat_h) const +{ + Gtk::CellRendererPixbuf::get_preferred_height_vfunc(widget, min_h, nat_h); + + if (min_h) { + min_h += (min_h) >> 1; + } + + if (nat_h) { + nat_h += (nat_h) >> 1; + } +} + +void ClipMaskIcon::get_preferred_width_vfunc(Gtk::Widget& widget, + int& min_w, + int& nat_w) const +{ + Gtk::CellRendererPixbuf::get_preferred_width_vfunc(widget, min_w, nat_w); + + if (min_w) { + min_w += (min_w) >> 1; + } + + if (nat_w) { + nat_w += (nat_w) >> 1; + } +} +#else +void ClipMaskIcon::get_size_vfunc(Gtk::Widget& widget, + const Gdk::Rectangle* cell_area, + int* x_offset, + int* y_offset, + int* width, + int* height ) const +{ + Gtk::CellRendererPixbuf::get_size_vfunc( widget, cell_area, x_offset, y_offset, width, height ); + + if ( width ) { + *width = phys;//+= (*width) >> 1; + } + if ( height ) { + *height =phys;//+= (*height) >> 1; + } +} +#endif + +#if WITH_GTKMM_3_0 +void ClipMaskIcon::render_vfunc( const Cairo::RefPtr<Cairo::Context>& cr, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + Gtk::CellRendererState flags ) +#else +void ClipMaskIcon::render_vfunc( const Glib::RefPtr<Gdk::Drawable>& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags ) +#endif +{ + switch (_property_active.get_value()) + { + case 1: + property_pixbuf() = _property_pixbuf_clip; + break; + case 2: + property_pixbuf() = _property_pixbuf_mask; + break; + case 3: + property_pixbuf() = _property_pixbuf_inverse; + break; + default: + property_pixbuf() = Glib::RefPtr<Gdk::Pixbuf>(0); + break; + } +#if WITH_GTKMM_3_0 + Gtk::CellRendererPixbuf::render_vfunc( cr, widget, background_area, cell_area, flags ); +#else + Gtk::CellRendererPixbuf::render_vfunc( window, widget, background_area, cell_area, expose_area, flags ); +#endif +} + +bool +ClipMaskIcon::activate_vfunc(GdkEvent* event, + Gtk::Widget& /*widget*/, + const Glib::ustring& path, + const Gdk::Rectangle& /*background_area*/, + const Gdk::Rectangle& /*cell_area*/, + Gtk::CellRendererState /*flags*/) +{ + return false; +} + + +} // namespace Widget +} // 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:fileencoding=utf-8:textwidth=99 : + + diff --git a/src/ui/widget/clipmaskicon.h b/src/ui/widget/clipmaskicon.h new file mode 100644 index 000000000..eca852a83 --- /dev/null +++ b/src/ui/widget/clipmaskicon.h @@ -0,0 +1,102 @@ +#ifndef __UI_DIALOG_CLIPMASKICON_H__ +#define __UI_DIALOG_CLIPMASKICON_H__ +/* + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gtkmm/cellrendererpixbuf.h> +#include <gtkmm/widget.h> +#include <glibmm/property.h> + +namespace Inkscape { +namespace UI { +namespace Widget { + +class ClipMaskIcon : public Gtk::CellRendererPixbuf { +public: + ClipMaskIcon(); + virtual ~ClipMaskIcon() {}; + + Glib::PropertyProxy<int> property_active() { return _property_active.get_proxy(); } + Glib::PropertyProxy< Glib::RefPtr<Gdk::Pixbuf> > property_pixbuf_on(); + Glib::PropertyProxy< Glib::RefPtr<Gdk::Pixbuf> > property_pixbuf_off(); + +protected: + +#if WITH_GTKMM_3_0 + virtual void render_vfunc( const Cairo::RefPtr<Cairo::Context>& cr, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + Gtk::CellRendererState flags ); + + virtual void get_preferred_width_vfunc(Gtk::Widget& widget, + int& min_w, + int& nat_w) const; + + virtual void get_preferred_height_vfunc(Gtk::Widget& widget, + int& min_h, + int& nat_h) const; +#else + virtual void render_vfunc( const Glib::RefPtr<Gdk::Drawable>& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags ); + + virtual void get_size_vfunc( Gtk::Widget &widget, + Gdk::Rectangle const *cell_area, + int *x_offset, int *y_offset, int *width, int *height ) const; +#endif + + virtual bool activate_vfunc(GdkEvent *event, + Gtk::Widget &widget, + const Glib::ustring &path, + const Gdk::Rectangle &background_area, + const Gdk::Rectangle &cell_area, + Gtk::CellRendererState flags); + + +private: + int phys; + + Glib::ustring _pixClipName; + Glib::ustring _pixInverseName; + Glib::ustring _pixMaskName; + + Glib::Property<int> _property_active; + Glib::Property< Glib::RefPtr<Gdk::Pixbuf> > _property_pixbuf_clip; + Glib::Property< Glib::RefPtr<Gdk::Pixbuf> > _property_pixbuf_inverse; + Glib::Property< Glib::RefPtr<Gdk::Pixbuf> > _property_pixbuf_mask; + +}; + + + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + + +#endif /* __UI_DIALOG_IMAGETOGGLER_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/color-picker.cpp b/src/ui/widget/color-picker.cpp index 5585f2db4..6b5a351f6 100644 --- a/src/ui/widget/color-picker.cpp +++ b/src/ui/widget/color-picker.cpp @@ -15,7 +15,7 @@ #include "desktop-handles.h" #include "document.h" #include "document-undo.h" -#include "dialogs/dialog-events.h" +#include "ui/dialog-events.h" #include "widgets/sp-color-notebook.h" #include "verbs.h" diff --git a/src/ui/widget/entity-entry.cpp b/src/ui/widget/entity-entry.cpp index 0f526f77a..c7d5efe29 100644 --- a/src/ui/widget/entity-entry.cpp +++ b/src/ui/widget/entity-entry.cpp @@ -206,9 +206,9 @@ EntityMultiLineEntry::on_changed() Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/entity-entry.h b/src/ui/widget/entity-entry.h index 09289496d..35f6ecfb4 100644 --- a/src/ui/widget/entity-entry.h +++ b/src/ui/widget/entity-entry.h @@ -76,9 +76,9 @@ protected: Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/filter-effect-chooser.cpp b/src/ui/widget/filter-effect-chooser.cpp index 78988a041..4754b9c23 100644 --- a/src/ui/widget/filter-effect-chooser.cpp +++ b/src/ui/widget/filter-effect-chooser.cpp @@ -23,6 +23,8 @@ namespace Widget { SimpleFilterModifier::SimpleFilterModifier(int flags) : _lb_blend(_("Blend mode:")), + _lb_blur(_("_Blur:")), + _lb_blur_unit(_("%")), _blend(BlendModeConverter, SP_ATTR_INVALID, false), _blur(_("Blur (%)"), 0, 0, 100, 1, 0.01, 1) { diff --git a/src/ui/widget/filter-effect-chooser.h b/src/ui/widget/filter-effect-chooser.h index 8d2389b15..6092c61a5 100644 --- a/src/ui/widget/filter-effect-chooser.h +++ b/src/ui/widget/filter-effect-chooser.h @@ -53,11 +53,13 @@ public: double get_blur_value() const; void set_blur_value(const double); void set_blur_sensitive(const bool); + Gtk::Label *get_blur_label() { return &_lb_blur; }; private: int _flags; Gtk::HBox _hb_blend; - Gtk::Label _lb_blend; + Gtk::HBox _hb_blur; + Gtk::Label _lb_blend, _lb_blur, _lb_blur_unit; ComboBoxEnum<Inkscape::Filters::FilterBlendMode> _blend; SpinScale _blur; diff --git a/src/ui/widget/highlight-picker.cpp b/src/ui/widget/highlight-picker.cpp new file mode 100644 index 000000000..8593f0bdf --- /dev/null +++ b/src/ui/widget/highlight-picker.cpp @@ -0,0 +1,207 @@ +/* + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <glibmm.h> +#include <gtkmm/icontheme.h> + +#include "display/cairo-utils.h" + +#include "highlight-picker.h" +#include "widgets/icon.h" +#include "widgets/toolbox.h" +#include "ui/icon-names.h" +#include <glibmm/i18n.h> + +namespace Inkscape { +namespace UI { +namespace Widget { + +HighlightPicker::HighlightPicker() : + Glib::ObjectBase(typeid(HighlightPicker)), + Gtk::CellRendererPixbuf(), + _property_active(*this, "active", 0) +{ + + property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE; +} + +HighlightPicker::~HighlightPicker() +{ +} + + +#if WITH_GTKMM_3_0 +void HighlightPicker::get_preferred_height_vfunc(Gtk::Widget& widget, + int& min_h, + int& nat_h) const +{ + Gtk::CellRendererPixbuf::get_preferred_height_vfunc(widget, min_h, nat_h); + + if (min_h) { + min_h += (min_h) >> 1; + } + + if (nat_h) { + nat_h += (nat_h) >> 1; + } +} + +void HighlightPicker::get_preferred_width_vfunc(Gtk::Widget& widget, + int& min_w, + int& nat_w) const +{ + Gtk::CellRendererPixbuf::get_preferred_width_vfunc(widget, min_w, nat_w); + + if (min_w) { + min_w += (min_w) >> 1; + } + + if (nat_w) { + nat_w += (nat_w) >> 1; + } +} +#else +void HighlightPicker::get_size_vfunc(Gtk::Widget& widget, + const Gdk::Rectangle* cell_area, + int* x_offset, + int* y_offset, + int* width, + int* height ) const +{ + Gtk::CellRendererPixbuf::get_size_vfunc( widget, cell_area, x_offset, y_offset, width, height ); + + if ( width ) { + *width = 10;//+= (*width) >> 1; + } + if ( height ) { + *height = 20; //cell_area ? cell_area->get_height() / 2 : 50; //+= (*height) >> 1; + } +} +#endif + +#if WITH_GTKMM_3_0 +void HighlightPicker::render_vfunc( const Cairo::RefPtr<Cairo::Context>& cr, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + Gtk::CellRendererState flags ) +#else +void HighlightPicker::render_vfunc( const Glib::RefPtr<Gdk::Drawable>& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags ) +#endif +{ + GdkRectangle carea; + + cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 20); + cairo_t *ct = cairo_create(s); + + /* Transparent area */ + carea.x = 0; + carea.y = 0; + carea.width = 10; + carea.height = 20; + + cairo_pattern_t *checkers = ink_cairo_pattern_create_checkerboard(); + + cairo_rectangle(ct, carea.x, carea.y, carea.width, carea.height / 2); + cairo_set_source(ct, checkers); + cairo_fill_preserve(ct); + ink_cairo_set_source_rgba32(ct, _property_active.get_value()); + cairo_fill(ct); + + cairo_pattern_destroy(checkers); + + cairo_rectangle(ct, carea.x, carea.y + carea.height / 2, carea.width, carea.height / 2); + ink_cairo_set_source_rgba32(ct, _property_active.get_value() | 0x000000ff); + cairo_fill(ct); + + cairo_rectangle(ct, carea.x, carea.y, carea.width, carea.height); + ink_cairo_set_source_rgba32(ct, 0x333333ff); + cairo_set_line_width(ct, 2); + cairo_stroke(ct); + + cairo_destroy(ct); + cairo_surface_flush(s); + + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data( cairo_image_surface_get_data(s), + GDK_COLORSPACE_RGB, TRUE, 8, + 10, 20, cairo_image_surface_get_stride(s), + ink_cairo_pixbuf_cleanup, s); + convert_pixbuf_argb32_to_normal(pixbuf); + + property_pixbuf() = Glib::wrap(pixbuf); +#if WITH_GTKMM_3_0 + Gtk::CellRendererPixbuf::render_vfunc( cr, widget, background_area, cell_area, flags ); +#else + Gtk::CellRendererPixbuf::render_vfunc( window, widget, background_area, cell_area, expose_area, flags ); +#endif +} + +bool +HighlightPicker::activate_vfunc(GdkEvent* event, + Gtk::Widget& /*widget*/, + const Glib::ustring& path, + const Gdk::Rectangle& /*background_area*/, + const Gdk::Rectangle& /*cell_area*/, + Gtk::CellRendererState /*flags*/) +{ + return false; +} + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +//should be okay to put this here +/** + * Converts GdkPixbuf's data to premultiplied ARGB. + * This function will convert a GdkPixbuf in place into Cairo's native pixel format. + * Note that this is a hack intended to save memory. When the pixbuf is in Cairo's format, + * using it with GTK will result in corrupted drawings. + */ +void +convert_pixbuf_normal_to_argb32(GdkPixbuf *pb) +{ + convert_pixels_pixbuf_to_argb32( + gdk_pixbuf_get_pixels(pb), + gdk_pixbuf_get_width(pb), + gdk_pixbuf_get_height(pb), + gdk_pixbuf_get_rowstride(pb)); +} + +/** + * Converts GdkPixbuf's data back to its native format. + * Once this is done, the pixbuf can be used with GTK again. + */ +void +convert_pixbuf_argb32_to_normal(GdkPixbuf *pb) +{ + convert_pixels_argb32_to_pixbuf( + gdk_pixbuf_get_pixels(pb), + gdk_pixbuf_get_width(pb), + gdk_pixbuf_get_height(pb), + gdk_pixbuf_get_rowstride(pb)); +} + +/* + 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:fileencoding=utf-8:textwidth=99 : + + diff --git a/src/ui/widget/highlight-picker.h b/src/ui/widget/highlight-picker.h new file mode 100644 index 000000000..c5fe4c02c --- /dev/null +++ b/src/ui/widget/highlight-picker.h @@ -0,0 +1,90 @@ +#ifndef __UI_DIALOG_HIGHLIGHT_PICKER_H__ +#define __UI_DIALOG_HIGHLIGHT_PICKER_H__ +/* + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gtkmm/cellrendererpixbuf.h> +#include <gtkmm/widget.h> +#include <glibmm/property.h> + +namespace Inkscape { +namespace UI { +namespace Widget { + +class HighlightPicker : public Gtk::CellRendererPixbuf { +public: + HighlightPicker(); + virtual ~HighlightPicker(); + + Glib::PropertyProxy<guint32> property_active() { return _property_active.get_proxy(); } + +protected: + +#if WITH_GTKMM_3_0 + virtual void render_vfunc( const Cairo::RefPtr<Cairo::Context>& cr, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + Gtk::CellRendererState flags ); + + virtual void get_preferred_width_vfunc(Gtk::Widget& widget, + int& min_w, + int& nat_w) const; + + virtual void get_preferred_height_vfunc(Gtk::Widget& widget, + int& min_h, + int& nat_h) const; +#else + virtual void render_vfunc( const Glib::RefPtr<Gdk::Drawable>& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags ); + + virtual void get_size_vfunc( Gtk::Widget &widget, + Gdk::Rectangle const *cell_area, + int *x_offset, int *y_offset, int *width, int *height ) const; +#endif + + virtual bool activate_vfunc(GdkEvent *event, + Gtk::Widget &widget, + const Glib::ustring &path, + const Gdk::Rectangle &background_area, + const Gdk::Rectangle &cell_area, + Gtk::CellRendererState flags); + +private: + + Glib::Property<guint32> _property_active; +}; + + + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + + +#endif /* __UI_DIALOG_IMAGETOGGLER_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/insertordericon.cpp b/src/ui/widget/insertordericon.cpp new file mode 100644 index 000000000..a28b0f834 --- /dev/null +++ b/src/ui/widget/insertordericon.cpp @@ -0,0 +1,165 @@ +/* + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "ui/widget/insertordericon.h" + +#include <gtkmm/icontheme.h> + +#include "widgets/icon.h" +#include "widgets/toolbox.h" +#include "ui/icon-names.h" +#include "layertypeicon.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +InsertOrderIcon::InsertOrderIcon() : + Glib::ObjectBase(typeid(InsertOrderIcon)), + Gtk::CellRendererPixbuf(), + _pixTopName(INKSCAPE_ICON("insert-top")), + _pixBottomName(INKSCAPE_ICON("insert-bottom")), + _property_active(*this, "active", 0), + _property_pixbuf_top(*this, "pixbuf_on", Glib::RefPtr<Gdk::Pixbuf>(0)), + _property_pixbuf_bottom(*this, "pixbuf_on", Glib::RefPtr<Gdk::Pixbuf>(0)) +{ + + property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE; + phys = sp_icon_get_phys_size((int)Inkscape::ICON_SIZE_DECORATION); + Glib::RefPtr<Gtk::IconTheme> icon_theme = Gtk::IconTheme::get_default(); + + if (!icon_theme->has_icon(_pixTopName)) { + Inkscape::queueIconPrerender( INKSCAPE_ICON(_pixTopName.data()), Inkscape::ICON_SIZE_DECORATION ); + } + if (!icon_theme->has_icon(_pixBottomName)) { + Inkscape::queueIconPrerender( INKSCAPE_ICON(_pixBottomName.data()), Inkscape::ICON_SIZE_DECORATION ); + } + + if (icon_theme->has_icon(_pixTopName)) { + _property_pixbuf_top = icon_theme->load_icon(_pixTopName, phys, (Gtk::IconLookupFlags)0); + } + if (icon_theme->has_icon(_pixBottomName)) { + _property_pixbuf_bottom = icon_theme->load_icon(_pixBottomName, phys, (Gtk::IconLookupFlags)0); + } + + property_pixbuf() = Glib::RefPtr<Gdk::Pixbuf>(0); +} + + +#if WITH_GTKMM_3_0 +void InsertOrderIcon::get_preferred_height_vfunc(Gtk::Widget& widget, + int& min_h, + int& nat_h) const +{ + Gtk::CellRendererPixbuf::get_preferred_height_vfunc(widget, min_h, nat_h); + + if (min_h) { + min_h += (min_h) >> 1; + } + + if (nat_h) { + nat_h += (nat_h) >> 1; + } +} + +void InsertOrderIcon::get_preferred_width_vfunc(Gtk::Widget& widget, + int& min_w, + int& nat_w) const +{ + Gtk::CellRendererPixbuf::get_preferred_width_vfunc(widget, min_w, nat_w); + + if (min_w) { + min_w += (min_w) >> 1; + } + + if (nat_w) { + nat_w += (nat_w) >> 1; + } +} +#else +void InsertOrderIcon::get_size_vfunc(Gtk::Widget& widget, + const Gdk::Rectangle* cell_area, + int* x_offset, + int* y_offset, + int* width, + int* height ) const +{ + Gtk::CellRendererPixbuf::get_size_vfunc( widget, cell_area, x_offset, y_offset, width, height ); + + if ( width ) { + *width = phys;//+= (*width) >> 1; + } + if ( height ) { + *height =phys;//+= (*height) >> 1; + } +} +#endif + +#if WITH_GTKMM_3_0 +void InsertOrderIcon::render_vfunc( const Cairo::RefPtr<Cairo::Context>& cr, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + Gtk::CellRendererState flags ) +#else +void InsertOrderIcon::render_vfunc( const Glib::RefPtr<Gdk::Drawable>& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags ) +#endif +{ + switch (_property_active.get_value()) + { + case 1: + property_pixbuf() = _property_pixbuf_top; + break; + case 2: + property_pixbuf() = _property_pixbuf_bottom; + break; + default: + property_pixbuf() = Glib::RefPtr<Gdk::Pixbuf>(0); + break; + } +#if WITH_GTKMM_3_0 + Gtk::CellRendererPixbuf::render_vfunc( cr, widget, background_area, cell_area, flags ); +#else + Gtk::CellRendererPixbuf::render_vfunc( window, widget, background_area, cell_area, expose_area, flags ); +#endif +} + +bool +InsertOrderIcon::activate_vfunc(GdkEvent* event, + Gtk::Widget& /*widget*/, + const Glib::ustring& path, + const Gdk::Rectangle& /*background_area*/, + const Gdk::Rectangle& /*cell_area*/, + Gtk::CellRendererState /*flags*/) +{ + return false; +} + + +} // namespace Widget +} // 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:fileencoding=utf-8:textwidth=99 : + + diff --git a/src/ui/widget/insertordericon.h b/src/ui/widget/insertordericon.h new file mode 100644 index 000000000..fb3412d3f --- /dev/null +++ b/src/ui/widget/insertordericon.h @@ -0,0 +1,100 @@ +#ifndef __UI_DIALOG_INSERTORDERICON_H__ +#define __UI_DIALOG_INSERTORDERICON_H__ +/* + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <glibmm.h> +#include <gtkmm/cellrendererpixbuf.h> +#include <gtkmm/widget.h> + +namespace Inkscape { +namespace UI { +namespace Widget { + +class InsertOrderIcon : public Gtk::CellRendererPixbuf { +public: + InsertOrderIcon(); + virtual ~InsertOrderIcon() {}; + + Glib::PropertyProxy<int> property_active() { return _property_active.get_proxy(); } + Glib::PropertyProxy< Glib::RefPtr<Gdk::Pixbuf> > property_pixbuf_on(); + Glib::PropertyProxy< Glib::RefPtr<Gdk::Pixbuf> > property_pixbuf_off(); + +protected: + +#if WITH_GTKMM_3_0 + virtual void render_vfunc( const Cairo::RefPtr<Cairo::Context>& cr, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + Gtk::CellRendererState flags ); + + virtual void get_preferred_width_vfunc(Gtk::Widget& widget, + int& min_w, + int& nat_w) const; + + virtual void get_preferred_height_vfunc(Gtk::Widget& widget, + int& min_h, + int& nat_h) const; +#else + virtual void render_vfunc( const Glib::RefPtr<Gdk::Drawable>& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags ); + + virtual void get_size_vfunc( Gtk::Widget &widget, + Gdk::Rectangle const *cell_area, + int *x_offset, int *y_offset, int *width, int *height ) const; +#endif + + virtual bool activate_vfunc(GdkEvent *event, + Gtk::Widget &widget, + const Glib::ustring &path, + const Gdk::Rectangle &background_area, + const Gdk::Rectangle &cell_area, + Gtk::CellRendererState flags); + + +private: + int phys; + + Glib::ustring _pixTopName; + Glib::ustring _pixBottomName; + + Glib::Property<int> _property_active; + Glib::Property< Glib::RefPtr<Gdk::Pixbuf> > _property_pixbuf_top; + Glib::Property< Glib::RefPtr<Gdk::Pixbuf> > _property_pixbuf_bottom; + +}; + + + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + + +#endif /* __UI_DIALOG_IMAGETOGGLER_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/layertypeicon.cpp b/src/ui/widget/layertypeicon.cpp new file mode 100644 index 000000000..3d6182bf8 --- /dev/null +++ b/src/ui/widget/layertypeicon.cpp @@ -0,0 +1,174 @@ +/* + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H +# include <glibmm/threads.h> +#endif + +#include "ui/widget/layertypeicon.h" + +#include <gtkmm/icontheme.h> + +#include "widgets/icon.h" +#include "widgets/toolbox.h" +#include "ui/icon-names.h" +#include "layertypeicon.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +LayerTypeIcon::LayerTypeIcon() : + Glib::ObjectBase(typeid(LayerTypeIcon)), + Gtk::CellRendererPixbuf(), + _pixLayerName(INKSCAPE_ICON("dialog-layers")), + _pixGroupName(INKSCAPE_ICON("layer-duplicate")), + _pixPathName(INKSCAPE_ICON("layer-rename")), + _property_active(*this, "active", false), + _property_activatable(*this, "activatable", true), + _property_pixbuf_layer(*this, "pixbuf_on", Glib::RefPtr<Gdk::Pixbuf>(0)), + _property_pixbuf_group(*this, "pixbuf_off", Glib::RefPtr<Gdk::Pixbuf>(0)), + _property_pixbuf_path(*this, "pixbuf_off", Glib::RefPtr<Gdk::Pixbuf>(0)) +{ + + property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE; + int phys = sp_icon_get_phys_size((int)Inkscape::ICON_SIZE_DECORATION); + Glib::RefPtr<Gtk::IconTheme> icon_theme = Gtk::IconTheme::get_default(); + + if (!icon_theme->has_icon(_pixLayerName)) { + Inkscape::queueIconPrerender( INKSCAPE_ICON(_pixLayerName.data()), Inkscape::ICON_SIZE_DECORATION ); + } + if (!icon_theme->has_icon(_pixGroupName)) { + Inkscape::queueIconPrerender( INKSCAPE_ICON(_pixGroupName.data()), Inkscape::ICON_SIZE_DECORATION ); + } + if (!icon_theme->has_icon(_pixPathName)) { + Inkscape::queueIconPrerender( INKSCAPE_ICON(_pixPathName.data()), Inkscape::ICON_SIZE_DECORATION ); + } + + if (icon_theme->has_icon(_pixLayerName)) { + _property_pixbuf_layer = icon_theme->load_icon(_pixLayerName, phys, (Gtk::IconLookupFlags)0); + } + if (icon_theme->has_icon(_pixGroupName)) { + _property_pixbuf_group = icon_theme->load_icon(_pixGroupName, phys, (Gtk::IconLookupFlags)0); + } + if (icon_theme->has_icon(_pixPathName)) { + _property_pixbuf_path = icon_theme->load_icon(_pixPathName, phys, (Gtk::IconLookupFlags)0); + } + + property_pixbuf() = _property_pixbuf_path.get_value(); +} + + +#if WITH_GTKMM_3_0 +void LayerTypeIcon::get_preferred_height_vfunc(Gtk::Widget& widget, + int& min_h, + int& nat_h) const +{ + Gtk::CellRendererPixbuf::get_preferred_height_vfunc(widget, min_h, nat_h); + + if (min_h) { + min_h += (min_h) >> 1; + } + + if (nat_h) { + nat_h += (nat_h) >> 1; + } +} + +void LayerTypeIcon::get_preferred_width_vfunc(Gtk::Widget& widget, + int& min_w, + int& nat_w) const +{ + Gtk::CellRendererPixbuf::get_preferred_width_vfunc(widget, min_w, nat_w); + + if (min_w) { + min_w += (min_w) >> 1; + } + + if (nat_w) { + nat_w += (nat_w) >> 1; + } +} +#else +void LayerTypeIcon::get_size_vfunc(Gtk::Widget& widget, + const Gdk::Rectangle* cell_area, + int* x_offset, + int* y_offset, + int* width, + int* height ) const +{ + Gtk::CellRendererPixbuf::get_size_vfunc( widget, cell_area, x_offset, y_offset, width, height ); + + if ( width ) { + *width += (*width) >> 1; + } + if ( height ) { + *height += (*height) >> 1; + } +} +#endif + +#if WITH_GTKMM_3_0 +void LayerTypeIcon::render_vfunc( const Cairo::RefPtr<Cairo::Context>& cr, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + Gtk::CellRendererState flags ) +#else +void LayerTypeIcon::render_vfunc( const Glib::RefPtr<Gdk::Drawable>& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags ) +#endif +{ + property_pixbuf() = _property_active.get_value() == 1 ? _property_pixbuf_group : (_property_active.get_value() == 2 ? _property_pixbuf_layer : _property_pixbuf_path); +#if WITH_GTKMM_3_0 + Gtk::CellRendererPixbuf::render_vfunc( cr, widget, background_area, cell_area, flags ); +#else + Gtk::CellRendererPixbuf::render_vfunc( window, widget, background_area, cell_area, expose_area, flags ); +#endif +} + +bool +LayerTypeIcon::activate_vfunc(GdkEvent* event, + Gtk::Widget& /*widget*/, + const Glib::ustring& path, + const Gdk::Rectangle& /*background_area*/, + const Gdk::Rectangle& /*cell_area*/, + Gtk::CellRendererState /*flags*/) +{ + _signal_pre_toggle.emit(event); + _signal_toggled.emit(path); + + return false; +} + + +} // namespace Widget +} // 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:fileencoding=utf-8:textwidth=99 : + + diff --git a/src/ui/widget/layertypeicon.h b/src/ui/widget/layertypeicon.h new file mode 100644 index 000000000..6c71ce361 --- /dev/null +++ b/src/ui/widget/layertypeicon.h @@ -0,0 +1,108 @@ +#ifndef __UI_DIALOG_LAYERTYPEICON_H__ +#define __UI_DIALOG_LAYERTYPEICON_H__ +/* + * Authors: + * Theodore Janeczko + * + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gtkmm/cellrendererpixbuf.h> +#include <gtkmm/widget.h> +#include <glibmm/property.h> + +namespace Inkscape { +namespace UI { +namespace Widget { + +class LayerTypeIcon : public Gtk::CellRendererPixbuf { +public: + LayerTypeIcon(); + virtual ~LayerTypeIcon() {}; + + sigc::signal<void, const Glib::ustring&> signal_toggled() { return _signal_toggled;} + sigc::signal<void, GdkEvent const *> signal_pre_toggle() { return _signal_pre_toggle; } + + Glib::PropertyProxy<int> property_active() { return _property_active.get_proxy(); } + Glib::PropertyProxy<int> property_activatable() { return _property_activatable.get_proxy(); } + Glib::PropertyProxy< Glib::RefPtr<Gdk::Pixbuf> > property_pixbuf_on(); + Glib::PropertyProxy< Glib::RefPtr<Gdk::Pixbuf> > property_pixbuf_off(); + +protected: + +#if WITH_GTKMM_3_0 + virtual void render_vfunc( const Cairo::RefPtr<Cairo::Context>& cr, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + Gtk::CellRendererState flags ); + + virtual void get_preferred_width_vfunc(Gtk::Widget& widget, + int& min_w, + int& nat_w) const; + + virtual void get_preferred_height_vfunc(Gtk::Widget& widget, + int& min_h, + int& nat_h) const; +#else + virtual void render_vfunc( const Glib::RefPtr<Gdk::Drawable>& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags ); + + virtual void get_size_vfunc( Gtk::Widget &widget, + Gdk::Rectangle const *cell_area, + int *x_offset, int *y_offset, int *width, int *height ) const; +#endif + + virtual bool activate_vfunc(GdkEvent *event, + Gtk::Widget &widget, + const Glib::ustring &path, + const Gdk::Rectangle &background_area, + const Gdk::Rectangle &cell_area, + Gtk::CellRendererState flags); + + +private: + Glib::ustring _pixLayerName; + Glib::ustring _pixGroupName; + Glib::ustring _pixPathName; + + Glib::Property<int> _property_active; + Glib::Property<int> _property_activatable; + Glib::Property< Glib::RefPtr<Gdk::Pixbuf> > _property_pixbuf_layer; + Glib::Property< Glib::RefPtr<Gdk::Pixbuf> > _property_pixbuf_group; + Glib::Property< Glib::RefPtr<Gdk::Pixbuf> > _property_pixbuf_path; + + sigc::signal<void, const Glib::ustring&> _signal_toggled; + sigc::signal<void, GdkEvent const *> _signal_pre_toggle; + +}; + + + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + + +#endif /* __UI_DIALOG_IMAGETOGGLER_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/licensor.cpp b/src/ui/widget/licensor.cpp index 42f352e3c..7429bb07e 100644 --- a/src/ui/widget/licensor.cpp +++ b/src/ui/widget/licensor.cpp @@ -160,9 +160,9 @@ void Licensor::update (SPDocument *doc) Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/licensor.h b/src/ui/widget/licensor.h index 0ac3e5ab8..c75c5fe9e 100644 --- a/src/ui/widget/licensor.h +++ b/src/ui/widget/licensor.h @@ -56,9 +56,9 @@ protected: Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/notebook-page.cpp b/src/ui/widget/notebook-page.cpp index 6653499b8..2f03ed23b 100644 --- a/src/ui/widget/notebook-page.cpp +++ b/src/ui/widget/notebook-page.cpp @@ -44,13 +44,13 @@ NotebookPage::NotebookPage(int n_rows, int n_columns, bool expand, bool fill, gu } // namespace UI } // namespace Inkscape -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/notebook-page.h b/src/ui/widget/notebook-page.h index 38ae9e054..4f7915423 100644 --- a/src/ui/widget/notebook-page.h +++ b/src/ui/widget/notebook-page.h @@ -67,13 +67,13 @@ protected: #endif // INKSCAPE_UI_WIDGET_NOTEBOOK_PAGE_H -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/object-composite-settings.cpp b/src/ui/widget/object-composite-settings.cpp index 537db0fdd..e4cd76345 100644 --- a/src/ui/widget/object-composite-settings.cpp +++ b/src/ui/widget/object-composite-settings.cpp @@ -40,7 +40,7 @@ namespace UI { namespace Widget { /*void ObjectCompositeSettings::_on_desktop_activate( - Inkscape::Application *application, + InkscapeApplication *application, SPDesktop *desktop, ObjectCompositeSettings *w ) { @@ -50,7 +50,7 @@ namespace Widget { } void ObjectCompositeSettings::_on_desktop_deactivate( - Inkscape::Application *application, + InkscapeApplication *application, SPDesktop *desktop, ObjectCompositeSettings *w ) { diff --git a/src/ui/widget/object-composite-settings.h b/src/ui/widget/object-composite-settings.h index 19a6cb2a5..e375bf24a 100644 --- a/src/ui/widget/object-composite-settings.h +++ b/src/ui/widget/object-composite-settings.h @@ -30,9 +30,9 @@ #include "ui/widget/spinbutton.h" class SPDesktop; +struct InkscapeApplication; namespace Inkscape { -struct Application; namespace UI { namespace Widget { @@ -66,8 +66,8 @@ private: gulong _desktop_activated; sigc::connection _subject_changed; - static void _on_desktop_activate(Inkscape::Application *application, SPDesktop *desktop, ObjectCompositeSettings *w); - static void _on_desktop_deactivate(Inkscape::Application *application, SPDesktop *desktop, ObjectCompositeSettings *w); + static void _on_desktop_activate(InkscapeApplication *application, SPDesktop *desktop, ObjectCompositeSettings *w); + static void _on_desktop_deactivate(InkscapeApplication *application, SPDesktop *desktop, ObjectCompositeSettings *w); void _subjectChanged(); void _blendBlurValueChanged(); void _opacityValueChanged(); diff --git a/src/ui/widget/page-sizer.cpp b/src/ui/widget/page-sizer.cpp index eae0d4a95..89b0b8f7e 100644 --- a/src/ui/widget/page-sizer.cpp +++ b/src/ui/widget/page-sizer.cpp @@ -728,9 +728,9 @@ PageSizer::on_units_changed() Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/page-sizer.h b/src/ui/widget/page-sizer.h index dc8e34d82..f4bcae4b6 100644 --- a/src/ui/widget/page-sizer.h +++ b/src/ui/widget/page-sizer.h @@ -277,9 +277,9 @@ protected: Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/panel.cpp b/src/ui/widget/panel.cpp index b37137228..0abd81b16 100644 --- a/src/ui/widget/panel.cpp +++ b/src/ui/widget/panel.cpp @@ -643,13 +643,13 @@ Panel::signalDocumentReplaced() return _signal_document_replaced; } -sigc::signal<void, Inkscape::Application *, SPDesktop *> & +sigc::signal<void, InkscapeApplication *, SPDesktop *> & Panel::signalActivateDesktop() { return _signal_activate_desktop; } -sigc::signal<void, Inkscape::Application *, SPDesktop *> & +sigc::signal<void, InkscapeApplication *, SPDesktop *> & Panel::signalDeactiveDesktop() { return _signal_deactive_desktop; diff --git a/src/ui/widget/panel.h b/src/ui/widget/panel.h index 0c3d822b8..177314797 100644 --- a/src/ui/widget/panel.h +++ b/src/ui/widget/panel.h @@ -45,9 +45,9 @@ namespace Gtk { class MenuItem; } -namespace Inkscape { +struct InkscapeApplication; -struct Application; +namespace Inkscape { class Selection; namespace UI { @@ -116,8 +116,8 @@ public: void setResponseSensitive(int response_id, bool setting); virtual sigc::signal<void, SPDesktop *, SPDocument *> &signalDocumentReplaced(); - virtual sigc::signal<void, Inkscape::Application *, SPDesktop *> &signalActivateDesktop(); - virtual sigc::signal<void, Inkscape::Application *, SPDesktop *> &signalDeactiveDesktop(); + virtual sigc::signal<void, InkscapeApplication *, SPDesktop *> &signalActivateDesktop(); + virtual sigc::signal<void, InkscapeApplication *, SPDesktop *> &signalDeactiveDesktop(); protected: /** @@ -147,8 +147,8 @@ protected: sigc::signal<void, int> _signal_response; sigc::signal<void> _signal_present; sigc::signal<void, SPDesktop *, SPDocument *> _signal_document_replaced; - sigc::signal<void, Inkscape::Application *, SPDesktop *> _signal_activate_desktop; - sigc::signal<void, Inkscape::Application *, SPDesktop *> _signal_deactive_desktop; + sigc::signal<void, InkscapeApplication *, SPDesktop *> _signal_activate_desktop; + sigc::signal<void, InkscapeApplication *, SPDesktop *> _signal_deactive_desktop; private: void _init(); diff --git a/src/ui/widget/preferences-widget.cpp b/src/ui/widget/preferences-widget.cpp index 3ba00c083..7f3e6cd47 100644 --- a/src/ui/widget/preferences-widget.cpp +++ b/src/ui/widget/preferences-widget.cpp @@ -14,10 +14,6 @@ # include <config.h> #endif -#ifdef WIN32 -#include <windows.h> -#endif - #if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H #include <glibmm/threads.h> #endif @@ -49,6 +45,10 @@ #include <glibmm/convert.h> #include <glibmm/i18n.h> +#ifdef WIN32 +#include <windows.h> +#endif + using namespace Inkscape::UI::Widget; namespace Inkscape { diff --git a/src/ui/widget/preferences-widget.h b/src/ui/widget/preferences-widget.h index cb4ce17d1..5d9816e74 100644 --- a/src/ui/widget/preferences-widget.h +++ b/src/ui/widget/preferences-widget.h @@ -306,9 +306,9 @@ public: Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/registered-widget.cpp b/src/ui/widget/registered-widget.cpp index 92cb3f03d..44d8dcbf3 100644 --- a/src/ui/widget/registered-widget.cpp +++ b/src/ui/widget/registered-widget.cpp @@ -99,6 +99,59 @@ RegisteredCheckButton::on_toggled() _wr->setUpdating (false); } +/*######################################### + * Registered TOGGLEBUTTON + */ + +RegisteredToggleButton::~RegisteredToggleButton() +{ + _toggled_connection.disconnect(); +} + +RegisteredToggleButton::RegisteredToggleButton (const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key, Registry& wr, bool right, Inkscape::XML::Node* repr_in, SPDocument *doc_in, char const *active_str, char const *inactive_str) + : RegisteredWidget<Gtk::ToggleButton>() + , _active_str(active_str) + , _inactive_str(inactive_str) +{ + init_parent(key, wr, repr_in, doc_in); + setProgrammatically = false; + set_tooltip_text (tip); + set_alignment (right? 1.0 : 0.0, 0.5); + _toggled_connection = signal_toggled().connect (sigc::mem_fun (*this, &RegisteredToggleButton::on_toggled)); +} + +void +RegisteredToggleButton::setActive (bool b) +{ + setProgrammatically = true; + set_active (b); + //The slave button is greyed out if the master button is untoggled + for (std::list<Gtk::Widget*>::const_iterator i = _slavewidgets.begin(); i != _slavewidgets.end(); ++i) { + (*i)->set_sensitive(b); + } + setProgrammatically = false; +} + +void +RegisteredToggleButton::on_toggled() +{ + if (setProgrammatically) { + setProgrammatically = false; + return; + } + + if (_wr->isUpdating()) + return; + _wr->setUpdating (true); + + write_to_xml(get_active() ? _active_str : _inactive_str); + //The slave button is greyed out if the master button is untoggled + for (std::list<Gtk::Widget*>::const_iterator i = _slavewidgets.begin(); i != _slavewidgets.end(); ++i) { + (*i)->set_sensitive(get_active()); + } + + _wr->setUpdating (false); +} /*######################################### * Registered UNITMENU @@ -735,9 +788,9 @@ RegisteredRandom::on_value_changed() Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/registered-widget.h b/src/ui/widget/registered-widget.h index d64c09c16..1f505a3cd 100644 --- a/src/ui/widget/registered-widget.h +++ b/src/ui/widget/registered-widget.h @@ -163,6 +163,31 @@ protected: void on_toggled(); }; +class RegisteredToggleButton : public RegisteredWidget<Gtk::ToggleButton> { +public: + virtual ~RegisteredToggleButton(); + RegisteredToggleButton (const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key, Registry& wr, bool right=true, Inkscape::XML::Node* repr_in=NULL, SPDocument *doc_in=NULL, char const *active_str = "true", char const *inactive_str = "false"); + + void setActive (bool); + + std::list<Gtk::Widget*> _slavewidgets; + + // a slave button is only sensitive when the master button is active + // i.e. a slave button is greyed-out when the master button is not checked + + void setSlaveWidgets(std::list<Gtk::Widget*> btns) { + _slavewidgets = btns; + } + + bool setProgrammatically; // true if the value was set by setActive, not changed by the user; + // if a callback checks it, it must reset it back to false + +protected: + char const *_active_str, *_inactive_str; + sigc::connection _toggled_connection; + void on_toggled(); +}; + class RegisteredUnitMenu : public RegisteredWidget<Labelled> { public: ~RegisteredUnitMenu(); @@ -394,9 +419,9 @@ protected: Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/registry.cpp b/src/ui/widget/registry.cpp index 725e52791..ea8198422 100644 --- a/src/ui/widget/registry.cpp +++ b/src/ui/widget/registry.cpp @@ -48,9 +48,9 @@ Registry::setUpdating (bool upd) Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/registry.h b/src/ui/widget/registry.h index 2da29735c..a236b96ad 100644 --- a/src/ui/widget/registry.h +++ b/src/ui/widget/registry.h @@ -39,9 +39,9 @@ protected: Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/rotateable.cpp b/src/ui/widget/rotateable.cpp index 72ec69362..2d7597d7c 100644 --- a/src/ui/widget/rotateable.cpp +++ b/src/ui/widget/rotateable.cpp @@ -173,13 +173,13 @@ Rotateable::~Rotateable() { } // namespace UI } // namespace Inkscape -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/rotateable.h b/src/ui/widget/rotateable.h index 52fb5306c..6404c3550 100644 --- a/src/ui/widget/rotateable.h +++ b/src/ui/widget/rotateable.h @@ -62,9 +62,9 @@ private: Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/selected-style.cpp b/src/ui/widget/selected-style.cpp index a575dd592..d22c59aa4 100644 --- a/src/ui/widget/selected-style.cpp +++ b/src/ui/widget/selected-style.cpp @@ -25,6 +25,7 @@ #include "desktop-style.h" #include "sp-namedview.h" #include "sp-linear-gradient.h" +#include "sp-mesh-gradient.h" #include "sp-radial-gradient.h" #include "sp-pattern.h" #include "ui/dialog/dialog-manager.h" @@ -213,6 +214,18 @@ SelectedStyle::SelectedStyle(bool /*layout*/) _gradient_box_r[i].pack_start(*(Glib::wrap(_gradient_preview_r[i]))); _gradient_box_r[i].show_all(); +#ifdef WITH_MESH + _mgradient[i].set_markup (_("<b>M</b>")); + sp_set_font_size_smaller (GTK_WIDGET(_mgradient[i].gobj())); + _mgradient[i].show_all(); + __mgradient[i] = (i == SS_FILL)? (_("Mesh gradient fill")) : (_("Mesh gradient stroke")); + + _gradient_preview_m[i] = GTK_WIDGET(sp_gradient_image_new (NULL)); + _gradient_box_m[i].pack_start(_mgradient[i]); + _gradient_box_m[i].pack_start(*(Glib::wrap(_gradient_preview_m[i]))); + _gradient_box_m[i].show_all(); +#endif + _many[i].set_markup (_("Different")); sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj())); _many[i].show_all(); @@ -1032,6 +1045,14 @@ SelectedStyle::update() place->add(_gradient_box_r[i]); place->set_tooltip_text(__rgradient[i]); _mode[i] = SS_RGRADIENT; +#ifdef WITH_MESH + } else if (SP_IS_MESHGRADIENT(server)) { + SPGradient *vector = SP_GRADIENT(server)->getVector(); + sp_gradient_image_set_gradient(SP_GRADIENT_IMAGE(_gradient_preview_m[i]), vector); + place->add(_gradient_box_m[i]); + place->set_tooltip_text(__mgradient[i]); + _mode[i] = SS_MGRADIENT; +#endif } else if (SP_IS_PATTERN (server)) { place->add(_pattern[i]); place->set_tooltip_text(__pattern[i]); @@ -1556,9 +1577,9 @@ Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop) Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/selected-style.h b/src/ui/widget/selected-style.h index 9557a8d74..0b6a14762 100644 --- a/src/ui/widget/selected-style.h +++ b/src/ui/widget/selected-style.h @@ -60,6 +60,9 @@ enum { SS_PATTERN, SS_LGRADIENT, SS_RGRADIENT, +#ifdef WITH_MESH + SS_MGRADIENT, +#endif SS_MANY, SS_COLOR }; @@ -186,6 +189,14 @@ protected: GtkWidget *_gradient_preview_r[2]; Gtk::HBox _gradient_box_r[2]; +#ifdef WITH_MESH + Gtk::Label _mgradient[2]; + Glib::ustring __mgradient[2]; + + GtkWidget *_gradient_preview_m[2]; + Gtk::HBox _gradient_box_m[2]; +#endif + Gtk::Label _many[2]; Glib::ustring __many[2]; @@ -290,13 +301,13 @@ protected: #endif // INKSCAPE_UI_WIDGET_BUTTON_H -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/spin-scale.cpp b/src/ui/widget/spin-scale.cpp index ade3d1e60..bb08d67df 100644 --- a/src/ui/widget/spin-scale.cpp +++ b/src/ui/widget/spin-scale.cpp @@ -235,13 +235,13 @@ void DualSpinScale::update_linked() } // namespace UI } // namespace Inkscape -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/spin-scale.h b/src/ui/widget/spin-scale.h index 5fec8b1d8..d0447e4a6 100644 --- a/src/ui/widget/spin-scale.h +++ b/src/ui/widget/spin-scale.h @@ -113,13 +113,13 @@ private: #endif // INKSCAPE_UI_WIDGET_SPIN_SCALE_H -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/spin-slider.cpp b/src/ui/widget/spin-slider.cpp index 1cb59a7b3..9b361ae78 100644 --- a/src/ui/widget/spin-slider.cpp +++ b/src/ui/widget/spin-slider.cpp @@ -262,13 +262,13 @@ void DualSpinSlider::update_linked() } // namespace UI } // namespace Inkscape -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/spin-slider.h b/src/ui/widget/spin-slider.h index 5f86fd15a..74982ea58 100644 --- a/src/ui/widget/spin-slider.h +++ b/src/ui/widget/spin-slider.h @@ -111,13 +111,13 @@ private: #endif // INKSCAPE_UI_WIDGET_SPIN_SLIDER_H -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/spinbutton.cpp b/src/ui/widget/spinbutton.cpp index 7709a837b..d7669d4e5 100644 --- a/src/ui/widget/spinbutton.cpp +++ b/src/ui/widget/spinbutton.cpp @@ -100,13 +100,13 @@ void SpinButton::undo() } // namespace UI } // namespace Inkscape -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/spinbutton.h b/src/ui/widget/spinbutton.h index 812b5f515..cbe33e8ea 100644 --- a/src/ui/widget/spinbutton.h +++ b/src/ui/widget/spinbutton.h @@ -111,13 +111,13 @@ private: #endif // INKSCAPE_UI_WIDGET_SPINBUTTON_H -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/style-swatch.cpp b/src/ui/widget/style-swatch.cpp index 98f4e47cd..157fd2ad9 100644 --- a/src/ui/widget/style-swatch.cpp +++ b/src/ui/widget/style-swatch.cpp @@ -386,9 +386,9 @@ void StyleSwatch::setStyle(SPStyle *query) Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/style-swatch.h b/src/ui/widget/style-swatch.h index 557ca82e2..582d2ebb3 100644 --- a/src/ui/widget/style-swatch.h +++ b/src/ui/widget/style-swatch.h @@ -108,13 +108,13 @@ friend class ToolObserver; #endif // INKSCAPE_UI_WIDGET_BUTTON_H -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/tolerance-slider.cpp b/src/ui/widget/tolerance-slider.cpp index 5fc588fdc..aac7451f4 100644 --- a/src/ui/widget/tolerance-slider.cpp +++ b/src/ui/widget/tolerance-slider.cpp @@ -216,9 +216,9 @@ void ToleranceSlider::update (double val) Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/tolerance-slider.h b/src/ui/widget/tolerance-slider.h index 2184cd52b..7ae8e4712 100644 --- a/src/ui/widget/tolerance-slider.h +++ b/src/ui/widget/tolerance-slider.h @@ -88,9 +88,9 @@ protected: Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/unit-menu.cpp b/src/ui/widget/unit-menu.cpp index 7416a2f02..423313a1f 100644 --- a/src/ui/widget/unit-menu.cpp +++ b/src/ui/widget/unit-menu.cpp @@ -140,13 +140,13 @@ bool UnitMenu::isRadial() const } // namespace UI } // namespace Inkscape -/* +/* Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/unit-menu.h b/src/ui/widget/unit-menu.h index 114c536c9..2fd25a6a9 100644 --- a/src/ui/widget/unit-menu.h +++ b/src/ui/widget/unit-menu.h @@ -141,9 +141,9 @@ protected: Local Variables: mode:c++ c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)) + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ -// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/unit-tracker.cpp b/src/ui/widget/unit-tracker.cpp index 67eb1f48d..c6318db25 100644 --- a/src/ui/widget/unit-tracker.cpp +++ b/src/ui/widget/unit-tracker.cpp @@ -13,7 +13,7 @@ */ #include "unit-tracker.h" -#include "ege-select-one-action.h" +#include "widgets/ege-select-one-action.h" #define COLUMN_STRING 0 diff --git a/src/ui/widget/unit-tracker.h b/src/ui/widget/unit-tracker.h index 61bb556ef..06245930e 100644 --- a/src/ui/widget/unit-tracker.h +++ b/src/ui/widget/unit-tracker.h @@ -16,13 +16,16 @@ #define INKSCAPE_UI_WIDGET_UNIT_TRACKER_H #include <map> -#include <gtk/gtk.h> - #include "util/units.h" using Inkscape::Util::Unit; using Inkscape::Util::UnitType; +typedef struct _GObject GObject; +typedef struct _GtkAction GtkAction; +typedef struct _GtkAdjustment GtkAdjustment; +typedef struct _GtkListStore GtkListStore; + namespace Inkscape { namespace UI { namespace Widget { |
