diff options
| author | Liam P. White <inkscapebrony@gmail.com> | 2014-09-27 14:17:45 +0000 |
|---|---|---|
| committer | Liam P. White <inkscapebrony@gmail.com> | 2014-09-27 14:17:45 +0000 |
| commit | fa9bd6393f316dab9303569b28f6b5d179fedd61 (patch) | |
| tree | 33df00632f850a6f117978c36145feeac05f1a4c /src/ui | |
| parent | Update to experimental r13527 (diff) | |
| download | inkscape-fa9bd6393f316dab9303569b28f6b5d179fedd61.tar.gz inkscape-fa9bd6393f316dab9303569b28f6b5d179fedd61.zip | |
Update to experimental r13565
(bzr r13341.5.16)
Diffstat (limited to 'src/ui')
56 files changed, 6187 insertions, 401 deletions
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 125755c48..56908f58e 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -2,6 +2,7 @@ set(ui_SRC clipboard.cpp control-manager.cpp + dialog-events.cpp previewholder.cpp uxmanager.cpp @@ -156,6 +157,7 @@ set(ui_SRC clipboard.h control-manager.h control-types.h + dialog-events.h icon-names.h previewable.h previewfillable.h diff --git a/src/ui/Makefile_insert b/src/ui/Makefile_insert index 4081f86f8..94064d0cf 100644 --- a/src/ui/Makefile_insert +++ b/src/ui/Makefile_insert @@ -6,6 +6,8 @@ ink_common_sources += \ ui/control-manager.cpp \ ui/control-manager.h \ ui/control-types.h \ + ui/dialog-events.cpp \ + ui/dialog-events.h \ ui/icon-names.h \ ui/previewable.h \ ui/previewfillable.h \ diff --git a/src/ui/dialog-events.cpp b/src/ui/dialog-events.cpp new file mode 100644 index 000000000..5bc8088a1 --- /dev/null +++ b/src/ui/dialog-events.cpp @@ -0,0 +1,254 @@ +/** + * @file + * Event handler for dialog windows. + */ +/* Authors: + * bulia byak <bulia@dr.com> + * Johan Engelen <j.b.c.engelen@ewi.utwente.nl> + * + * Copyright (C) 2003-2014 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.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 (desktop, wd); +} + +void +sp_transientize_callback ( 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..b4a5d7c35 --- /dev/null +++ b/src/ui/dialog-events.h @@ -0,0 +1,74 @@ +/** @file + * @brief Event handler for dialog windows + */ +/* Authors: + * bulia byak <bulia@dr.com> + * + * Copyright (C) 2003-2014 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; + +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 ( 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 1bd939707..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,6 +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 44ed3b58f..7212fa372 100644 --- a/src/ui/dialog/aboutbox.cpp +++ b/src/ui/dialog/aboutbox.cpp @@ -254,6 +254,7 @@ void AboutBox::initStrings() { "Nicholas Bishop\n" "Joshua L. Blocher\n" "Hanno Böck\n" +"Tomasz Boczkowski\n" "Henrik Bohre\n" "Boldewyn\n" "Daniel Borgmann\n" @@ -270,6 +271,7 @@ void AboutBox::initStrings() { "Gail Carmichael\n" "Ed Catmur\n" "Chema Celorio\n" +"Jabiertxo Arraiza Cenoz\n" "Johan Ceuppens\n" "Zbigniew Chyla\n" "Alexander Clausen\n" @@ -311,6 +313,7 @@ void AboutBox::initStrings() { "Hannes Hochreiner\n" "Thomas Holder\n" "Joel Holdsworth\n" +"Christoffer Holmstedt\n" "Alan Horkan\n" "Karl Ove Hufthammer\n" "Richard Hughes\n" @@ -319,6 +322,7 @@ void AboutBox::initStrings() { "Thomas Ingham\n" "Jean-Olivier Irisson\n" "Bob Jamison\n" +"Ted Janeczko\n" "jEsuSdA\n" "Fernando Lucchesi Bastos Jurema\n" "Lauris Kaplinski\n" @@ -342,7 +346,9 @@ void AboutBox::initStrings() { "Aurel-Aimé Marmion\n" "Colin Marquardt\n" "Craig Marshall\n" +"Ivan Masár\n" "Dmitry G. Mastrukov\n" +"David Mathog\n" "Matiphas\n" "Michael Meeks\n" "Federico Mena\n" @@ -360,8 +366,10 @@ void AboutBox::initStrings() { "Nick\n" "Andreas Nilsson\n" "Mitsuru Oka\n" +"Vinícius dos Santos Oliveira\n" "Martin Owens\n" "Alvin Penner\n" +"Matthew Petroff\n" "Jon Phillips\n" "Zdenko Podobny\n" "Alexandre Prokoudine\n" @@ -398,6 +406,8 @@ void AboutBox::initStrings() { "Joakim Verona\n" "Lucas Vieites\n" "Daniel Wagenaar\n" +"Liam P. White\n" +"Sebastian Wüst\n" "Michael Wybrow\n" "Gellule Xg\n" "Daniel Yacob\n" @@ -482,6 +492,7 @@ void AboutBox::initStrings() { "Elias Norberg <elno0959 at student.su.se>, 2009.\n" "Equipe de Tradução Inkscape Brasil <www.inkscapebrasil.org>, 2007.\n" "Fatih Demir <kabalak@gtranslator.org>, 2000.\n" +"Firas Hanife <FirasHanife@gmail.com>, 2014.\n" "Foppe Benedictus <foppe.benedictus@gmail.com>, 2007-2009.\n" "Francesc Dorca <f.dorca@filnet.es>, 2003. Traducció sodipodi.\n" "Francisco Javier F. Serrador <serrador@arrakis.es>, 2003.\n" @@ -493,8 +504,9 @@ void AboutBox::initStrings() { "Hizkuntza Politikarako Sailburuordetza <hizkpol@ej-gv.es>, 2005.\n" "Ilia Penev <lichopicho@gmail.com>, 2006.\n" "Ivan Masár <helix84@centrum.sk>, 2006-2010. \n" +"Ivan Řihošek <irihosek@seznam.cz>, 2014.\n" "Iñaki Larrañaga <dooteo@euskalgnu.org>, 2006.\n" -"Jānis Eisaks <jancs@dv.lv>, 2012, 2013.\n" +"Jānis Eisaks <jancs@dv.lv>, 2012-2014.\n" "Jeffrey Steve Borbón Sanabria <jeff_kerokid@yahoo.com>, 2005.\n" "Jesper Öqvist <jesper@llbit.se>, 2010, 2011.\n" "Joaquim Perez i Noguer <noguer@gmail.com>, 2008-2009.\n" @@ -516,7 +528,7 @@ void AboutBox::initStrings() { "Kingsley Turner <kingsley@maddogsbreakfast.com.au>, 2006.\n" "Kitae <bluetux@gmail.com>, 2006.\n" "Kjartan Maraas <kmaraas@gnome.org>, 2000-2002.\n" -"Kris De Gussem <Kris.DeGussem@gmail.com>, 2008-2013.\n" +"Kris De Gussem <Kris.DeGussem@gmail.com>, 2008-2014.\n" "Lauris Kaplinski <lauris@ariman.ee>, 2000.\n" "Leandro Regueiro <leandro.regueiro@gmail.com>, 2006-2008, 2010.\n" "Liu Xiaoqin <liuxqsmile@gmail.com>, 2008.\n" @@ -536,7 +548,7 @@ void AboutBox::initStrings() { "Muhammad Bashir Al-Noimi <mhdbnoimi@gmail.com>, 2008.\n" "Myckel Habets <myckel@sdf.lonestar.org>, 2008.\n" "Nguyen Dinh Trung <nguyendinhtrung141@gmail.com>, 2007, 2008.\n" -"Nicolas Dufour <nicoduf@yahoo.fr>, 2008-2013.\n" +"Nicolas Dufour <nicoduf@yahoo.fr>, 2008-2014.\n" "Pawan Chitrakar <pchitrakar@gmail.com>, 2006.\n" "Przemysław Loesch <p_loesch@poczta.onet.pl>, 2005.\n" "Quico Llach <quico@softcatala.org>, 2000. Traducció sodipodi.\n" 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/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/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index 7e69e439a..5c0437133 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 { @@ -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/dock-behavior.cpp b/src/ui/dialog/dock-behavior.cpp index 9b216e841..f65b298c9 100644 --- a/src/ui/dialog/dock-behavior.cpp +++ b/src/ui/dialog/dock-behavior.cpp @@ -24,7 +24,7 @@ #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-properties.cpp b/src/ui/dialog/document-properties.cpp index 559cd6bbc..c7fcbbfc4 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -1736,9 +1736,13 @@ void DocumentProperties::onDocUnitChange() prefs->setBool("/options/transform/gradient", true); { ShapeEditor::blockSetItem(true); - gdouble viewscale_w = doc->getWidth().value("px")/doc->getRoot()->viewBox.width(); - gdouble viewscale_h = doc->getHeight().value("px")/doc->getRoot()->viewBox.height(); - gdouble viewscale = std::min(viewscale_h, viewscale_w); + gdouble viewscale = 1.0; + Geom::Rect vb = doc->getRoot()->viewBox; + if ( !vb.hasZeroArea() ) { + gdouble viewscale_w = doc->getWidth().value("px") / vb.width(); + gdouble viewscale_h = doc->getHeight().value("px")/ vb.height(); + viewscale = std::min(viewscale_h, viewscale_w); + } gdouble scale = Inkscape::Util::Quantity::convert(1, old_doc_unit, doc_unit); doc->getRoot()->scaleChildItemsRec(Geom::Scale(scale), Geom::Point(-viewscale*doc->getRoot()->viewBox.min()[Geom::X] + (doc->getWidth().value("px") - viewscale*doc->getRoot()->viewBox.width())/2, diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp index 7ce9fe2ec..188135e2b 100644 --- a/src/ui/dialog/export.cpp +++ b/src/ui/dialog/export.cpp @@ -62,7 +62,7 @@ #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" 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 9b75d360a..310630f3e 100644 --- a/src/ui/dialog/filedialogimpl-gtkmm.cpp +++ b/src/ui/dialog/filedialogimpl-gtkmm.cpp @@ -22,7 +22,7 @@ #endif #include "filedialogimpl-gtkmm.h" -#include "dialogs/dialog-events.h" +#include "ui/dialog-events.h" #include "interface.h" #include "io/sys.h" #include "path-prefix.h" diff --git a/src/ui/dialog/filedialogimpl-win32.cpp b/src/ui/dialog/filedialogimpl-win32.cpp index 06153a2d8..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" diff --git a/src/ui/dialog/find.cpp b/src/ui/dialog/find.cpp index 37f2761df..9b814bb9f 100644 --- a/src/ui/dialog/find.cpp +++ b/src/ui/dialog/find.cpp @@ -31,7 +31,7 @@ #include "selection.h" #include "desktop-handles.h" -#include "dialogs/dialog-events.h" +#include "ui/dialog-events.h" #include "verbs.h" #include "interface.h" #include "preferences.h" diff --git a/src/ui/dialog/floating-behavior.cpp b/src/ui/dialog/floating-behavior.cpp index dd07f009a..f286588b2 100644 --- a/src/ui/dialog/floating-behavior.cpp +++ b/src/ui/dialog/floating-behavior.cpp @@ -28,7 +28,7 @@ #include "inkscape.h" #include "desktop.h" -#include "dialogs/dialog-events.h" +#include "ui/dialog-events.h" #include "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/guides.cpp b/src/ui/dialog/guides.cpp index 80740113c..76c26a3cb 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" diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp index eef32d10d..ad8b66b8c 100644 --- a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp +++ b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp @@ -12,9 +12,9 @@ #include <glibmm/threads.h> #endif +#include <gtkmm.h> #include "lpe-fillet-chamfer-properties.h" #include <boost/lexical_cast.hpp> -#include <gtkmm/stock.h> #include <glibmm/main.h> #include <glibmm/i18n.h> #include "inkscape.h" @@ -31,9 +31,8 @@ #include "selection-chemistry.h" #include "ui/icon-names.h" #include "ui/widget/imagetoggler.h" -#include <cmath> -#include <gtkmm/radiobutton.h> #include "util/units.h" +#include <cmath> //#include "event-context.h" @@ -44,38 +43,24 @@ namespace Dialogs { FilletChamferPropertiesDialog::FilletChamferPropertiesDialog() : _desktop(NULL), _knotpoint(NULL), _position_visible(false) { -#if WITH_GTKMM_3_0 - Gtk::Box *mainVBox = get_content_area(); -#else Gtk::Box *mainVBox = get_vbox(); -#endif - mainVBox->set_homogeneous(false); - -#if WITH_GTKMM_3_0 - _layout_table.set_row_spacing(4); - _layout_table.set_column_spacing(4); -#else _layout_table.set_spacings(4); _layout_table.resize(2, 2); -#endif // Layer name widgets - _fillet_chamfer_position_entry.set_activates_default(true); + _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); -#if WITH_GTKMM_3_0 - _layout_table.attach(_fillet_chamfer_position_label, 0, 0, 1, 1); - _layout_table.attach(_fillet_chamfer_position_entry, 1, 0, 1, 1); - _fillet_chamfer_position_entry.set_hexpand(); -#else _layout_table.attach(_fillet_chamfer_position_label, 0, 1, 0, 1, Gtk::FILL, Gtk::FILL); - _layout_table.attach(_fillet_chamfer_position_entry, 1, 2, 0, 1, + _layout_table.attach(_fillet_chamfer_position_numeric, 1, 2, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL); -#endif - _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")); @@ -115,7 +100,7 @@ FilletChamferPropertiesDialog::FilletChamferPropertiesDialog() show_all_children(); - set_focus(_fillet_chamfer_position_entry); + set_focus(_fillet_chamfer_position_numeric); } FilletChamferPropertiesDialog::~FilletChamferPropertiesDialog() @@ -128,12 +113,16 @@ void FilletChamferPropertiesDialog::showDialog( SPDesktop *desktop, Geom::Point knotpoint, const Inkscape::LivePathEffect:: FilletChamferPointArrayParamKnotHolderEntity *pt, - const gchar *unit) + 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); @@ -150,9 +139,9 @@ void FilletChamferPropertiesDialog::showDialog( void FilletChamferPropertiesDialog::_apply() { - std::istringstream i_pos(_fillet_chamfer_position_entry.get_text()); - double d_pos, d_width; - if (i_pos >> d_pos) { + 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) { @@ -203,6 +192,13 @@ void FilletChamferPropertiesDialog::_handleButtonEvent(GdkEventButton *event) 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; @@ -211,16 +207,13 @@ void FilletChamferPropertiesDialog::_setKnotPoint(Geom::Point knotpoint) _fillet_chamfer_position_label.set_label(_("Position (%):")); } else { _flexible = false; - std::string posConcat = - std::string(_("Position (")) + std::string(unit) + std::string(")"); + 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); } - std::ostringstream s; - s.imbue(std::locale::classic()); - s << position; - _fillet_chamfer_position_entry.set_text(s.str()); + _fillet_chamfer_position_numeric.set_value(position); if (knotpoint.y() == 1) { _fillet_chamfer_type_fillet.set_active(true); } else if (knotpoint.y() == 2) { @@ -246,6 +239,16 @@ 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) { diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.h b/src/ui/dialog/lpe-fillet-chamfer-properties.h index 9beca02e1..47ff97b00 100644 --- a/src/ui/dialog/lpe-fillet-chamfer-properties.h +++ b/src/ui/dialog/lpe-fillet-chamfer-properties.h @@ -8,24 +8,10 @@ #ifndef INKSCAPE_DIALOG_FILLET_CHAMFER_PROPERTIES_H #define INKSCAPE_DIALOG_FILLET_CHAMFER_PROPERTIES_H -#if HAVE_CONFIG_H - #include "config.h" -#endif - -#include <gtkmm/dialog.h> #include <2geom/point.h> +#include <gtkmm.h> #include "live_effects/parameter/filletchamferpointarray.h" -#include <gtkmm/entry.h> -#include <gtkmm/label.h> -#include <gtkmm/radiobutton.h> - -#if WITH_GTKMM_3_0 - #include <gtkmm/grid.h> -#else - #include <gtkmm/table.h> -#endif - class SPDesktop; namespace Inkscape { @@ -44,7 +30,9 @@ public: static void showDialog(SPDesktop *desktop, Geom::Point knotpoint, const Inkscape::LivePathEffect:: FilletChamferPointArrayParamKnotHolderEntity *pt, - const gchar *unit); + const gchar *unit, + bool use_distance, + bool aprox_radius); protected: @@ -53,19 +41,14 @@ protected: _knotpoint; Gtk::Label _fillet_chamfer_position_label; - Gtk::Entry _fillet_chamfer_position_entry; + 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; -#if WITH_GTKMM_3_0 - Gtk::Grid _layout_table; -#else Gtk::Table _layout_table; -#endif - bool _position_visible; double _index; @@ -83,10 +66,14 @@ protected: 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); 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/objects.cpp b/src/ui/dialog/objects.cpp new file mode 100644 index 000000000..07a27c96e --- /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 "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..607087f6d 100644 --- a/src/ui/dialog/ocaldialogs.cpp +++ b/src/ui/dialog/ocaldialogs.cpp @@ -27,7 +27,7 @@ #include "filedialogimpl-gtkmm.h" #include "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/swatches.cpp b/src/ui/dialog/swatches.cpp index 16c7bc3e6..187e31233 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/xml-tree.cpp b/src/ui/dialog/xml-tree.cpp index c87e42633..9a8c188b2 100644 --- a/src/ui/dialog/xml-tree.cpp +++ b/src/ui/dialog/xml-tree.cpp @@ -24,7 +24,7 @@ #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" diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index b32bafdbf..d7b35c974 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -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); @@ -844,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) { @@ -856,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 569a8e154..cdbf34e9d 100644 --- a/src/ui/tool/multi-path-manipulator.h +++ b/src/ui/tool/multi-path-manipulator.h @@ -107,7 +107,7 @@ private: void _commit(CommitEvent cps); 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 e38f82673..c52bd4c07 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -59,6 +59,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 @@ -293,7 +298,8 @@ bool Handle::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEven break; default: break; } - // new double click event to set the handlers of a node to the default proportion, 0.3334% + 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; @@ -304,11 +310,11 @@ bool Handle::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEven return ControlPoint::_eventHandler(event_context, event); } -//this function moves the handler and its oposite to the default proportion of 0.3334 +//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,0.3334)); - this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),0.3334)); + setPosition(_pm().BSplineHandleReposition(this,defaultStartPower)); + this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),defaultStartPower)); _pm().update(); } } @@ -617,9 +623,9 @@ void Node::move(Geom::Point const &new_pos) Geom::Point delta = new_pos - position(); // save the previous nodes strength to apply it again once the node is moved - double nodeWeight = 0.0000; - double nextNodeWeight = 0.0000; - double prevNodeWeight = 0.0000; + double nodeWeight = noPower; + double nextNodeWeight = noPower; + double prevNodeWeight = noPower; Node *n = this; Node * nextNode = n->nodeToward(n->front()); Node * prevNode = n->nodeToward(n->back()); @@ -671,9 +677,9 @@ 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 = 0.0000; - double nextNodeWeight = 0.0000; - double prevNodeWeight = 0.0000; + double nodeWeight = noPower; + double nextNodeWeight = noPower; + double prevNodeWeight = noPower; Node *n = this; Node * nextNode = n->nodeToward(n->front()); Node * prevNode = n->nodeToward(n->back()); @@ -903,12 +909,12 @@ void Node::setType(NodeType type, bool update_handles) break; default: break; } - /* in node type changes, about bspline traces, we can mantain them with 0.0000 power in border mode, + /* 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 = 0.0000; - if(_pm().BSplineHandlePosition(this->front()) != 0.0000 ){ - weight = 0.3334; + 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)); @@ -1454,7 +1460,7 @@ Glib::ustring Node::_getTip(unsigned state) const "<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)"),0.0000/*this->bsplineWeight*/); + "<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); diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index 5f20aece7..52ff5d42c 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -56,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 @@ -991,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]); @@ -1218,7 +1250,7 @@ double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2){ if(h2){ h = h2; } - double pos = 0.0000; + double pos = noPower; const double handleCubicGap = 0.01; Node *n = h->parent(); Node * nextNode = NULL; @@ -1227,9 +1259,11 @@ double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2){ SPCurve *lineInsideNodes = new SPCurve(); lineInsideNodes->moveto(n->position()); lineInsideNodes->lineto(nextNode->position()); - pos = Geom::nearest_point(Geom::Point(h->position()[X] - handleCubicGap,h->position()[Y] - handleCubicGap),*lineInsideNodes->first_segment()); + 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 == 0.0000 && !h2){ + if (pos == noPower && !h2){ return BSplineHandlePosition(h, h->other()); } return pos; @@ -1252,14 +1286,14 @@ Geom::Point PathManipulator::BSplineHandleReposition(Handle *h,double pos){ SPCurve *lineInsideNodes = new SPCurve(); Node * nextNode = NULL; nextNode = n->nodeToward(h); - if(nextNode && pos != 0.0000){ + 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 == 0.0000){ + if(pos == noPower){ ret = n->position(); } } diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index 72250a550..46ab53eef 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -80,12 +80,11 @@ 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) , blue_curve(NULL) - , blue2_bpath(NULL) - , blue2_curve(NULL) , green_bpaths(NULL) , green_curve(NULL) , green_anchor(NULL) @@ -142,13 +141,6 @@ void FreehandBase::setup() { // Create blue curve this->blue_curve = new SPCurve(); - // Create blue2 bpath - this->blue2_bpath = sp_canvas_bpath_new(sp_desktop_sketch(this->desktop), NULL); - sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(this->blue2_bpath), this->blue_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); - - // Create blue2 curve - this->blue2_curve = new SPCurve(); - // Create green curve this->green_curve = new SPCurve(); @@ -262,8 +254,9 @@ static void spdc_apply_powerstroke_shape(const std::vector<Geom::Point> & points sp_style_unref(style); } - char * width_str = new char[50]; - sprintf(width_str, "0,%f", stroke_width / 2.); + std::ostringstream s; + s.imbue(std::locale::classic()); + s << "0," << stroke_width / 2.; // write powerstroke parameters: lpe->getRepr()->setAttribute("start_linecap_type", "zerowidth"); @@ -272,9 +265,7 @@ static void spdc_apply_powerstroke_shape(const std::vector<Geom::Point> & points lpe->getRepr()->setAttribute("sort_points", "true"); lpe->getRepr()->setAttribute("interpolator_type", "CubicBezierJohan"); lpe->getRepr()->setAttribute("interpolator_beta", "0.2"); - lpe->getRepr()->setAttribute("offset_points", width_str); - - delete [] width_str; + lpe->getRepr()->setAttribute("offset_points", s.str().c_str()); } static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, SPCurve *curve) @@ -550,10 +541,6 @@ void spdc_concat_colors_and_flush(FreehandBase *dc, gboolean forceclosed) dc->blue_curve->reset(); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(dc->blue_bpath), NULL); - // Blue2 - dc->blue2_curve->reset(); - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(dc->blue2_bpath), NULL); - // Red if (dc->red_curve_is_valid) { c->append_continuous(dc->red_curve, 0.0625); @@ -610,12 +597,12 @@ 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 (dc->sa->start) { - s = reverse_then_unref(s); - } if(prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 1 || prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 2){ - dc->overwriteCurve = s; + s = dc->overwriteCurve; + } + if (dc->sa->start) { + s = reverse_then_unref(s); } s->append_continuous(c, 0.0625); c->unref(); @@ -791,15 +778,6 @@ static void spdc_free_colors(FreehandBase *dc) dc->blue_curve = dc->blue_curve->unref(); } - // Blue2 - if (dc->blue2_bpath) { - sp_canvas_item_destroy(SP_CANVAS_ITEM(dc->blue2_bpath)); - dc->blue2_bpath = NULL; - } - if (dc->blue2_curve) { - dc->blue2_curve = dc->blue2_curve->unref(); - } - // Green while (dc->green_bpaths) { sp_canvas_item_destroy(SP_CANVAS_ITEM(dc->green_bpaths->data)); diff --git a/src/ui/tools/freehand-base.h b/src/ui/tools/freehand-base.h index 6e04e03b7..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; @@ -64,10 +65,6 @@ public: SPCanvasItem *blue_bpath; SPCurve *blue_curve; - // Blue2 - SPCanvasItem *blue2_bpath; - SPCurve *blue2_curve; - // Green GSList *green_bpaths; SPCurve *green_curve; diff --git a/src/ui/tools/node-tool.cpp b/src/ui/tools/node-tool.cpp index d4d7b2c35..0b98bacc1 100644 --- a/src/ui/tools/node-tool.cpp +++ b/src/ui/tools/node-tool.cpp @@ -305,27 +305,23 @@ void NodeTool::update_helperpath () { if (SP_IS_LPE_ITEM(selection->singleItem())) { Inkscape::LivePathEffect::Effect *lpe = SP_LPE_ITEM(selection->singleItem())->getCurrentLPE(); if (lpe && lpe->isVisible()/* && lpe->showOrigPath()*/) { - if (lpe) { - SPCurve *c = new SPCurve(); - SPCurve *cc = new SPCurve(); - std::vector<Geom::PathVector> cs = lpe->getCanvasIndicators(SP_LPE_ITEM(selection->singleItem())); - for (std::vector<Geom::PathVector>::iterator p = cs.begin(); p != cs.end(); ++p) { - cc->set_pathvector(*p); - c->append(cc, false); - cc->reset(); - } - if (!c->is_empty()) { - c->transform(selection->singleItem()->i2dt_affine()); - SPCanvasItem *helperpath = sp_canvas_bpath_new(sp_desktop_tempgroup(this->desktop), c); - sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helperpath), - 0x0000ff9A, 1.0, - SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); - sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helperpath), 0, SP_WIND_RULE_NONZERO); - this->helperpath_tmpitem = this->desktop->add_temporary_canvasitem(helperpath,0); - } - c->unref(); - cc->unref(); - } + SPCurve *c = new SPCurve(); + SPCurve *cc = new SPCurve(); + std::vector<Geom::PathVector> cs = lpe->getCanvasIndicators(SP_LPE_ITEM(selection->singleItem())); + for (std::vector<Geom::PathVector>::iterator p = cs.begin(); p != cs.end(); ++p) { + cc->set_pathvector(*p); + c->append(cc, false); + cc->reset(); + } + if (!c->is_empty()) { + SPCanvasItem *helperpath = sp_canvas_bpath_new(sp_desktop_tempgroup(this->desktop), c); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helperpath), 0x0000ff9A, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helperpath), 0, SP_WIND_RULE_NONZERO); + sp_canvas_item_affine_absolute(helperpath, selection->singleItem()->i2dt_affine()); + this->helperpath_tmpitem = this->desktop->add_temporary_canvasitem(helperpath, 0); + } + c->unref(); + cc->unref(); } } } @@ -524,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/pen-tool.cpp b/src/ui/tools/pen-tool.cpp index 9a73d497f..2c5ffc182 100644 --- a/src/ui/tools/pen-tool.cpp +++ b/src/ui/tools/pen-tool.cpp @@ -181,6 +181,7 @@ void PenTool::_pen_context_set_mode(guint mode) { // define the nodes this->spiro = (mode == 1); this->bspline = (mode == 2); + this->_bspline_spiro_color(); } /** @@ -188,7 +189,6 @@ void PenTool::_pen_context_set_mode(guint mode) { */ void PenTool::setup() { FreehandBase::setup(); - ControlManager &mgr = ControlManager::getManager(); // Pen indicators @@ -239,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(); @@ -433,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; @@ -700,11 +702,9 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) { } // calls the function "bspline_spiro_motion" when the mouse starts or stops moving if(this->bspline){ - this->_bspline_spiro_color(); 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_color(); this->_bspline_spiro_motion(mevent.state & GDK_SHIFT_MASK); pen_drag_origin_w = event_w; } @@ -744,6 +744,7 @@ 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; } @@ -1326,9 +1327,6 @@ void PenTool::_resetColors() { // Blue this->blue_curve->reset(); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->blue_bpath), NULL); - // Blue2 - this->blue2_curve->reset(); - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->blue2_bpath), NULL); // Green while (this->green_bpaths) { sp_canvas_item_destroy(SP_CANVAS_ITEM(this->green_bpaths->data)); @@ -1385,42 +1383,32 @@ void PenTool::_setAngleDistanceStatusMessage(Geom::Point const p, int pc_point_t // 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() { - bool remake_green_bpaths = false; + static Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if(this->spiro){ - //If the colour is not defined as trasparent, por example when changing - //from drawing to spiro mode or when selecting the pen tool - if(this->green_color != 0x00ff000){ - //We change the green and red colours to transparent, so this lines are not necessary - //to the drawing with spiro - this->red_color = 0xff00000; - this->green_color = 0x00ff000; - remake_green_bpaths = true; - } + this->red_color = 0xff00000; + this->green_color = 0x00ff000; }else if(this->bspline){ - //If we come from working with the spiro curve and change the mode the "green_curve" colour is transparent - if(this->green_color != 0xff00007f){ - //since we are not im spiro mode, we assign the original colours - //to the red and the green curve, removing their transparency - this->red_color = 0xff00007f; - //Damos color rojo a la linea verde + 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; - remake_green_bpaths = true; + this->red_color = 0xff00007f; + } else { + this->green_color = this->highlight_color; + this->red_color = this->highlight_color; } }else{ - //If we come from working with the spiro curve and change the mode the "green_curve" colour is transparent - if(this->green_color != 0x00ff007f){ - //since we are not im spiro mode, we assign the original colours - //to the red and the green curve, removing their transparency - this->red_color = 0xff00007f; + 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; - remake_green_bpaths = true; + } else { + this->green_color = this->highlight_color; } - //we hide the spiro/bspline rests - sp_canvas_item_hide(this->blue2_bpath); + 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 && remake_green_bpaths) { + if (this->green_bpaths) { // remove old piecewise green canvasitems while (this->green_bpaths) { sp_canvas_item_destroy(SP_CANVAS_ITEM(this->green_bpaths->data)); @@ -1584,6 +1572,9 @@ void PenTool::_bspline_spiro_motion(bool shift){ 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{ @@ -1607,8 +1598,11 @@ void PenTool::_bspline_spiro_motion(bool shift){ SBasisWPower = WPower->first_segment()->toSBasis(); WPower->reset(); this->p[1] = SBasisWPower.valueAt(WP); - if(!Geom::are_near(this->p[1],this->p[0])) + 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{ @@ -1795,11 +1789,11 @@ void PenTool::_bspline_spiro_build() this->_spiro_doEffect(curve); } - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->blue2_bpath), curve); - sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(this->blue2_bpath), this->blue_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); - sp_canvas_item_show(this->blue2_bpath); + 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->blue2_curve->reset(); + this->blue_curve->reset(); //We hide the holders that doesn't contribute anything if(this->spiro){ sp_canvas_item_show(this->c1); @@ -1811,7 +1805,7 @@ void PenTool::_bspline_spiro_build() sp_canvas_item_hide(this->cl0); }else{ //if the curve is empty - sp_canvas_item_hide(this->blue2_bpath); + sp_canvas_item_hide(this->blue_bpath); } } @@ -1819,155 +1813,145 @@ void PenTool::_bspline_spiro_build() void PenTool::_bspline_doEffect(SPCurve * curve) { // commenting the function doEffect in src/live_effects/lpe-bspline.cpp - Geom::PathVector const original_pathv = curve->get_pathvector(); - if (curve->get_segment_count() < 1) + 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(); - for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { + //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(); // incoming curve - Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve - Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop + 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::Point nextPointAt2(0,0); - Geom::Point nextPointAt3(0,0); - Geom::D2< Geom::SBasis > SBasisIn; - Geom::D2< Geom::SBasis > SBasisOut; - Geom::D2< Geom::SBasis > SBasisHelper; + 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 Geom::LineSegment. + 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(); } } - while ( curve_it2 != curve_endit ) - { - SPCurve * in = new SPCurve(); + 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){ + cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); + if (cubic) { SBasisIn = in->first_segment()->toSBasis(); - pointAt1 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[1],*in->first_segment())); - pointAt2 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[2],*in->first_segment())); - }else{ + 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; - 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(); - nextPointAt1 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[1],*out->first_segment())); - nextPointAt2 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[2],*out->first_segment()));; - nextPointAt3 = (*cubic)[3]; - }else{ - nextPointAt1 = out->first_segment()->initialPoint(); - nextPointAt2 = out->first_segment()->finalPoint(); - nextPointAt3 = out->first_segment()->finalPoint(); + 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); } - out->reset(); - delete out; - 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); - SPCurve *curveHelper = new SPCurve(); - curveHelper->moveto(previousNode); - curveHelper->curveto(pointAt1, pointAt2, node); - nCurve->append_continuous(curveHelper, 0.0625); - curveHelper->reset(); - delete curveHelper; ++curve_it1; ++curve_it2; } - SPCurve *out = new SPCurve(); - out->moveto(curve_it1->initialPoint()); - out->lineto(curve_it1->finalPoint()); - cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); - if (cubic) { - SBasisOut = out->first_segment()->toSBasis(); - nextPointAt1 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[1], *out->first_segment())); - nextPointAt2 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[2], *out->first_segment())); - nextPointAt3 = out->first_segment()->finalPoint(); - } else { - nextPointAt1 = out->first_segment()->initialPoint(); - nextPointAt2 = out->first_segment()->finalPoint(); - nextPointAt3 = out->first_segment()->finalPoint(); - } - out->reset(); - delete out; - SPCurve *curveHelper = new SPCurve(); - curveHelper->moveto(node); - Geom::Point startNode = path_it->begin()->initialPoint(); - if (path_it->closed()) { - 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); - curveHelper->curveto(nextPointAt1, nextPointAt2, startNode); - nCurve->append_continuous(curveHelper, 0.0625); - nCurve->move_endpoints(startNode,startNode); - }else{ - SPCurve * start = new SPCurve(); - start->moveto(path_it->begin()->initialPoint()); - start->lineto(path_it->begin()->finalPoint()); - startNode = start->first_segment()->initialPoint(); - start->reset(); - delete start; - curveHelper->curveto(nextPointAt1, nextPointAt2, nextPointAt3); - nCurve->append_continuous(curveHelper, 0.0625); - nCurve->move_endpoints(startNode,nextPointAt3); - } - curveHelper->reset(); - delete curveHelper; if (path_it->closed()) { nCurve->closepath_current(); } - curve->append(nCurve,false); + curve->append(nCurve, false); nCurve->reset(); delete nCurve; } diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index 0e8660248..ca6dffc12 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -803,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); @@ -816,7 +817,6 @@ void PencilTool::_fitAndSplit() { using Geom::X; using Geom::Y; // if we are in BSpline we modify the trace to create adhoc nodes - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); guint mode = prefs->getInt("/tools/freehand/pencil/freehand-mode", 0); if(mode == 2){ Geom::Point B = b[0] + (1./3)*(b[3] - b[0]); @@ -855,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/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/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/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..2afdc02a6 --- /dev/null +++ b/src/ui/widget/highlight-picker.cpp @@ -0,0 +1,214 @@ +/* + * 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 "display/cairo-utils.h" + +#include <gtkmm/icontheme.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..2f06225bc --- /dev/null +++ b/src/ui/widget/insertordericon.cpp @@ -0,0 +1,173 @@ +/* + * 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/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..4b4b51de2 --- /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/property.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 : |
