summaryrefslogtreecommitdiffstats
path: root/src/ui
diff options
context:
space:
mode:
authorJabiertxof <jtx@jtx>2017-03-16 19:08:44 +0000
committerJabiertxof <jtx@jtx>2017-03-16 19:08:44 +0000
commit8330d0ef2b97c73121ead78ea9fbcec6ee01f879 (patch)
tree1b1717d1706ee6ebfecc800f2cc80430eb0450e0 /src/ui
parentupdate to trunk (diff)
parentFix rendering when canvas rotated. General code clean-up and documentation. (diff)
downloadinkscape-8330d0ef2b97c73121ead78ea9fbcec6ee01f879.tar.gz
inkscape-8330d0ef2b97c73121ead78ea9fbcec6ee01f879.zip
Update to trunk
(bzr r13645.1.170)
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/CMakeLists.txt6
-rw-r--r--src/ui/clipboard.cpp56
-rw-r--r--src/ui/dialog/aboutbox.cpp10
-rw-r--r--src/ui/dialog/cssdialog.cpp146
-rw-r--r--src/ui/dialog/cssdialog.h93
-rw-r--r--src/ui/dialog/dialog-manager.cpp9
-rw-r--r--src/ui/dialog/dialog-manager.h10
-rw-r--r--src/ui/dialog/filedialogimpl-win32.cpp164
-rw-r--r--src/ui/dialog/lpe-fillet-chamfer-properties.cpp18
-rw-r--r--src/ui/dialog/lpe-fillet-chamfer-properties.h2
-rw-r--r--src/ui/dialog/lpe-powerstroke-properties.cpp17
-rw-r--r--src/ui/dialog/lpe-powerstroke-properties.h2
-rw-r--r--src/ui/dialog/prototype.cpp171
-rw-r--r--src/ui/dialog/prototype.h80
-rw-r--r--src/ui/dialog/styledialog.cpp1395
-rw-r--r--src/ui/dialog/styledialog.h175
-rw-r--r--src/ui/dialog/symbols.cpp52
-rw-r--r--src/ui/interface.cpp11
-rw-r--r--src/ui/object-edit.cpp8
-rw-r--r--src/ui/tool/multi-path-manipulator.h15
-rw-r--r--src/ui/tool/node.h5
-rw-r--r--src/ui/tools/flood-tool.cpp59
-rw-r--r--src/ui/tools/freehand-base.h6
-rw-r--r--src/ui/tools/mesh-tool.cpp2
-rw-r--r--src/ui/tools/select-tool.cpp2
-rw-r--r--src/ui/tools/text-tool.cpp56
-rw-r--r--src/ui/tools/text-tool.h1
-rw-r--r--src/ui/tools/tool-base.cpp26
-rw-r--r--src/ui/widget/panel.h1
29 files changed, 2362 insertions, 236 deletions
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index f2a256698..259a06013 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -57,6 +57,7 @@ set(ui_SRC
dialog/calligraphic-profile-rename.cpp
dialog/clonetiler.cpp
dialog/color-item.cpp
+ dialog/cssdialog.cpp
dialog/debug.cpp
dialog/desktop-tracker.cpp
dialog/dialog-manager.cpp
@@ -98,7 +99,9 @@ set(ui_SRC
dialog/polar-arrange-tab.cpp
dialog/print-colors-preview-dialog.cpp
dialog/print.cpp
+ dialog/prototype.cpp
dialog/spellcheck.cpp
+ dialog/styledialog.cpp
dialog/svg-fonts-dialog.cpp
dialog/swatches.cpp
dialog/symbols.cpp
@@ -195,6 +198,7 @@ set(ui_SRC
dialog/calligraphic-profile-rename.h
dialog/clonetiler.h
dialog/color-item.h
+ dialog/cssdialog.h
dialog/debug.h
dialog/desktop-tracker.h
dialog/dialog-manager.h
@@ -238,7 +242,9 @@ set(ui_SRC
dialog/polar-arrange-tab.h
dialog/print-colors-preview-dialog.h
dialog/print.h
+ dialog/prototype.h
dialog/spellcheck.h
+ dialog/styledialog.h
dialog/svg-fonts-dialog.h
dialog/swatches.h
dialog/symbols.h
diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp
index c1e824c1e..734640584 100644
--- a/src/ui/clipboard.cpp
+++ b/src/ui/clipboard.cpp
@@ -76,6 +76,7 @@
#include "sp-namedview.h"
#include "persp3d.h"
#include "object-set.h"
+#include "extension/find_extension_by_mime.h"
/// Made up mimetype to represent Gdk::Pixbuf clipboard contents.
#define CLIPBOARD_GDK_PIXBUF_TARGET "image/x-gdk-pixbuf"
@@ -143,6 +144,11 @@ private:
Inkscape::XML::Node *_clipnode; ///< The node that holds extra information
Inkscape::XML::Document *_doc; ///< Reference to the clipboard's Inkscape::XML::Document
std::set<SPItem*> cloned_elements;
+ std::vector<SPCSSAttr*> te_selected_style;
+ std::vector<unsigned> te_selected_style_positions;
+ int nr_blocks = 0;
+ unsigned copied_style_length = 0;
+
// we need a way to copy plain text AND remember its style;
// the standard _clipnode is only available in an SVG tree, hence this special storage
@@ -241,6 +247,9 @@ void ClipboardManagerImpl::copy(ObjectSet *set)
sp_repr_css_attr_unref(_text_style);
_text_style = NULL;
}
+ te_selected_style.clear();
+ te_selected_style_positions.clear();
+ te_selected_style = Inkscape::UI::Tools::sp_text_get_selected_style(desktop->event_context, &copied_style_length, &nr_blocks, &te_selected_style_positions);
_text_style = Inkscape::UI::Tools::sp_text_get_style_at_cursor(desktop->event_context);
return;
}
@@ -945,15 +954,7 @@ bool ClipboardManagerImpl::_pasteImage(SPDocument *doc)
return false;
}
- // TODO unify with interface.cpp's sp_ui_drag_data_received()
- // AARGH stupid
- Inkscape::Extension::DB::InputList o;
- Inkscape::Extension::db.get_input_list(o);
- Inkscape::Extension::DB::InputList::const_iterator i = o.begin();
- while (i != o.end() && strcmp( (*i)->get_mimetype(), "image/png" ) != 0) {
- ++i;
- }
- Inkscape::Extension::Extension *png = *i;
+ Inkscape::Extension::Extension *png = Inkscape::Extension::find_by_mime("image/png");
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
Glib::ustring attr_saved = prefs->getString("/dialogs/import/link");
bool ask_saved = prefs->getBool("/dialogs/import/ask");
@@ -983,7 +984,42 @@ bool ClipboardManagerImpl::_pasteText(SPDesktop *desktop)
// if the text editing tool is active, paste the text into the active text object
if (tools_isactive(desktop, TOOLS_TEXT)) {
- return Inkscape::UI::Tools::sp_text_paste_inline(desktop->event_context);
+ if(Inkscape::UI::Tools::sp_text_paste_inline(desktop->event_context) == false)
+ return false;
+ //apply the saved style to pasted text
+ Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
+ Glib::ustring const clip_text = refClipboard->wait_for_text();
+ Glib::ustring text(clip_text);
+ if(text.length() == copied_style_length)
+ {
+ Inkscape::UI::Tools::TextTool *tc = SP_TEXT_CONTEXT(desktop->event_context);
+ Inkscape::Text::Layout const *layout = te_get_layout(tc->text);
+ Inkscape::Text::Layout::iterator it_next;
+ Inkscape::Text::Layout::iterator it = tc->text_sel_end;
+
+ SPCSSAttr *css = take_style_from_item(tc->text);
+ for (int i = 0; i < nr_blocks; ++i)
+ {
+ gchar const *w = sp_repr_css_property(css, "font-size", "40px");
+ if (w)
+ sp_repr_css_set_property(te_selected_style[i], "font-size", w);
+ }
+
+ for (int i = 0; i < text.length(); ++i)
+ it.prevCharacter();
+ it_next = layout->charIndexToIterator(layout->iteratorToCharIndex(it));
+
+ for (int i = 0; i < nr_blocks; ++i)
+ {
+ for (int j = te_selected_style_positions[i]; j < te_selected_style_positions[i+1]; ++j)
+ it_next.nextCharacter();
+ sp_te_apply_style(tc->text, it, it_next, te_selected_style[i]);
+ te_update_layout_now_recursive(tc->text);
+ for (int j = te_selected_style_positions[i]; j < te_selected_style_positions[i+1]; ++j)
+ it.nextCharacter();
+ }
+ }
+ return true;
}
// try to parse the text as a color and, if successful, apply it as the current style
diff --git a/src/ui/dialog/aboutbox.cpp b/src/ui/dialog/aboutbox.cpp
index 8f0545e96..805bfb562 100644
--- a/src/ui/dialog/aboutbox.cpp
+++ b/src/ui/dialog/aboutbox.cpp
@@ -115,7 +115,7 @@ AboutBox::AboutBox() : Gtk::Dialog(_("About Inkscape")) {
Gtk::Label *link = new Gtk::Label();
const gchar *website_link =
- "<a href=\"https://www.inkscape.org\"> https://www.inkscape.org</a>";
+ "<a href=\"https://www.inkscape.org\">https://www.inkscape.org</a>";
link->set_markup(website_link);
link->set_alignment(Gtk::ALIGN_END);
@@ -152,16 +152,16 @@ Gtk::Widget *build_splash_widget() {
the `screens' directory. Thus the translation of "about.svg" should be
the filename of its translated version, e.g. about.zh.svg for Chinese.
- N.B. about.svg changes once per release. (We should probably rename
- the original to about-0.40.svg etc. as soon as we have a translation.
- If we do so, then add an item to release-checklist saying that the
- string here should be changed.) */
+ Please don't translate the filename unless the translated picture exists. */
// FIXME? INKSCAPE_SCREENSDIR and "about.svg" are in UTF-8, not the
// native filename encoding... and the filename passed to sp_document_new
// should be in UTF-*8..
char *about=g_build_filename(INKSCAPE_SCREENSDIR, _("about.svg"), NULL);
+ if (!g_file_test (about, G_FILE_TEST_EXISTS)) {
+ about=g_build_filename(INKSCAPE_SCREENSDIR, "about.svg", NULL);
+ }
SPDocument *doc=SPDocument::createNewDoc (about, TRUE);
g_free(about);
g_return_val_if_fail(doc != NULL, NULL);
diff --git a/src/ui/dialog/cssdialog.cpp b/src/ui/dialog/cssdialog.cpp
new file mode 100644
index 000000000..85c804b75
--- /dev/null
+++ b/src/ui/dialog/cssdialog.cpp
@@ -0,0 +1,146 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ * Tavmjong Bah
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "cssdialog.h"
+#include "ui/widget/addtoicon.h"
+#include "widgets/icon.h"
+#include "verbs.h"
+#include "sp-object.h"
+#include "selection.h"
+#include "xml/attribute-record.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * Constructor
+ * A treeview whose each row corresponds to a CSS property of selector selected.
+ * New CSS property can be added by clicking '+' at bottom of the CSS pane. '-'
+ * in front of the CSS property row can be clicked to delete the CSS property.
+ * Besides clicking on an already selected property row makes the property editable
+ * and clicking 'Enter' updates the property with changes reflected in the
+ * drawing.
+ */
+CssDialog::CssDialog():
+ UI::Widget::Panel("", "/dialogs/css", SP_VERB_DIALOG_CSS),
+ _desktop(0)
+{
+ set_size_request(20, 15);
+ _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET);
+ _treeView.set_headers_visible(true);
+ _scrolledWindow.add(_treeView);
+ _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
+ _store = Gtk::ListStore::create(_cssColumns);
+ _treeView.set_model(_store);
+
+ Inkscape::UI::Widget::AddToIcon * addRenderer = manage(new Inkscape::UI::Widget::AddToIcon());
+ addRenderer->property_active() = false;
+
+ int addCol = _treeView.append_column("", *addRenderer) - 1;
+ Gtk::TreeViewColumn *col = _treeView.get_column(addCol);
+ if (col) {
+ col->add_attribute(addRenderer->property_active(), _cssColumns._colUnsetProp);
+ }
+
+ _propRenderer = Gtk::manage(new Gtk::CellRendererText());
+ _propRenderer->property_editable() = true;
+ int nameColNum = _treeView.append_column("CSS Property", *_propRenderer) - 1;
+ _propCol = _treeView.get_column(nameColNum);
+ if (_propCol) {
+ _propCol->add_attribute(_propRenderer->property_text(), _cssColumns._propertyLabel);
+ }
+
+ _sheetRenderer = Gtk::manage(new Gtk::CellRendererText());
+ _sheetRenderer->property_editable() = true;
+ int sheetColNum = _treeView.append_column("Style Sheet", *_sheetRenderer) - 1;
+ _sheetCol = _treeView.get_column(sheetColNum);
+ if (_sheetCol) {
+ _sheetCol->add_attribute(_sheetRenderer->property_text(), _cssColumns._styleSheetVal);
+ }
+
+ _attrRenderer = Gtk::manage(new Gtk::CellRendererText());
+ _attrRenderer->property_editable() = false;
+ int attrColNum = _treeView.append_column("Style Attribute", *_attrRenderer) - 1;
+ _attrCol = _treeView.get_column(attrColNum);
+ if (_attrCol) {
+ _attrCol->add_attribute(_attrRenderer->property_text(), _cssColumns._styleAttrVal);
+ }
+
+ _styleButton(_buttonAddProperty, "list-add", "Add a new property");
+
+ _mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK);
+ _buttonBox.pack_start(_buttonAddProperty, Gtk::PACK_SHRINK);
+
+ _getContents()->pack_start(_mainBox, Gtk::PACK_EXPAND_WIDGET);
+
+ setDesktop(getDesktop());
+
+ _buttonAddProperty.signal_clicked().connect(sigc::mem_fun(*this, &CssDialog::_addProperty));
+}
+
+
+/**
+ * @brief CssDialog::~CssDialog
+ * Class destructor
+ */
+CssDialog::~CssDialog()
+{
+ setDesktop(NULL);
+}
+
+
+/**
+ * @brief CssDialog::setDesktop
+ * @param desktop
+ * This function sets the 'desktop' for the CSS pane.
+ */
+void CssDialog::setDesktop(SPDesktop* desktop)
+{
+ _desktop = desktop;
+}
+
+
+/**
+ * @brief CssDialog::_styleButton
+ * @param btn
+ * @param iconName
+ * @param tooltip
+ * This function sets the style of '+'button at the bottom of dialog.
+ */
+void CssDialog::_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(*manage(Glib::wrap(child)));
+ btn.set_relief(Gtk::RELIEF_NONE);
+ btn.set_tooltip_text(tooltip);
+}
+
+
+/**
+ * @brief CssDialog::_addProperty
+ * This function is a slot to signal_clicked for '+' button at the bottom of CSS
+ * panel. A new row is added, double clicking which text for new property can be
+ * added.
+ */
+void CssDialog::_addProperty()
+{
+ _propRow = *(_store->append());
+}
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
diff --git a/src/ui/dialog/cssdialog.h b/src/ui/dialog/cssdialog.h
new file mode 100644
index 000000000..31eb67e3f
--- /dev/null
+++ b/src/ui/dialog/cssdialog.h
@@ -0,0 +1,93 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ * Tavmjong Bah
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef CSSDIALOG_H
+#define CSSDIALOG_H
+
+#include <gtkmm/treeview.h>
+#include <gtkmm/liststore.h>
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/dialog.h>
+#include <ui/widget/panel.h>
+
+#include "desktop.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * @brief The CssDialog class
+ * This dialog allows to add, delete and modify CSS properties for selectors
+ * created in Style Dialog. Double clicking any selector in Style dialog, a list
+ * of CSS properties will show up in this dialog (if any exist), else new properties
+ * can be added and each new property forms a new row in this pane.
+ */
+class CssDialog : public UI::Widget::Panel
+{
+public:
+ CssDialog();
+ ~CssDialog();
+
+ static CssDialog &getInstance() { return *new CssDialog(); }
+
+ // Data structure
+ class CssColumns : public Gtk::TreeModel::ColumnRecord {
+ public:
+ CssColumns() {
+ add(_colUnsetProp);
+ add(_propertyLabel);
+ add(_styleSheetVal);
+ add(_styleAttrVal);
+ }
+ Gtk::TreeModelColumn<bool> _colUnsetProp;
+ Gtk::TreeModelColumn<Glib::ustring> _propertyLabel;
+ Gtk::TreeModelColumn<Glib::ustring> _styleSheetVal;
+ Gtk::TreeModelColumn<Glib::ustring> _styleAttrVal;
+ };
+ CssColumns _cssColumns;
+
+ // TreeView
+ Gtk::TreeView _treeView;
+ Glib::RefPtr<Gtk::ListStore> _store;
+ Gtk::TreeModel::Row _propRow;
+ Gtk::CellRendererText *_propRenderer;
+ Gtk::CellRendererText *_sheetRenderer;
+ Gtk::CellRendererText *_attrRenderer;
+ Gtk::TreeViewColumn *_propCol;
+ Gtk::TreeViewColumn *_sheetCol;
+ Gtk::TreeViewColumn *_attrCol;
+
+ // Widgets
+ Gtk::VBox _mainBox;
+ Gtk::ScrolledWindow _scrolledWindow;
+ Gtk::HBox _buttonBox;
+ Gtk::Button _buttonAddProperty;
+
+ // Variables - Inkscape
+ SPDesktop* _desktop;
+
+ // Helper functions
+ void setDesktop(SPDesktop* desktop);
+ void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip);
+
+ // Signal handlers
+ void _addProperty();
+};
+
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+#endif // CSSDIALOG_H
diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp
index c53112656..01cd9dd0f 100644
--- a/src/ui/dialog/dialog-manager.cpp
+++ b/src/ui/dialog/dialog-manager.cpp
@@ -19,6 +19,7 @@
#include "ui/dialog/dialog-manager.h"
+#include "ui/dialog/prototype.h"
#include "ui/dialog/align-and-distribute.h"
#include "ui/dialog/document-metadata.h"
#include "ui/dialog/document-properties.h"
@@ -57,6 +58,8 @@
#include "ui/dialog/svg-fonts-dialog.h"
#include "ui/dialog/objects.h"
#include "ui/dialog/tags.h"
+#include "ui/dialog/styledialog.h"
+#include "ui/dialog/cssdialog.h"
namespace Inkscape {
namespace UI {
@@ -103,6 +106,7 @@ DialogManager::DialogManager() {
registerFactory("InkscapePreferences", &create<InkscapePreferences, FloatingBehavior>);
if (dialogs_type == FLOATING) {
+ registerFactory("Prototype", &create<Prototype, FloatingBehavior>);
registerFactory("AlignAndDistribute", &create<AlignAndDistribute, FloatingBehavior>);
registerFactory("DocumentMetadata", &create<DocumentMetadata, FloatingBehavior>);
registerFactory("DocumentProperties", &create<DocumentProperties, FloatingBehavior>);
@@ -125,6 +129,8 @@ DialogManager::DialogManager() {
registerFactory("Swatches", &create<SwatchesPanel, FloatingBehavior>);
registerFactory("TileDialog", &create<ArrangeDialog, FloatingBehavior>);
registerFactory("Symbols", &create<SymbolsDialog, FloatingBehavior>);
+ registerFactory("StyleDialog", &create<StyleDialog, FloatingBehavior>);
+ registerFactory("CssDialog", &create<CssDialog, FloatingBehavior>);
#if HAVE_POTRACE
registerFactory("Trace", &create<TraceDialog, FloatingBehavior>);
@@ -142,6 +148,7 @@ DialogManager::DialogManager() {
} else {
+ registerFactory("Prototype", &create<Prototype, DockBehavior>);
registerFactory("AlignAndDistribute", &create<AlignAndDistribute, DockBehavior>);
registerFactory("DocumentMetadata", &create<DocumentMetadata, DockBehavior>);
registerFactory("DocumentProperties", &create<DocumentProperties, DockBehavior>);
@@ -164,6 +171,8 @@ DialogManager::DialogManager() {
registerFactory("Swatches", &create<SwatchesPanel, DockBehavior>);
registerFactory("TileDialog", &create<ArrangeDialog, DockBehavior>);
registerFactory("Symbols", &create<SymbolsDialog, DockBehavior>);
+ registerFactory("StyleDialog", &create<StyleDialog, DockBehavior>);
+ registerFactory("CssDialog", &create<CssDialog, DockBehavior>);
#if HAVE_POTRACE
registerFactory("Trace", &create<TraceDialog, DockBehavior>);
diff --git a/src/ui/dialog/dialog-manager.h b/src/ui/dialog/dialog-manager.h
index 15573f760..36e9a12be 100644
--- a/src/ui/dialog/dialog-manager.h
+++ b/src/ui/dialog/dialog-manager.h
@@ -30,11 +30,11 @@ public:
static DialogManager &getInstance();
- sigc::signal<void> show_dialogs;
- sigc::signal<void> show_f12;
- sigc::signal<void> hide_dialogs;
- sigc::signal<void> hide_f12;
- sigc::signal<void> transientize;
+ // sigc::signal<void> show_dialogs;
+ // sigc::signal<void> show_f12;
+ // sigc::signal<void> hide_dialogs;
+ // sigc::signal<void> hide_f12;
+ // sigc::signal<void> transientize;
/* generic dialog management start */
typedef std::map<GQuark, DialogFactory> FactoryMap;
diff --git a/src/ui/dialog/filedialogimpl-win32.cpp b/src/ui/dialog/filedialogimpl-win32.cpp
index 02d77cba1..1efec7d52 100644
--- a/src/ui/dialog/filedialogimpl-win32.cpp
+++ b/src/ui/dialog/filedialogimpl-win32.cpp
@@ -18,38 +18,23 @@
#endif
#include "filedialogimpl-win32.h"
//General includes
-#include <list>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <set>
+#include <cairomm/win32_surface.h>
#include <gdk/gdkwin32.h>
-#include <glib/gstdio.h>
-#include <glibmm/i18n.h>
+#include <gdkmm/general.h>
#include <glibmm/fileutils.h>
-#include <gtkmm/window.h>
+#include <glibmm/i18n.h>
//Inkscape includes
-#include "inkscape.h"
-#include "ui/dialog-events.h"
+#include "display/cairo-utils.h"
+#include "document.h"
+#include "extension/db.h"
#include "extension/input.h"
#include "extension/output.h"
-#include "extension/db.h"
-
-//#include "display/drawing-item.h"
-//#include "display/drawing.h"
-#include "sp-item.h"
-#include "display/canvas-arena.h"
-
#include "filedialog.h"
-
-#include "sp-root.h"
+#include "helper/pixbuf-ops.h"
#include "preferences.h"
+#include "util/units.h"
-#include <zlib.h>
-#include <cairomm/win32_surface.h>
-#include <cairomm/context.h>
-#include <gdkmm/general.h>
using namespace std;
using namespace Glib;
@@ -807,7 +792,7 @@ LRESULT CALLBACK FileOpenDialogImplWin32::preview_wnd_proc(HWND hwnd, UINT uMsg,
_wsplitpath(pImpl->_path_string, NULL, NULL, szFileName, NULL);
const int iLength = snwprintf(szCaption,
- sizeof(szCaption), L"%s\n%d kB",
+ sizeof(szCaption), L"%ls\n%d kB",
szFileName, pImpl->_preview_file_size);
DrawTextW(dc, szCaption, iLength, &rcCaptionRect,
@@ -1020,15 +1005,11 @@ void FileOpenDialogImplWin32::free_preview()
bool FileOpenDialogImplWin32::set_svg_preview()
{
- return false;
- // NOTE: it's not worth the effort to fix this to use Cairo.
- // Native file dialogs are unmaintainable and should be removed anyway.
- #if 0
const int PreviewSize = 512;
gchar *utf8string = g_utf16_to_utf8((const gunichar2*)_path_string,
_MAX_PATH, NULL, NULL, NULL);
- SPDocument *svgDoc = SPDocument::createNewDoc (utf8string, true);
+ SPDocument *svgDoc = SPDocument::createNewDoc (utf8string, 0);
g_free(utf8string);
// Check the document loaded properly
@@ -1042,87 +1023,39 @@ bool FileOpenDialogImplWin32::set_svg_preview()
}
// Get the size of the document
- const double svgWidth = svgDoc->getWidth();
- const double svgHeight = svgDoc->getHeight();
+ Inkscape::Util::Quantity svgWidth = svgDoc->getWidth();
+ Inkscape::Util::Quantity svgHeight = svgDoc->getHeight();
+ const double svgWidth_px = svgWidth.value("px");
+ const double svgHeight_px = svgHeight.value("px");
// Find the minimum scale to fit the image inside the preview area
- const double scaleFactorX = PreviewSize / svgWidth;
- const double scaleFactorY = PreviewSize / svgHeight;
+ const double scaleFactorX = PreviewSize / svgWidth_px;
+ const double scaleFactorY = PreviewSize / svgHeight_px;
const double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX;
// Now get the resized values
- const double scaledSvgWidth = scaleFactor * svgWidth;
- const double scaledSvgHeight = scaleFactor * svgHeight;
-
- Geom::Rect area(Geom::Point(0, 0), Geom::Point(scaledSvgWidth, scaledSvgHeight));
- NRRectL areaL = {0, 0, scaledSvgWidth, scaledSvgHeight};
- NRRectL bbox = {0, 0, scaledSvgWidth, scaledSvgHeight};
-
- // write object bbox to area
- svgDoc->ensureUpToDate();
- Geom::OptRect maybeArea = area | svgDoc->getRoot()->desktopVisualBounds();
-
- NRArena *const arena = NRArena::create();
-
- unsigned const key = SPItem::display_key_new(1);
-
- NRArenaItem *root = svgDoc->getRoot()->invoke_show(
- arena, key, SP_ITEM_SHOW_DISPLAY);
-
- NRGC gc(NULL);
- gc.transform = Geom::Affine(Geom::Scale(scaleFactor, scaleFactor));
+ const int scaledSvgWidth = round(scaleFactor * svgWidth_px);
+ const int scaledSvgHeight = round(scaleFactor * svgHeight_px);
- nr_arena_item_invoke_update (root, NULL, &gc,
- NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);
-
- // Prepare a GDI compatible NRPixBlock
- NRPixBlock pixBlock;
- pixBlock.size = NR_PIXBLOCK_SIZE_BIG;
- pixBlock.mode = NR_PIXBLOCK_MODE_R8G8B8;
- pixBlock.empty = 1;
- pixBlock.visible_area.x0 = pixBlock.area.x0 = 0;
- pixBlock.visible_area.y0 = pixBlock.area.y0 = 0;
- pixBlock.visible_area.x1 = pixBlock.area.x1 = scaledSvgWidth;
- pixBlock.visible_area.y1 = pixBlock.area.y1 = scaledSvgHeight;
- pixBlock.rs = 4 * ((3 * (int)scaledSvgWidth + 3) / 4);
- pixBlock.data.px = g_try_new (unsigned char, pixBlock.rs * scaledSvgHeight);
-
- // Fail if the pixblock failed to allocate
- if(pixBlock.data.px == NULL)
- {
- svgDoc->doUnref();
- return false;
- }
-
- memset(pixBlock.data.px, 0xFF, pixBlock.rs * scaledSvgHeight);
-
- memcpy(&root->bbox, &areaL, sizeof(areaL));
-
- // Render the image
- nr_arena_item_invoke_render(NULL, root, &bbox, &pixBlock, /*0*/NR_ARENA_ITEM_RENDER_NO_CACHE);
+ const double dpi = 96*scaleFactor;
+ Inkscape::Pixbuf * pixbuf = sp_generate_internal_bitmap(svgDoc, NULL, 0, 0, svgWidth_px, svgHeight_px, scaledSvgWidth, scaledSvgHeight, dpi, dpi, (guint32) 0xffffff00, NULL);
// Tidy up
svgDoc->doUnref();
- svgDoc->getRoot()->invoke_hide(key);
- nr_object_unref((NRObject *) arena);
+ if (pixbuf == NULL) {
+ return false;
+ }
// Create the GDK pixbuf
_mutex->lock();
-
- _preview_bitmap_image = Gdk::Pixbuf::create_from_data(
- pixBlock.data.px, Gdk::COLORSPACE_RGB, false, 8,
- (int)scaledSvgWidth, (int)scaledSvgHeight, pixBlock.rs,
- sigc::ptr_fun(destroy_svg_rendering));
-
- _preview_document_width = scaledSvgWidth;
- _preview_document_height = scaledSvgHeight;
- _preview_image_width = svgWidth;
- _preview_image_height = svgHeight;
-
+ _preview_bitmap_image = Glib::wrap(pixbuf->getPixbufRaw());
+ _preview_document_width = svgWidth_px;
+ _preview_document_height = svgHeight_px;
+ _preview_image_width = scaledSvgWidth;
+ _preview_image_height = scaledSvgHeight;
_mutex->unlock();
return true;
- #endif
}
void FileOpenDialogImplWin32::destroy_svg_rendering(const guint8 *buffer)
@@ -1301,17 +1234,8 @@ bool FileOpenDialogImplWin32::set_emf_preview()
const double emfWidth = w;
const double emfHeight = h;
- // Find the minimum scale to fit the image inside the preview area
- const double scaleFactorX = PreviewSize / emfWidth;
- const double scaleFactorY = PreviewSize / emfHeight;
- const double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX;
-
- // Now get the resized values
- const double scaledEmfWidth = scaleFactor * emfWidth;
- const double scaledEmfHeight = scaleFactor * emfHeight;
-
- _preview_document_width = scaledEmfWidth;
- _preview_document_height = scaledEmfHeight;
+ _preview_document_width = emfWidth / 2540 * 96; // width is in units of 0.01 mm
+ _preview_document_height = emfHeight / 2540 * 96; // height is in units of 0.01 mm
_preview_image_width = emfWidth;
_preview_image_height = emfHeight;
@@ -1370,17 +1294,13 @@ void FileOpenDialogImplWin32::render_preview()
}
// Find the minimum scale to fit the image inside the preview area
- const double scaleFactorX =
- ((double)_preview_width - pagePadding * 2 - blurRadius) / _preview_document_width;
- const double scaleFactorY =
- ((double)_preview_height - pagePadding * 2
- - shaddowOffsetY - halfBlurRadius - captionHeight) / _preview_document_height;
- double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX;
- scaleFactor = (scaleFactor > 1.0) ? 1.0 : scaleFactor;
+ const double scaleFactorX = ((double)_preview_width - pagePadding * 2 - blurRadius) / _preview_image_width;
+ const double scaleFactorY = ((double)_preview_height - pagePadding * 2 - shaddowOffsetY - halfBlurRadius - captionHeight) / _preview_image_height;
+ const double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX;
// Now get the resized values
- const double scaledSvgWidth = scaleFactor * _preview_document_width;
- const double scaledSvgHeight = scaleFactor * _preview_document_height;
+ const double scaledSvgWidth = scaleFactor * _preview_image_width;
+ const double scaledSvgHeight = scaleFactor * _preview_image_height;
const int svgX = pagePadding + halfBlurRadius;
const int svgY = pagePadding;
@@ -1565,7 +1485,7 @@ int FileOpenDialogImplWin32::format_caption(wchar_t *caption, int caption_size)
_wsplitpath(_path_string, NULL, NULL, szFileName, NULL);
return snwprintf(caption, caption_size,
- L"%s\n%d kB\n%d \xD7 %d", szFileName, _preview_file_size,
+ L"%ls\n%d\u2009kB\n%d\u2009px \xD7 %d\u2009px", szFileName, _preview_file_size,
(int)_preview_document_width, (int)_preview_document_height);
}
@@ -1971,18 +1891,18 @@ UINT_PTR CALLBACK FileSaveDialogImplWin32::GetSaveFileName_hookproc(
if(dlgFont) SendMessage(pImpl->_title_edit, WM_SETFONT, (WPARAM)dlgFont, MAKELPARAM(FALSE, 0));
SetWindowPos(pImpl->_title_edit, NULL, rCB1.left-rROOT.left, rCB1.top+ydelta-rROOT.top,
rCB1.right-rCB1.left, rCB1.bottom-rCB1.top, SWP_SHOWWINDOW|SWP_NOZORDER);
- // TODO: make sure this works for Unicode
- SetWindowText(pImpl->_title_edit, pImpl->myDocTitle.c_str());
+ SetWindowTextW(pImpl->_title_edit,
+ (const wchar_t*)g_utf8_to_utf16(pImpl->myDocTitle.c_str(), -1, NULL, NULL, NULL));
}
}
break;
case WM_DESTROY:
{
if(pImpl->_title_edit) {
- int length = GetWindowTextLength(pImpl->_title_edit)+1;
- char* temp_title = new char[length];
- GetWindowText(pImpl->_title_edit, temp_title, length);
- pImpl->myDocTitle = temp_title;
+ int length = GetWindowTextLengthW(pImpl->_title_edit)+1;
+ wchar_t* temp_title = new wchar_t[length];
+ GetWindowTextW(pImpl->_title_edit, temp_title, length);
+ pImpl->myDocTitle = g_utf16_to_utf8((gunichar2*)temp_title, -1, NULL, NULL, NULL);
delete[] temp_title;
DestroyWindow(pImpl->_title_label);
pImpl->_title_label = NULL;
diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp
index c349ffd78..82c6035d4 100644
--- a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp
+++ b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp
@@ -34,32 +34,30 @@ FilletChamferPropertiesDialog::FilletChamferPropertiesDialog()
{
Gtk::Box *mainVBox = get_vbox();
mainVBox->set_homogeneous(false);
- _layout_table.set_spacings(4);
- _layout_table.resize(3, 3);
+ _layout_table.set_row_spacing(4);
+ _layout_table.set_column_spacing(4);
// Layer name widgets
_fillet_chamfer_position_numeric.set_digits(4);
_fillet_chamfer_position_numeric.set_increments(1,1);
//todo: get tha max aloable infinity freeze the widget
_fillet_chamfer_position_numeric.set_range(0., SCALARPARAM_G_MAXDOUBLE);
+ _fillet_chamfer_position_numeric.set_hexpand();
_fillet_chamfer_position_label.set_label(_("Radius (pixels):"));
_fillet_chamfer_position_label.set_alignment(1.0, 0.5);
- _layout_table.attach(_fillet_chamfer_position_label, 0, 1, 0, 1, Gtk::FILL,
- Gtk::FILL);
- _layout_table.attach(_fillet_chamfer_position_numeric, 1, 2, 0, 1,
- Gtk::FILL | Gtk::EXPAND, Gtk::FILL);
+ _layout_table.attach(_fillet_chamfer_position_label, 0, 0, 1, 1);
+ _layout_table.attach(_fillet_chamfer_position_numeric, 1, 0, 1, 1);
_fillet_chamfer_chamfer_subdivisions.set_digits(0);
_fillet_chamfer_chamfer_subdivisions.set_increments(1,1);
//todo: get tha max aloable infinity freeze the widget
_fillet_chamfer_chamfer_subdivisions.set_range(0, SCALARPARAM_G_MAXDOUBLE);
+ _fillet_chamfer_chamfer_subdivisions.set_hexpand();
_fillet_chamfer_chamfer_subdivisions_label.set_label(_("Chamfer subdivisions:"));
_fillet_chamfer_chamfer_subdivisions_label.set_alignment(1.0, 0.5);
- _layout_table.attach(_fillet_chamfer_chamfer_subdivisions_label, 0, 1, 1, 2, Gtk::FILL,
- Gtk::FILL);
- _layout_table.attach(_fillet_chamfer_chamfer_subdivisions, 1, 2, 1, 2,
- Gtk::FILL | Gtk::EXPAND, Gtk::FILL);
+ _layout_table.attach(_fillet_chamfer_chamfer_subdivisions_label, 0, 1, 1, 1);
+ _layout_table.attach(_fillet_chamfer_chamfer_subdivisions, 1, 1, 1, 1);
_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"));
diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.h b/src/ui/dialog/lpe-fillet-chamfer-properties.h
index 105cafe68..4021d6152 100644
--- a/src/ui/dialog/lpe-fillet-chamfer-properties.h
+++ b/src/ui/dialog/lpe-fillet-chamfer-properties.h
@@ -51,7 +51,7 @@ protected:
Gtk::Label _fillet_chamfer_chamfer_subdivisions_label;
Gtk::SpinButton _fillet_chamfer_chamfer_subdivisions;
- Gtk::Table _layout_table;
+ Gtk::Grid _layout_table;
bool _position_visible;
Gtk::Button _close_button;
diff --git a/src/ui/dialog/lpe-powerstroke-properties.cpp b/src/ui/dialog/lpe-powerstroke-properties.cpp
index ca10721db..0757aa1c3 100644
--- a/src/ui/dialog/lpe-powerstroke-properties.cpp
+++ b/src/ui/dialog/lpe-powerstroke-properties.cpp
@@ -40,14 +40,15 @@ PowerstrokePropertiesDialog::PowerstrokePropertiesDialog()
{
Gtk::Box *mainVBox = get_vbox();
- _layout_table.set_spacings(4);
- _layout_table.resize (2, 2);
+ _layout_table.set_row_spacing(4);
+ _layout_table.set_column_spacing(4);
// Layer name widgets
_powerstroke_position_entry.set_activates_default(true);
_powerstroke_position_entry.set_digits(4);
_powerstroke_position_entry.set_increments(1,1);
_powerstroke_position_entry.set_range(-SCALARPARAM_G_MAXDOUBLE, SCALARPARAM_G_MAXDOUBLE);
+ _powerstroke_position_entry.set_hexpand();
_powerstroke_position_label.set_label(_("Position:"));
_powerstroke_position_label.set_alignment(1.0, 0.5);
@@ -55,16 +56,14 @@ PowerstrokePropertiesDialog::PowerstrokePropertiesDialog()
_powerstroke_width_entry.set_digits(4);
_powerstroke_width_entry.set_increments(1,1);
_powerstroke_width_entry.set_range(-SCALARPARAM_G_MAXDOUBLE, SCALARPARAM_G_MAXDOUBLE);
+ _powerstroke_width_entry.set_hexpand();
_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);
+ _layout_table.attach(_powerstroke_position_label,0,0,1,1);
+ _layout_table.attach(_powerstroke_position_entry,1,0,1,1);
+ _layout_table.attach(_powerstroke_width_label, 0,1,1,1);
+ _layout_table.attach(_powerstroke_width_entry, 1,1,1,1);
mainVBox->pack_start(_layout_table, true, true, 4);
diff --git a/src/ui/dialog/lpe-powerstroke-properties.h b/src/ui/dialog/lpe-powerstroke-properties.h
index 1e4c1df5b..56b6e499d 100644
--- a/src/ui/dialog/lpe-powerstroke-properties.h
+++ b/src/ui/dialog/lpe-powerstroke-properties.h
@@ -40,7 +40,7 @@ protected:
Gtk::SpinButton _powerstroke_position_entry;
Gtk::Label _powerstroke_width_label;
Gtk::SpinButton _powerstroke_width_entry;
- Gtk::Table _layout_table;
+ Gtk::Grid _layout_table;
bool _position_visible;
Gtk::Button _close_button;
diff --git a/src/ui/dialog/prototype.cpp b/src/ui/dialog/prototype.cpp
new file mode 100644
index 000000000..b3bf60aab
--- /dev/null
+++ b/src/ui/dialog/prototype.cpp
@@ -0,0 +1,171 @@
+/*
+ * A bare minimum example of deriving from Inkscape::UI:Widget::Panel.
+ *
+ * Author:
+ * Tavmjong Bah
+ *
+ * Copyright (C) Tavmjong Bah <tavmjong@free.fr>
+ *
+ * Released under the GNU GPL, read the file 'COPYING' for more information.
+ */
+
+#include "ui/dialog/prototype.h"
+#include "verbs.h"
+#include "desktop.h"
+#include "document.h"
+#include "selection.h"
+
+// Only for use in demonstration widget.
+#include "sp-root.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+Prototype::Prototype() :
+ // UI::Widget::Panel("Prototype Label", "/dialogs/prototype", SP_VERB_DIALOG_PROTOTYPE,
+ // "Prototype Apply Label", true),
+ UI::Widget::Panel("Prototype Label", "/dialogs/prototype", SP_VERB_DIALOG_PROTOTYPE),
+
+ desktopTracker() //,
+ // desktopChangedConnection()
+{
+ std::cout << "Prototype::Prototype()" << std::endl;
+
+ // A widget for demonstration that displays the current SVG's id.
+ _getContents()->pack_start(label); // Panel::_getContents()
+
+ // desktop is set by Panel constructor so this should never be NULL.
+ // Note, we need to use getDesktop() since _desktop is private in Panel.h.
+ // It should probably be protected instead... but need to verify in doesn't break anything.
+ if (getDesktop() == NULL) {
+ std::cerr << "Prototype::Prototype: desktop is NULL!" << std::endl;
+ }
+
+ connectionDesktopChanged = desktopTracker.connectDesktopChanged(
+ sigc::mem_fun(*this, &Prototype::handleDesktopChanged) );
+ desktopTracker.connect(GTK_WIDGET(gobj()));
+
+ // This results in calling handleDocumentReplaced twice. Fix me!
+ connectionDocumentReplaced = getDesktop()->connectDocumentReplaced(
+ sigc::mem_fun(this, &Prototype::handleDocumentReplaced));
+
+ // Alternative mechanism but results in calling handleDocumentReplaced four times.
+ // signalDocumentReplaced().connect(
+ // sigc::mem_fun(this, &Prototype::handleDocumentReplaced));
+
+ connectionSelectionChanged = getDesktop()->getSelection()->connectChanged(
+ sigc::hide(sigc::mem_fun(this, &Prototype::handleSelectionChanged)));
+
+ updateLabel();
+}
+
+Prototype::~Prototype()
+{
+ // Never actually called.
+ std::cout << "Prototype::~Prototype()" << std::endl;
+ connectionDesktopChanged.disconnect();
+ connectionDocumentReplaced.disconnect();
+ connectionSelectionChanged.disconnect();
+}
+
+/*
+ * Called when a dialog is displayed, including when a dialog is reopened.
+ * (When a dialog is closed, it is not destroyed so the contructor is not called.
+ * This function can handle any reinitialization needed.)
+ */
+void
+Prototype::present()
+{
+ std::cout << "Prototype::present()" << std::endl;
+ UI::Widget::Panel::present();
+}
+
+/*
+ * When Inkscape is first opened, a default document is shown. If another document is immediately
+ * opened, it will replace the default document in the same desktop. This function handles the
+ * change. Bug: This is called twice for some reason.
+ */
+void
+Prototype::handleDocumentReplaced(SPDesktop *desktop, SPDocument * /* document */)
+{
+ std::cout << "Prototype::handleDocumentReplaced()" << std::endl;
+ if (getDesktop() != desktop) {
+ std::cerr << "Prototype::handleDocumentReplaced(): Error: panel desktop not equal to existing desktop!" << std::endl;
+ }
+
+ connectionSelectionChanged.disconnect();
+
+ connectionSelectionChanged = desktop->getSelection()->connectChanged(
+ sigc::hide(sigc::mem_fun(this, &Prototype::handleSelectionChanged)));
+
+ // Update demonstration widget.
+ updateLabel();
+}
+
+/*
+ * When a dialog is floating, it is connected to the active desktop.
+ */
+void
+Prototype::handleDesktopChanged(SPDesktop* desktop) {
+ std::cout << "Prototype::handleDesktopChanged(): " << desktop << std::endl;
+
+ if (getDesktop() == desktop) {
+ // This will happen after construction of Prototype. We've already
+ // set up signals so just return.
+ std::cout << " getDesktop() == desktop" << std::endl;
+ return;
+ }
+
+ // Connections are disconnect safe.
+ connectionSelectionChanged.disconnect();
+ connectionDocumentReplaced.disconnect();
+
+ setDesktop( desktop );
+
+ connectionSelectionChanged = desktop->getSelection()->connectChanged(
+ sigc::hide(sigc::mem_fun(this, &Prototype::handleSelectionChanged)));
+ connectionDocumentReplaced = desktop->connectDocumentReplaced(
+ sigc::mem_fun(this, &Prototype::handleDocumentReplaced));
+
+ // Update demonstration widget.
+ updateLabel();
+}
+
+/*
+ * Handle a change in which objects are selected in a document.
+ */
+void
+Prototype::handleSelectionChanged() {
+ std::cout << "Prototype::handleSelectionChanged()" << std::endl;
+
+ // Update demonstration widget.
+ label.set_label("Selection Changed!");
+}
+
+/*
+ * Update label... just a utility function for this example.
+ */
+void
+Prototype::updateLabel() {
+
+ const gchar* root_id = getDesktop()->getDocument()->getRoot()->getId();
+ Glib::ustring label_string("Document's SVG id: ");
+ label_string += (root_id?root_id:"null");
+ label.set_label(label_string);
+}
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/ui/dialog/prototype.h b/src/ui/dialog/prototype.h
new file mode 100644
index 000000000..95c3856f8
--- /dev/null
+++ b/src/ui/dialog/prototype.h
@@ -0,0 +1,80 @@
+/*
+ * A bare minimum example of deriving from Inkscape::UI:Widget::Panel.
+ *
+ * Author:
+ * Tavmjong Bah
+ *
+ * Copyright (C) Tavmjong Bah <tavmjong@free.fr>
+ *
+ * Released under the GNU GPL, read the file 'COPYING' for more information.
+ */
+
+#ifndef SEEN_PROTOTYPE_PANEL_H
+#define SEEN_PROTOTYPE_PANEL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <iostream>
+#include "ui/widget/panel.h"
+#include "ui/dialog/desktop-tracker.h"
+
+// Only to display status.
+#include <gtkmm/label.h>
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * A panel that does almost nothing!
+ */
+class Prototype : public UI::Widget::Panel
+{
+public:
+ virtual ~Prototype();
+
+ static Prototype& getInstance() { return *new Prototype(); };
+
+ virtual void present();
+
+private:
+
+ // No default constructor, noncopyable, nonassignable
+ Prototype();
+ Prototype(Prototype const &d);
+ Prototype operator=(Prototype const &d);
+
+ // Signals and handlers
+ sigc::connection connectionDocumentReplaced;
+ sigc::connection connectionDesktopChanged;
+ sigc::connection connectionSelectionChanged;
+
+ void handleDocumentReplaced(SPDesktop* desktop, SPDocument *document);
+ void handleDesktopChanged(SPDesktop* desktop);
+ void handleSelectionChanged();
+
+ DesktopTracker desktopTracker;
+
+ // Just for example
+ Gtk::Label label;
+ void updateLabel();
+};
+
+} //namespace Dialogs
+} //namespace UI
+} //namespace Inkscape
+
+#endif // SEEN_PROTOTYPE_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/styledialog.cpp b/src/ui/dialog/styledialog.cpp
new file mode 100644
index 000000000..4576671e7
--- /dev/null
+++ b/src/ui/dialog/styledialog.cpp
@@ -0,0 +1,1395 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ * Tavmjong Bah
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "styledialog.h"
+#include "ui/widget/addtoicon.h"
+#include "widgets/icon.h"
+#include "verbs.h"
+#include "sp-object.h"
+#include "selection.h"
+#include "xml/attribute-record.h"
+#include "xml/node-observer.h"
+#include "attribute-rel-svg.h"
+#include "inkscape.h"
+#include "document-undo.h"
+
+#include <glibmm/i18n.h>
+#include <glibmm/regex.h>
+
+#include <map>
+
+//#define DEBUG_STYLEDIALOG
+
+using Inkscape::DocumentUndo;
+using Inkscape::Util::List;
+using Inkscape::XML::AttributeRecord;
+
+/**
+ * This macro is used to remove spaces around selectors or any strings when
+ * parsing is done to update XML style element or row labels in this dialog.
+ */
+#define REMOVE_SPACES(x) x.erase(0, x.find_first_not_of(' ')); \
+ x.erase(x.find_last_not_of(' ') + 1);
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+class StyleDialog::NodeObserver : public Inkscape::XML::NodeObserver {
+public:
+ NodeObserver(StyleDialog* styleDialog) :
+ _styleDialog(styleDialog)
+ {
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::NodeObserver: Constructor" << std::endl;
+#endif
+ };
+
+ virtual void notifyContentChanged(Inkscape::XML::Node &node,
+ Inkscape::Util::ptr_shared<char> old_content,
+ Inkscape::Util::ptr_shared<char> new_content);
+
+ StyleDialog * _styleDialog;
+};
+
+
+void
+StyleDialog::NodeObserver::notifyContentChanged(
+ Inkscape::XML::Node &/*node*/,
+ Inkscape::Util::ptr_shared<char> /*old_content*/,
+ Inkscape::Util::ptr_shared<char> /*new_content*/ ) {
+
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::NodeObserver::notifyContentChanged" << std::endl;
+#endif
+
+ _styleDialog->_readStyleElement();
+ _styleDialog->_selectRow();
+}
+
+
+StyleDialog::TreeStore::TreeStore()
+{
+}
+
+
+/**
+ * Allow dragging only selectors.
+ */
+bool
+StyleDialog::TreeStore::row_draggable_vfunc(const Gtk::TreeModel::Path& path) const
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::TreeStore::row_draggable_vfunc" << std::endl;
+#endif
+ auto unconstThis = const_cast<StyleDialog::TreeStore*>(this);
+ const_iterator iter = unconstThis->get_iter(path);
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ bool is_draggable = row[_styledialog->_mColumns._colIsSelector];
+ return is_draggable;
+ }
+ return Gtk::TreeStore::row_draggable_vfunc(path);
+}
+
+
+/**
+ * Allow dropping only inbetween other selectors.
+ */
+bool
+StyleDialog::TreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest,
+ const Gtk::SelectionData& selection_data) const
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::TreeStore::row_drop_possible_vfunc" << std::endl;
+#endif
+
+ Gtk::TreeModel::Path dest_parent = dest;
+ dest_parent.up();
+ return dest_parent.empty();
+}
+
+
+// This is only here to handle updating style element after a drag and drop.
+void
+StyleDialog::TreeStore::on_row_deleted(const TreeModel::Path& path)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "on_row_deleted" << std::endl;
+#endif
+
+ if (_styledialog->_updating) return; // Don't write if we deleted row (other than from DND)
+
+ _styledialog->_writeStyleElement();
+}
+
+
+Glib::RefPtr<StyleDialog::TreeStore> StyleDialog::TreeStore::create(StyleDialog *styledialog)
+{
+ StyleDialog::TreeStore * store = new StyleDialog::TreeStore();
+ store->_styledialog = styledialog;
+ store->set_column_types( store->_styledialog->_mColumns );
+ return Glib::RefPtr<StyleDialog::TreeStore>( store );
+}
+
+/**
+ * Constructor
+ * A treeview and a set of two buttons are added to the dialog. _addSelector
+ * adds selectors to treeview. _delSelector deletes the selector from the dialog.
+ * Any addition/deletion of the selectors updates XML style element accordingly.
+ */
+StyleDialog::StyleDialog() :
+ UI::Widget::Panel("", "/dialogs/style", SP_VERB_DIALOG_STYLE),
+ _updating(false),
+ _textNode(NULL),
+ _desktopTracker()
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::StyleDialog" << std::endl;
+#endif
+
+ // Tree
+ Inkscape::UI::Widget::AddToIcon * addRenderer = manage(
+ new Inkscape::UI::Widget::AddToIcon() );
+
+ _store = TreeStore::create(this);
+ _treeView.set_model(_store);
+ _treeView.set_headers_visible(true);
+ _treeView.enable_model_drag_source();
+ _treeView.enable_model_drag_dest( Gdk::ACTION_MOVE );
+
+ int addCol = _treeView.append_column("", *addRenderer) - 1;
+ Gtk::TreeViewColumn *col = _treeView.get_column(addCol);
+ if ( col ) {
+ col->add_attribute( addRenderer->property_active(), _mColumns._colIsSelector );
+ }
+ _treeView.append_column("CSS Selector", _mColumns._colSelector);
+ _treeView.set_expander_column(*(_treeView.get_column(1)));
+
+ // Pack widgets
+ _paned.pack1(_mainBox, Gtk::SHRINK);
+ _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET);
+ _scrolledWindow.add(_treeView);
+ _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
+ create = manage( new Gtk::Button() );
+ _styleButton(*create, "list-add", "Add a new CSS Selector");
+ create->signal_clicked().connect(sigc::mem_fun(*this, &StyleDialog::_addSelector));
+
+ del = manage( new Gtk::Button() );
+ _styleButton(*del, "list-remove", "Remove a CSS Selector");
+ del->signal_clicked().connect(sigc::mem_fun(*this, &StyleDialog::_delSelector));
+ del->set_sensitive(false);
+
+ _mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK);
+
+ _buttonBox.pack_start(*create, Gtk::PACK_SHRINK);
+ _buttonBox.pack_start(*del, Gtk::PACK_SHRINK);
+
+ _getContents()->pack_start(_paned, Gtk::PACK_EXPAND_WIDGET);
+
+ // Dialog size request
+ Gtk::Requisition sreq1, sreq2;
+ get_preferred_size(sreq1, sreq2);
+ int minWidth = 300;
+ int minHeight = 300;
+ minWidth = (sreq2.width > minWidth ? sreq2.width : minWidth );
+ minHeight = (sreq2.height > minHeight ? sreq2.height : minHeight);
+ set_size_request(minWidth, minHeight);
+
+ // Signal handlers
+ _treeView.signal_button_release_event().connect( // Needs to be release, not press.
+ sigc::mem_fun(*this, &StyleDialog::_handleButtonEvent),
+ false);
+
+ _treeView.signal_button_release_event().connect_notify(
+ sigc::mem_fun(*this, &StyleDialog::_buttonEventsSelectObjs),
+ false);
+
+ //_treeView.get_selection()->signal_changed().connect(
+ // sigc::mem_fun(*this, &StyleDialog::_selChanged));
+
+ _objObserver.signal_changed().connect(sigc::mem_fun(*this, &StyleDialog::_objChanged));
+
+
+ // Add CSS dialog
+ _cssPane = new CssDialog;
+ _paned.pack2(*_cssPane, Gtk::SHRINK);
+ _cssPane->show_all();
+
+ _cssPane->_propRenderer->signal_edited().connect(
+ sigc::mem_fun(*this, &StyleDialog::_handleProp));
+ _cssPane->_sheetRenderer->signal_edited().connect(
+ sigc::mem_fun(*this, &StyleDialog::_handleSheet));
+ _cssPane->_attrRenderer->signal_edited().connect(
+ sigc::mem_fun(*this, &StyleDialog::_handleAttr));
+ _cssPane->_treeView.signal_button_release_event().connect(
+ sigc::mem_fun(*this, &StyleDialog::_delProperty),
+ false);
+
+ // Document & Desktop
+ _desktop_changed_connection = _desktopTracker.connectDesktopChanged(
+ sigc::mem_fun(*this, &StyleDialog::_handleDesktopChanged) );
+ _desktopTracker.connect(GTK_WIDGET(gobj()));
+
+ _document_replaced_connection = getDesktop()->connectDocumentReplaced(
+ sigc::mem_fun(this, &StyleDialog::_handleDocumentReplaced));
+
+ _selection_changed_connection = getDesktop()->getSelection()->connectChanged(
+ sigc::hide(sigc::mem_fun(this, &StyleDialog::_handleSelectionChanged)));
+
+ // Load tree
+ _readStyleElement();
+ _selectRow();
+
+ if (!_store->children().empty()) {
+ del->set_sensitive(true);
+ }
+
+}
+
+
+/**
+ * @brief StyleDialog::~StyleDialog
+ * Class destructor
+ */
+StyleDialog::~StyleDialog()
+{
+#ifdef DEBUG_STYLEDIALOOG
+ std::cout << "StyleDialog::~StyleDialog" << std::endl;
+#endif
+ _desktop_changed_connection.disconnect();
+ _document_replaced_connection.disconnect();
+ _selection_changed_connection.disconnect();
+}
+
+
+/**
+ * @brief StyleDialog::_styleTextNode
+ * @return Inkscape::XML::Node* pointing to a style element's text node.
+ * Returns the style element's text node. If there is no style element, one is created.
+ * Ditto for text node.
+ */
+Inkscape::XML::Node* StyleDialog::_getStyleTextNode()
+{
+
+ Inkscape::XML::Node *styleNode = NULL;
+ Inkscape::XML::Node *textNode = NULL;
+
+ Inkscape::XML::Node *root = SP_ACTIVE_DOCUMENT->getReprRoot();
+ for (unsigned i = 0; i < root->childCount(); ++i) {
+ if (Glib::ustring(root->nthChild(i)->name()) == "svg:style") {
+
+ styleNode = root->nthChild(i);
+
+ for (unsigned j = 0; j < styleNode->childCount(); ++j) {
+ if (styleNode->nthChild(j)->type() == Inkscape::XML::TEXT_NODE) {
+ textNode = styleNode->nthChild(j);
+ }
+ }
+
+ if (textNode == NULL) {
+ // Style element found but does not contain text node!
+ std::cerr << "StyleDialog::_getStyleTextNode(): No text node!" << std::endl;
+ textNode = SP_ACTIVE_DOCUMENT->getReprDoc()->createTextNode("");
+ styleNode->appendChild(textNode);
+ Inkscape::GC::release(textNode);
+ }
+ }
+ }
+
+ if (styleNode == NULL) {
+ // Style element not found, create one
+ styleNode = SP_ACTIVE_DOCUMENT->getReprDoc()->createElement("svg:style");
+ textNode = SP_ACTIVE_DOCUMENT->getReprDoc()->createTextNode("");
+
+ styleNode->appendChild(textNode);
+ Inkscape::GC::release(textNode);
+
+ root->addChild(styleNode, NULL);
+ Inkscape::GC::release(styleNode);
+ }
+
+ if (_textNode != textNode) {
+ _textNode = textNode;
+ NodeObserver *no = new NodeObserver(this);
+ textNode->addObserver(*no);
+ }
+
+ return textNode;
+}
+
+
+/**
+ * @brief StyleDialog::_readStyleElement
+ * Fill the Gtk::TreeStore from the svg:style element.
+ */
+void StyleDialog::_readStyleElement()
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_readStyleElement: updating " << (_updating?"true":"false")<< std::endl;
+#endif
+
+ if (_updating) return; // Don't read if we wrote style element.
+ _updating = true;
+ _store->clear();
+
+ Inkscape::XML::Node * textNode = _getStyleTextNode();
+ if (textNode == NULL) {
+ std::cerr << "StyleDialog::_readStyleElement: No text node!" << std::endl;
+ }
+
+ // Get content from style text node.
+ std::string content = (textNode->content() ? textNode->content() : "");
+
+ // Remove end-of-lines (check it works on Windoze).
+ content.erase(std::remove(content.begin(), content.end(), '\n'), content.end());
+
+ // First split into selector/value chunks.
+ // An attempt to use Glib::Regex failed. A C++11 version worked but
+ // reportedly has problems on Windows. Using split_simple() is simpler
+ // and probably faster.
+ //
+ // Glib::RefPtr<Glib::Regex> regex1 =
+ // Glib::Regex::create("([^\\{]+)\\{([^\\{]+)\\}");
+ //
+ // Glib::MatchInfo minfo;
+ // regex1->match(content, minfo);
+
+ // Split on curly brackets. Even tokens are selectors, odd are values.
+ std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("[}{]", content);
+
+ // If text node is empty, return (avoids problem with negative below).
+ if (tokens.size() == 0) {
+ _updating = false;
+ return;
+ }
+
+ for (unsigned i = 0; i < tokens.size()-1; i += 2) {
+
+ Glib::ustring selector = tokens[i];
+ REMOVE_SPACES(selector); // Remove leading/trailing spaces
+
+ // Get list of objects selector matches
+ std::vector<SPObject *> objVec = _getObjVec( selector );
+
+ Glib::ustring properties;
+ // Check to make sure we do have a value to match selector.
+ if ((i+1) < tokens.size()) {
+ properties = tokens[i+1];
+ } else {
+ std::cerr << "StyleDialog::_readStyleElement: Missing values "
+ "for last selector!" << std::endl;
+ }
+ REMOVE_SPACES(properties);
+
+ Gtk::TreeModel::Row row = *(_store->append());
+ row[_mColumns._colSelector] = selector;
+ row[_mColumns._colIsSelector] = true;
+ row[_mColumns._colObj] = objVec;
+ row[_mColumns._colProperties] = properties;
+
+ // Add as children, objects that match selector.
+ for (auto& obj: objVec) {
+ Gtk::TreeModel::Row childrow = *(_store->append(row->children()));
+ childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId());
+ childrow[_mColumns._colIsSelector] = false;
+ childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj);
+ childrow[_mColumns._colProperties] = ""; // Unused
+ }
+ }
+ _updating = false;
+}
+
+
+/**
+ * @brief StyleDialog::_writeStyleElement
+ * Update the content of the style element as selectors (or objects) are added/removed.
+ */
+void StyleDialog::_writeStyleElement()
+{
+ _updating = true;
+
+ Glib::ustring styleContent;
+ for (auto& row: _store->children()) {
+ styleContent = styleContent + row[_mColumns._colSelector] +
+ " { " + row[_mColumns._colProperties] + " }\n";
+ }
+ // We could test if styleContent is empty and then delete the style node here but there is no
+ // harm in keeping it around ...
+
+ Inkscape::XML::Node *textNode = _getStyleTextNode();
+ textNode->setContent(styleContent.c_str());
+
+ DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_STYLE, _("Edited style element."));
+
+ _updating = false;
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_writeStyleElement(): |" << styleContent << "|" << std::endl;
+#endif
+}
+
+
+/**
+ * @brief StyleDialog::_addToSelector
+ * @param row
+ * Add selected objects on the desktop to the selector corresponding to 'row'.
+ */
+void StyleDialog::_addToSelector(Gtk::TreeModel::Row row)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_addToSelector: Entrance" << std::endl;
+#endif
+ if (*row) {
+
+ Glib::ustring selector = row[_mColumns._colSelector];
+
+ if (selector[0] == '#') {
+ // 'id' selector... add selected object's id's to list.
+ Inkscape::Selection* selection = getDesktop()->getSelection();
+ for (auto& obj: selection->objects()) {
+
+ Glib::ustring id = (obj->getId()?obj->getId():"");
+
+ std::vector<SPObject *> objVec = row[_mColumns._colObj];
+ bool found = false;
+ for (auto& obj: objVec) {
+ if (id == obj->getId()) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // Update row
+ objVec.push_back(obj); // Adding to copy so need to update tree
+ row[_mColumns._colObj] = objVec;
+ row[_mColumns._colSelector] = _getIdList( objVec );
+
+ // Add child row
+ Gtk::TreeModel::Row childrow = *(_store->append(row->children()));
+ childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId());
+ childrow[_mColumns._colIsSelector] = false;
+ childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj);
+ childrow[_mColumns._colProperties] = ""; // Unused
+ }
+ }
+ }
+
+ else if (selector[0] == '.') {
+ // 'class' selector... add value to class attribute of selected objects.
+
+ // Get first class (split on white space or comma)
+ std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("[,\\s]+", selector);
+ Glib::ustring className = tokens[0];
+ className.erase(0,1);
+
+ // Get list of objects to modify
+ Inkscape::Selection* selection = getDesktop()->getSelection();
+ std::vector<SPObject *> objVec( selection->objects().begin(),
+ selection->objects().end() );
+
+ _insertClass( objVec, className );
+
+ row[_mColumns._colObj] = _getObjVec( selector );
+
+ for (auto& obj: objVec) {
+ // Add child row
+ Gtk::TreeModel::Row childrow = *(_store->append(row->children()));
+ childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId());
+ childrow[_mColumns._colIsSelector] = false;
+ childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj);
+ childrow[_mColumns._colProperties] = ""; // Unused
+ }
+ }
+
+ else {
+ // Do nothing for element selectors.
+ // std::cout << " Element selector... doing nothing!" << std::endl;
+ }
+ }
+
+ _writeStyleElement();
+}
+
+
+/**
+ * @brief StyleDialog::_removeFromSelector
+ * @param row
+ * Remove the object corresponding to 'row' from the parent selector.
+ */
+void StyleDialog::_removeFromSelector(Gtk::TreeModel::Row row)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_removeFromSelector: Entrance" << std::endl;
+#endif
+ if (*row) {
+
+ Glib::ustring objectLabel = row[_mColumns._colSelector];
+ Gtk::TreeModel::iterator iter = row->parent();
+ if (iter) {
+ Gtk::TreeModel::Row parent = *iter;
+ Glib::ustring selector = parent[_mColumns._colSelector];
+
+ if (selector[0] == '#') {
+ // 'id' selector... remove selected object's id's to list.
+
+ // Erase from selector label.
+ auto i = selector.find(objectLabel);
+ if (i != Glib::ustring::npos) {
+ selector.erase(i, objectLabel.length());
+ }
+ // Erase any comma/space
+ if (i != Glib::ustring::npos && selector[i] == ',') {
+ selector.erase(i, 1);
+ }
+ if (i != Glib::ustring::npos && selector[i] == ' ') {
+ selector.erase(i, 1);
+ }
+
+ // Update store
+ if (selector.empty()) {
+ _store->erase(parent);
+ } else {
+ // Save new selector and update object vector.
+ parent[_mColumns._colSelector] = selector;
+ parent[_mColumns._colObj] = _getObjVec( selector );
+ _store->erase(row);
+ }
+ }
+
+ else if (selector[0] == '.') {
+ // 'class' selector... remove value to class attribute of selected objects.
+
+ std::vector<SPObject *> objVec = row[_mColumns._colObj]; // Just one
+
+ // Get first class (split on white space or comma)
+ std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("[,\\s]+", selector);
+ Glib::ustring className = tokens[0];
+ className.erase(0,1); // Erase '.'
+
+ // Erase class name from 'class' attribute.
+ Glib::ustring classAttr = objVec[0]->getRepr()->attribute("class");
+ auto i = classAttr.find( className );
+ if (i != Glib::ustring::npos) {
+ classAttr.erase(i, className.length());
+ }
+ if (i != Glib::ustring::npos && classAttr[i] == ' ') {
+ classAttr.erase(i, 1);
+ }
+ objVec[0]->getRepr()->setAttribute("class", classAttr);
+
+ parent[_mColumns._colObj] = _getObjVec( selector );
+ _store->erase(row);
+ }
+
+ else {
+ // Do nothing for element selectors.
+ // std::cout << " Element selector... doing nothing!" << std::endl;
+ }
+ }
+ }
+
+ _writeStyleElement();
+
+}
+
+
+/**
+ * @brief StyleDialog::_getIdList
+ * @param sel
+ * @return This function returns a comma seperated list of ids for objects in input vector.
+ * It is used in creating an 'id' selector. It relies on objects having 'id's.
+ */
+Glib::ustring StyleDialog::_getIdList(std::vector<SPObject*> sel)
+{
+ Glib::ustring str;
+ for (auto& obj: sel) {
+ str += "#" + Glib::ustring(obj->getId()) + ", ";
+ }
+ if (!str.empty()) {
+ str.erase(str.size()-1); // Remove space at end. c++11 has pop_back() but not ustring.
+ str.erase(str.size()-1); // Remove comma at end.
+ }
+ return str;
+}
+
+/**
+ * @brief StyleDialog::_getObjVec
+ * @param selector: a valid CSS selector string.
+ * @return objVec: a vector of pointers to SPObject's the selector matches.
+ * Return a vector of all objects that selector matches.
+ */
+std::vector<SPObject *> StyleDialog::_getObjVec(Glib::ustring selector) {
+
+ std::vector<SPObject *> objVec = SP_ACTIVE_DOCUMENT->getObjectsBySelector( selector );
+
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_getObjVec: |" << selector << "|" << std::endl;
+ for (auto& obj: objVec) {
+ std::cout << " " << (obj->getId()?obj->getId():"null") << std::endl;
+ }
+#endif
+
+ return objVec;
+}
+
+
+/**
+ * @brief StyleDialog::_insertClass
+ * @param objs: list of objects to insert class
+ * @param class: class to insert
+ * Insert a class name into objects' 'class' attribute.
+ */
+void StyleDialog::_insertClass(const std::vector<SPObject *>& objVec, const Glib::ustring& className) {
+
+ for (auto& obj: objVec) {
+
+ if (!obj->getRepr()->attribute("class")) {
+ // 'class' attribute does not exist, create it.
+ obj->getRepr()->setAttribute("class", className);
+ } else {
+ // 'class' attribute exists, append.
+ Glib::ustring classAttr = obj->getRepr()->attribute("class");
+
+ // Split on white space.
+ std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("\\s+", classAttr);
+ bool add = true;
+ for (auto& token: tokens) {
+ if (token == className) {
+ add = false; // Might be useful to still add...
+ break;
+ }
+ }
+ if (add) {
+ obj->getRepr()->setAttribute("class", classAttr + " " + className );
+ }
+ }
+ }
+ }
+
+
+/**
+ * @brief StyleDialog::_selectObjects
+ * @param eventX
+ * @param eventY
+ * This function selects objects in the drawing corresponding to the selector
+ * selected in the treeview.
+ */
+void StyleDialog::_selectObjects(int eventX, int eventY)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_selectObjects: " << eventX << ", " << eventY << std::endl;
+#endif
+
+ getDesktop()->selection->clear();
+ Gtk::TreeViewColumn *col = _treeView.get_column(1);
+ Gtk::TreeModel::Path path;
+ int x2 = 0;
+ int y2 = 0;
+ // To do: We should be able to do this via passing in row.
+ if (_treeView.get_path_at_pos(eventX, eventY, path, col, x2, y2)) {
+ if (col == _treeView.get_column(1)) {
+ Gtk::TreeModel::iterator iter = _store->get_iter(path);
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ Gtk::TreeModel::Children children = row.children();
+ std::vector<SPObject *> objVec = row[_mColumns._colObj];
+ for (unsigned i = 0; i < objVec.size(); ++i) {
+ SPObject *obj = objVec[i];
+ getDesktop()->selection->add(obj);
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * @brief StyleDialog::_addSelector
+ *
+ * This function opens a dialog to add a selector. The dialog is prefilled
+ * with an 'id' selector containing a list of the id's of selected objects
+ * or with a 'class' selector if no objects are selected.
+ */
+void StyleDialog::_addSelector()
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_addSelector: Entrance" << std::endl;
+#endif
+
+ // Store list of selected elements on desktop (not to be confused with selector).
+ Inkscape::Selection* selection = getDesktop()->getSelection();
+ std::vector<SPObject *> objVec( selection->objects().begin(),
+ selection->objects().end() );
+
+ // ==== Create popup dialog ====
+ Gtk::Dialog *textDialogPtr = new Gtk::Dialog();
+ textDialogPtr->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL);
+ textDialogPtr->add_button(_("Add"), Gtk::RESPONSE_OK);
+
+ Gtk::Entry *textEditPtr = manage ( new Gtk::Entry() );
+ textDialogPtr->get_vbox()->pack_start(*textEditPtr, Gtk::PACK_SHRINK);
+
+ Gtk::Label *textLabelPtr = manage ( new Gtk::Label(
+ _("Invalid entry: Not an id (#), class (.), or element CSS selector.")
+ ) );
+ textDialogPtr->get_vbox()->pack_start(*textLabelPtr, Gtk::PACK_SHRINK);
+
+ /**
+ * By default, the entrybox contains 'Class1' as text. However, if object(s)
+ * is(are) selected and user clicks '+' at the bottom of dialog, the
+ * entrybox will have the id(s) of the selected objects as text.
+ */
+ if (getDesktop()->getSelection()->isEmpty()) {
+ textEditPtr->set_text(".Class1");
+ } else {
+ textEditPtr->set_text(_getIdList(objVec));
+ }
+
+ Gtk::Requisition sreq1, sreq2;
+ textDialogPtr->get_preferred_size(sreq1, sreq2);
+ int minWidth = 200;
+ int minHeight = 100;
+ minWidth = (sreq2.width > minWidth ? sreq2.width : minWidth );
+ minHeight = (sreq2.height > minHeight ? sreq2.height : minHeight);
+ textDialogPtr->set_size_request(minWidth, minHeight);
+ textEditPtr->show();
+ textLabelPtr->hide();
+ textDialogPtr->show();
+
+
+ // ==== Get response ====
+ int result = -1;
+ bool invalid = true;
+ Glib::ustring selectorValue;
+
+ while (invalid) {
+ result = textDialogPtr->run();
+ if (result != Gtk::RESPONSE_OK) { // Cancel, close dialog, etc.
+ textDialogPtr->hide();
+ return;
+ }
+ /**
+ * @brief selectorName
+ * This string stores selector name. The text from entrybox is saved as name
+ * for selector. If the entrybox is empty, the text (thus selectorName) is
+ * set to ".Class1"
+ */
+ selectorValue = textEditPtr->get_text();
+ Glib::ustring firstWord = selectorValue.substr(0, selectorValue.find(" "));
+
+ del->set_sensitive(true);
+
+ if (selectorValue[0] == '.' ||
+ selectorValue[0] == '#' ||
+ selectorValue[0] == '*' ||
+ SPAttributeRelSVG::isSVGElement( firstWord ) ) {
+ invalid = false;
+ } else {
+ textLabelPtr->show();
+ }
+ }
+ delete textDialogPtr;
+
+ // ==== Handle response ====
+
+ // If class selector, add selector name to class attribute for each object
+ if (selectorValue[0] == '.') {
+
+ Glib::ustring className = selectorValue;
+ className.erase(0,1);
+ _insertClass(objVec, className);
+ }
+
+ // Generate a new object vector (we could have an element selector,
+ // the user could have edited the id selector list, etc.).
+ objVec = _getObjVec( selectorValue );
+
+ // Add entry to GUI tree
+ Gtk::TreeModel::Row row = *(_store->append());
+ row[_mColumns._colSelector] = selectorValue;
+ row[_mColumns._colIsSelector] = true;
+ row[_mColumns._colObj] = objVec;
+
+ // Add as children objects that match selector.
+ for (auto& obj: objVec) {
+ Gtk::TreeModel::Row childrow = *(_store->append(row->children()));
+ childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId());
+ childrow[_mColumns._colIsSelector] = false;
+ childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj);
+ }
+
+ // Add entry to style element
+ _writeStyleElement();
+}
+
+/**
+ * @brief StyleDialog::_delSelector
+ * This function deletes selector when '-' at the bottom is clicked.
+ * Note: If deleting a class selector, class attributes are NOT changed.
+ */
+void StyleDialog::_delSelector()
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_delSelector" << std::endl;
+#endif
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ _updating = true;
+ _store->erase(iter);
+ _updating = false;
+ _writeStyleElement();
+ }
+}
+
+/**
+ * @brief StyleDialog::_handleButtonEvent
+ * @param event
+ * @return
+ * Handles the event when '+' button in front of a selector name is clicked or when a '-' button in
+ * front of a child object is clicked. In the first case, the selected objects on the desktop (if
+ * any) are added as children of the selector in the treeview. In the latter case, the object
+ * corresponding to the row is removed from the selector.
+ */
+bool StyleDialog::_handleButtonEvent(GdkEventButton *event)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_handleButtonEvent: Entrance" << std::endl;
+#endif
+ if (event->type == GDK_BUTTON_RELEASE && event->button == 1) {
+ Gtk::TreeViewColumn *col = 0;
+ Gtk::TreeModel::Path path;
+ int x = static_cast<int>(event->x);
+ int y = static_cast<int>(event->y);
+ int x2 = 0;
+ int y2 = 0;
+ if (_treeView.get_path_at_pos(x, y, path, col, x2, y2)) {
+ if (col == _treeView.get_column(0)) {
+ Gtk::TreeModel::iterator iter = _store->get_iter(path);
+ Gtk::TreeModel::Row row = *iter;
+
+ // Add or remove objects from a
+ if (!row.parent()) {
+ // Add selected objects to selector.
+ _addToSelector(row);
+ } else {
+ // Remove object from selector
+ _removeFromSelector(row);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+// -------------------------------------------------------------------
+
+class PropertyData
+{
+public:
+ PropertyData() {};
+ PropertyData(Glib::ustring name) : _name(name) {};
+
+ void _setSheetValue(Glib::ustring value) { _sheetValue = value; };
+ void _setAttrValue(Glib::ustring value) { _attrValue = value; };
+ Glib::ustring _getName() { return _name; };
+ Glib::ustring _getSheetValue() { return _sheetValue; };
+ Glib::ustring _getAttrValue() { return _attrValue; };
+
+private:
+ Glib::ustring _name;
+ Glib::ustring _sheetValue;
+ Glib::ustring _attrValue;
+};
+
+// -------------------------------------------------------------------
+
+
+/**
+ * @brief StyleDialog::_updateCSSPanel
+ * Updates CSS panel according to row in Style panel.
+ */
+void StyleDialog::_updateCSSPanel()
+{
+ // This should probably be in a member function of CSSDialog.
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_updateCSSPanel" << std::endl;
+#endif
+ _updating = true;
+
+ _cssPane->_store->clear();
+
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ Glib::ustring properties;
+ Glib::ustring sheet;
+ Glib::ustring attr;
+ if (row[_mColumns._colIsSelector]) {
+ _cssPane->_propRenderer->property_editable() = true;
+ _cssPane->_sheetRenderer->property_editable() = true;
+ _cssPane->_sheetRenderer->property_foreground_rgba() = Gdk::RGBA("black");
+ _cssPane->_attrRenderer->property_editable() = false;
+ _cssPane->_buttonAddProperty.set_sensitive(true);
+
+ properties = row[_mColumns._colProperties];
+ sheet = row[_mColumns._colProperties];
+ _objObserver.set( NULL );
+ } else {
+ _cssPane->_propRenderer->property_editable() = false;
+ _cssPane->_sheetRenderer->property_editable() = false;
+ _cssPane->_sheetRenderer->property_foreground_rgba() = Gdk::RGBA("gray");
+ _cssPane->_attrRenderer->property_editable() = false; // false for now...
+ _cssPane->_buttonAddProperty.set_sensitive(false);
+
+ std::vector<SPObject *> objects = row[_mColumns._colObj];
+ Gtk::TreeModel::iterator piter = row.parent();
+ if (piter) {
+ Gtk::TreeModel::Row prow = *piter;
+ sheet = prow[_mColumns._colProperties];
+ }
+ _objObserver.set( objects[0] );
+ if (objects[0] && objects[0]->getAttribute("style") != NULL) {
+ properties = objects[0]->getAttribute("style");
+ attr = objects[0]->getAttribute("style");
+ }
+ }
+ REMOVE_SPACES(properties); // Remove leading/trailing spaces
+
+ std::map<Glib::ustring, PropertyData> propMap;
+
+ std::vector<Glib::ustring> sheetList = Glib::Regex::split_simple("\\s*;\\s*", sheet);
+ for (auto& token: sheetList) {
+
+ if (token.empty()) break;
+
+ std::vector<Glib::ustring> pair =
+ Glib::Regex::split_simple("\\s*:\\s*", token);
+ if( pair.size() > 1) {
+ PropertyData temp( pair[0] );
+ temp._setSheetValue( pair[1] );
+ propMap[pair[0]] = temp;
+ }
+ }
+
+ std::vector<Glib::ustring> attrList = Glib::Regex::split_simple("\\s*;\\s*", attr);
+ for (auto& token: attrList) {
+
+ if (token.empty()) break;
+
+ std::vector<Glib::ustring> pair =
+ Glib::Regex::split_simple("\\s*:\\s*", token);
+
+ if( pair.size() > 1) {
+ auto it = propMap.find(pair[0]);
+ if (it != propMap.end()) {
+ (*it).second._setAttrValue( pair[1] );
+ } else {
+ PropertyData temp(pair[0]);
+ temp._setAttrValue( pair[1] );
+ propMap[pair[0]] = temp;
+ }
+ }
+ }
+
+ for (auto it : propMap) {
+ // std::cout << " " << it.first
+ // << " " << it.second._getName()
+ // << " " << it.second._getSheetValue()
+ // << " " << it.second._getAttrValue()
+ // << std::endl;
+ _cssPane->_propRow = *(_cssPane->_store->append());
+ _cssPane->_propRow[_cssPane->_cssColumns._colUnsetProp] = false;
+ _cssPane->_propRow[_cssPane->_cssColumns._propertyLabel] = it.second._getName();
+ _cssPane->_propRow[_cssPane->_cssColumns._styleSheetVal] = it.second._getSheetValue();
+ _cssPane->_propRow[_cssPane->_cssColumns._styleAttrVal ] = it.second._getAttrValue();
+ }
+ }
+
+ _updating = false;
+}
+
+
+/**
+ * Handle document replaced. (Happens when a default document is immediately replaced by another
+ * document in a new window.)
+ */
+void
+StyleDialog::_handleDocumentReplaced(SPDesktop *desktop, SPDocument * /* document */)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::handleDocumentReplaced()" << std::endl;
+#endif
+
+ _selection_changed_connection.disconnect();
+
+ _selection_changed_connection = desktop->getSelection()->connectChanged(
+ sigc::hide(sigc::mem_fun(this, &StyleDialog::_handleSelectionChanged)));
+
+ _readStyleElement();
+ _selectRow();
+}
+
+
+/*
+ * When a dialog is floating, it is connected to the active desktop.
+ */
+void
+StyleDialog::_handleDesktopChanged(SPDesktop* desktop) {
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::handleDesktopReplaced()" << std::endl;
+#endif
+
+ if (getDesktop() == desktop) {
+ // This will happen after construction of dialog. We've already
+ // set up signals so just return.
+ return;
+ }
+
+ _selection_changed_connection.disconnect();
+ _document_replaced_connection.disconnect();
+
+ setDesktop( desktop );
+
+ _selection_changed_connection = desktop->getSelection()->connectChanged(
+ sigc::hide(sigc::mem_fun(this, &StyleDialog::_handleSelectionChanged)));
+ _document_replaced_connection = desktop->connectDocumentReplaced(
+ sigc::mem_fun(this, &StyleDialog::_handleDocumentReplaced));
+
+ _readStyleElement();
+ _selectRow();
+}
+
+
+/*
+ * Handle a change in which objects are selected in a document.
+ */
+void
+StyleDialog::_handleSelectionChanged() {
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_handleSelectionChanged()" << std::endl;
+#endif
+
+ _selectRow();
+}
+
+
+/**
+ * @brief StyleDialog::_buttonEventsSelectObjs
+ * @param event
+ * This function detects single or double click on a selector in any row. Clicking
+ * on a selector selects the matching objects on the desktop. A double click will
+ * in addition open the CSS dialog.
+ */
+void StyleDialog::_buttonEventsSelectObjs(GdkEventButton* event )
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_buttonEventsSelectObjs" << std::endl;
+#endif
+
+ _updating = true;
+
+ if (event->type == GDK_BUTTON_RELEASE && event->button == 1) {
+ int x = static_cast<int>(event->x);
+ int y = static_cast<int>(event->y);
+ _selectObjects(x, y);
+ //}
+ //else if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
+ //int x = static_cast<int>(event->x);
+ //int y = static_cast<int>(event->y);
+ //_selectObjects(x, y);
+
+ _updateCSSPanel();
+ }
+ _updating = false;
+}
+
+
+/**
+ * @brief StyleDialog::_selectRow
+ * This function selects the row in treeview corresponding to an object selected
+ * in the drawing. If more than one row matches, the first is chosen.
+ */
+void StyleDialog::_selectRow()
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_selectRow: updating: " << (_updating?"true":"false") << std::endl;
+#endif
+ if (_updating || !getDesktop()) return; // Avoid updating if we have set row via dialog.
+
+ if (SP_ACTIVE_DESKTOP != getDesktop()) {
+ std::cerr << "StyleDialog::_selectRow: SP_ACTIVE_DESKTOP != getDesktop()" << std::endl;
+ return;
+ }
+
+ Inkscape::Selection* selection = getDesktop()->getSelection();
+ if (!selection->isEmpty()) {
+ SPObject *obj = selection->objects().back();
+
+ Gtk::TreeModel::Children children = _store->children();
+ for(Gtk::TreeModel::Children::iterator iter = children.begin();
+ iter != children.end(); ++iter) {
+
+ Gtk::TreeModel::Row row = *iter;
+ std::vector<SPObject *> objVec = row[_mColumns._colObj];
+ for (unsigned i = 0; i < objVec.size(); ++i) {
+ if (obj->getId() == objVec[i]->getId()) {
+ _treeView.get_selection()->select(row);
+ _updateCSSPanel();
+ return;
+ }
+ }
+ }
+ }
+
+ // Selection empty or no row matches.
+ _treeView.get_selection()->unselect_all();
+ _updateCSSPanel();
+}
+
+
+void StyleDialog::_objChanged() {
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_objChanged" << std::endl;
+#endif
+ if (_updating) return;
+ _updateCSSPanel();
+}
+
+
+/**
+ * @brief StyleDialog::_handleProp
+ * @param path
+ * @param new_text
+ * Called when new text is entered into a "prop" cell..
+ */
+void StyleDialog::_handleProp(const Glib::ustring& path, const Glib::ustring& new_text)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_handleProp: path: " << path
+ << " new_text: " << new_text << std::endl;
+#endif
+
+ Gtk::TreeModel::iterator iterCss = _cssPane->_treeView.get_model()->get_iter(path);
+ if (iterCss) {
+ Gtk::TreeModel::Row row = *iterCss;
+ row[_cssPane->_cssColumns._propertyLabel] = new_text;
+ }
+
+ // To do: validate.
+}
+
+/**
+ * @brief StyleDialog::_handleSheet
+ * @param path
+ * @param new_text
+ * Called when new text is entered into a "sheet" cell..
+ */
+void StyleDialog::_handleSheet(const Glib::ustring& path, const Glib::ustring& new_text)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_handleSheet: path: " << path
+ << " new_text: " << new_text << std::endl;
+#endif
+
+ Gtk::TreeModel::iterator iterCss = _cssPane->_treeView.get_model()->get_iter(path);
+ if (iterCss) {
+ Gtk::TreeModel::Row row = *iterCss;
+ row[_cssPane->_cssColumns._styleSheetVal] = new_text;
+ }
+
+ // To do: validate (run through style.read()/style.write()?).
+
+ Glib::ustring properties;
+ for (auto& crow: _cssPane->_store->children()) {
+ properties = properties +
+ crow[_cssPane->_cssColumns._propertyLabel] + ": " +
+ crow[_cssPane->_cssColumns._styleSheetVal] + "; ";
+ }
+
+ // Update selector data.
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ row[_mColumns._colProperties] = properties;
+ _writeStyleElement();
+ }
+}
+
+/**
+ * @brief StyleDialog::_handleAttr
+ * @param path
+ * @param new_text
+ * Called when new text is entered into an "attr" cell..
+ */
+void StyleDialog::_handleAttr(const Glib::ustring& path, const Glib::ustring& new_text)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_handleAttr: path: " << path
+ << " new_text: " << new_text << std::endl;
+#endif
+
+ Gtk::TreeModel::iterator iterCss = _cssPane->_treeView.get_model()->get_iter(path);
+ if (iterCss) {
+ Gtk::TreeModel::Row row = *iterCss;
+ row[_cssPane->_cssColumns._styleAttrVal] = new_text;
+ }
+
+ // To do: validate (run through style.read()/style.write()?).
+
+ Glib::ustring properties;
+ for (auto& crow: _cssPane->_store->children()) {
+ properties = properties +
+ crow[_cssPane->_cssColumns._propertyLabel] + ": ";
+ crow[_cssPane->_cssColumns._styleAttrVal] + "; ";
+ }
+
+ std::cout << "StyleDialog::_handlerAttr(): Unimplemented write." << std::endl;
+}
+
+/**
+ * @brief StyleDialog::_delProperty
+ * @param event
+ * @return
+ * Delete a property from the CSS dialog and then update trees.
+ */
+bool StyleDialog::_delProperty(GdkEventButton *event)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_delProperty" << std::endl;
+#endif
+
+ if (event->type == GDK_BUTTON_RELEASE && event->button == 1) {
+ Gtk::TreeViewColumn *col = 0;
+ Gtk::TreeModel::Path path;
+ int x = static_cast<int>(event->x);
+ int y = static_cast<int>(event->y);
+ int x2 = 0;
+ int y2 = 0;
+ Gtk::TreeModel::Row cssRow;
+ Glib::ustring toDelProperty;
+ if (_cssPane->_treeView.get_path_at_pos(x, y, path, col, x2, y2)) {
+ if (col == _cssPane->_treeView.get_column(0)) {
+ Gtk::TreeModel::iterator cssIter =
+ _cssPane->_treeView.get_selection()->get_selected();
+ if (cssIter) {
+
+ Gtk::TreeModel::Row cssRow = *cssIter;
+
+ // Update selector data.
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+
+ Gtk::TreeModel::Row row = *iter;
+
+ if ( row[_mColumns._colIsSelector]) {
+
+ // We only care about style sheet for selectors so erase row in CSS pane.
+ _cssPane->_store->erase(cssIter);
+
+ // Update style sheet
+ Glib::ustring properties;
+ for (auto& crow: _cssPane->_store->children()) {
+ Glib::ustring sheetVal = crow[_cssPane->_cssColumns._styleSheetVal];
+ if (!sheetVal.empty()) {
+ properties = properties +
+ crow[_cssPane->_cssColumns._propertyLabel] + ": " +
+ crow[_cssPane->_cssColumns._styleSheetVal] + "; ";
+ }
+ }
+
+ row[_mColumns._colProperties] = properties;
+ _writeStyleElement();
+
+ } else {
+
+ // We only erase row if style sheet does not contain deleted property.
+ // Otherwise we set style attr value to empty string.
+ Gtk::TreeModel::Row cssRow = *cssIter;
+ Glib::ustring val = cssRow[_cssPane->_cssColumns._styleSheetVal];
+ if (val.empty()) {
+ _cssPane->_store->erase(cssIter);
+ } else {
+ cssRow[_cssPane->_cssColumns._styleAttrVal] = Glib::ustring();
+ }
+
+ // Update style attribute
+ std::vector<SPObject *> objects = row[_mColumns._colObj];
+ Glib::ustring properties;
+ for (auto& crow: _cssPane->_store->children()) {
+ Glib::ustring attrVal = crow[_cssPane->_cssColumns._styleAttrVal];
+ if (!attrVal.empty()) {
+ properties = properties +
+ crow[_cssPane->_cssColumns._propertyLabel] + ": " +
+ crow[_cssPane->_cssColumns._styleAttrVal] + "; ";
+ }
+ }
+
+ if (objects[0]) {
+ if (properties.empty()) {
+ objects[0]->setAttribute("style", NULL);
+ } else {
+ objects[0]->setAttribute("style", properties);
+ }
+ DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_STYLE,
+ _("Deleted property from style attribute."));
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+/**
+ * @brief StyleDialog::_styleButton
+ * @param btn
+ * @param iconName
+ * @param tooltip
+ * Set the style of '+' and '-' buttons at the bottom of dialog.
+ */
+void StyleDialog::_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(*manage(Glib::wrap(child)));
+ btn.set_relief(Gtk::RELIEF_NONE);
+ btn.set_tooltip_text (tooltip);
+}
+
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/ui/dialog/styledialog.h b/src/ui/dialog/styledialog.h
new file mode 100644
index 000000000..e84489e66
--- /dev/null
+++ b/src/ui/dialog/styledialog.h
@@ -0,0 +1,175 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ * Tavmjong Bah
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef STYLEDIALOG_H
+#define STYLEDIALOG_H
+
+#include <ui/widget/panel.h>
+#include <gtkmm/treeview.h>
+#include <gtkmm/treestore.h>
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/dialog.h>
+#include <gtkmm/treeselection.h>
+#include <gtkmm/paned.h>
+
+#include "ui/dialog/desktop-tracker.h"
+#include "ui/dialog/cssdialog.h"
+
+#include "xml/helper-observer.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * @brief The StyleDialog class
+ * A list of CSS selectors will show up in this dialog. This dialog allows one to
+ * add and delete selectors. Elements can be added to and removed from the selectors
+ * in the dialog. Selection of any selector row selects the matching objects in
+ * the drawing and vice-versa. (Only simple selectors supported for now.)
+ *
+ * This class must keep two things in sync:
+ * 1. The text node of the style element.
+ * 2. The Gtk::TreeModel.
+ */
+class StyleDialog : public Widget::Panel {
+
+public:
+ ~StyleDialog();
+
+ static StyleDialog &getInstance() { return *new StyleDialog(); }
+
+private:
+ // No default constructor, noncopyable, nonassignable
+ StyleDialog();
+ StyleDialog(StyleDialog const &d);
+ StyleDialog operator=(StyleDialog const &d);
+
+ // Monitor <style> element for changes.
+ class NodeObserver;
+
+ // Data structure
+ class ModelColumns : public Gtk::TreeModel::ColumnRecord {
+ public:
+ ModelColumns() {
+ add(_colSelector);
+ add(_colIsSelector);
+ add(_colObj);
+ add(_colProperties);
+ }
+ Gtk::TreeModelColumn<Glib::ustring> _colSelector; // Selector or matching object id.
+ Gtk::TreeModelColumn<bool> _colIsSelector; // Selector row or child object row.
+ Gtk::TreeModelColumn<std::vector<SPObject *> > _colObj; // List of matching objects.
+ Gtk::TreeModelColumn<Glib::ustring> _colProperties; // List of properties.
+ };
+ ModelColumns _mColumns;
+
+ // Override Gtk::TreeStore to control drag-n-drop (only allow dragging and dropping of selectors).
+ // See: https://developer.gnome.org/gtkmm-tutorial/stable/sec-treeview-examples.html.en
+ //
+ // TreeStore implements simple drag and drop (DND) but there appears no way to know when a DND
+ // has been completed (other than doing the whole DND ourselves). As a hack, we use
+ // on_row_deleted to trigger write of style element.
+ class TreeStore : public Gtk::TreeStore {
+ protected:
+ TreeStore();
+ bool row_draggable_vfunc(const Gtk::TreeModel::Path& path) const override;
+ bool row_drop_possible_vfunc(const Gtk::TreeModel::Path& path,
+ const Gtk::SelectionData& selection_data) const override;
+ void on_row_deleted(const TreeModel::Path& path) override;
+
+ public:
+ static Glib::RefPtr<StyleDialog::TreeStore> create(StyleDialog *styledialog);
+
+ private:
+ StyleDialog *_styledialog;
+ };
+
+ // TreeView
+ Gtk::TreeView _treeView;
+ Glib::RefPtr<TreeStore> _store;
+
+ // Widgets
+ Gtk::VPaned _paned;
+ Gtk::VBox _mainBox;
+ Gtk::HBox _buttonBox;
+ Gtk::ScrolledWindow _scrolledWindow;
+ Gtk::Button* del;
+ Gtk::Button* create;
+ CssDialog *_cssPane;
+
+ // Reading and writing the style element.
+ Inkscape::XML::Node *_getStyleTextNode();
+ void _readStyleElement();
+ void _writeStyleElement();
+
+ // Manipulate Tree
+ void _addToSelector(Gtk::TreeModel::Row row);
+ void _removeFromSelector(Gtk::TreeModel::Row row);
+ Glib::ustring _getIdList(std::vector<SPObject *>);
+ std::vector<SPObject *> _getObjVec(Glib::ustring selector);
+ void _insertClass(const std::vector<SPObject *>& objVec, const Glib::ustring& className);
+ void _selectObjects(int, int);
+ void _updateCSSPanel();
+
+ // Variables
+ bool _updating; // Prevent cyclic actions: read <-> write, select via dialog <-> via desktop
+ Inkscape::XML::Node *_textNode; // Track so we know when to add a NodeObserver.
+
+ // Signals and handlers - External
+ sigc::connection _document_replaced_connection;
+ sigc::connection _desktop_changed_connection;
+ sigc::connection _selection_changed_connection;
+
+ void _handleDocumentReplaced(SPDesktop* desktop, SPDocument *document);
+ void _handleDesktopChanged(SPDesktop* desktop);
+ void _handleSelectionChanged();
+
+ DesktopTracker _desktopTracker;
+
+ Inkscape::XML::SignalObserver _objObserver; // Track object in selected row (for style change).
+
+ // Signal and handlers - Internal
+ void _addSelector();
+ void _delSelector();
+ bool _handleButtonEvent(GdkEventButton *event);
+ void _buttonEventsSelectObjs(GdkEventButton *event);
+ void _selectRow(); // Select row in tree when selection changed.
+ void _objChanged();
+
+ // Signal handlers for CssDialog
+ void _handleProp( const Glib::ustring& path, const Glib::ustring& new_text);
+ void _handleSheet(const Glib::ustring& path, const Glib::ustring& new_text);
+ void _handleAttr( const Glib::ustring& path, const Glib::ustring& new_text);
+ bool _delProperty(GdkEventButton *event);
+
+ // GUI
+ void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip);
+};
+
+} // namespace Dialogc
+} // namespace UI
+} // namespace Inkscape
+
+#endif // STYLEDIALOG_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/symbols.cpp b/src/ui/dialog/symbols.cpp
index 4840b897b..740ff4f39 100644
--- a/src/ui/dialog/symbols.cpp
+++ b/src/ui/dialog/symbols.cpp
@@ -25,6 +25,7 @@
#include <gtkmm/comboboxtext.h>
#include <gtkmm/iconview.h>
#include <gtkmm/liststore.h>
+#include <glibmm/regex.h>
#include <glibmm/stringutils.h>
#include <glibmm/markup.h>
#include <glibmm/i18n.h>
@@ -127,7 +128,7 @@ SymbolsDialog::SymbolsDialog( gchar const* prefsPath ) :
sigc::connection connSet = symbolSet->signal_changed().connect(
sigc::mem_fun(*this, &SymbolsDialog::rebuild));
instanceConns.push_back(connSet);
-
+
++row;
/********************* Icon View **************************/
@@ -342,7 +343,7 @@ void SymbolsDialog::rebuild() {
addSymbol->set_sensitive( true );
removeSymbol->set_sensitive( true );
} else {
- addSymbol->set_sensitive( false );
+ addSymbol->set_sensitive( false );
removeSymbol->set_sensitive( false );
}
add_symbols( symbolDocument );
@@ -451,10 +452,21 @@ void SymbolsDialog::iconChanged() {
#ifdef WITH_LIBVISIO
// Read Visio stencil files
-SPDocument* read_vss( gchar* fullname, gchar* filename ) {
+SPDocument* read_vss( gchar* fullname, Glib::ustring name ) {
+
+ #ifdef WIN32
+ // RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows
+ // therefore attempt to convert uri to the system codepage
+ // even if this is not possible the alternate short (8.3) file name will be used if available
+ fullname = g_win32_locale_filename_from_utf8(fullname);
+ #endif
RVNGFileStream input(fullname);
+ #ifdef WIN32
+ g_free(fullname);
+ #endif
+
if (!libvisio::VisioDocument::isSupported(&input)) {
return NULL;
}
@@ -474,6 +486,12 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) {
return NULL;
}
+ // prepare a valid title for the symbol file
+ Glib::ustring title = Glib::Markup::escape_text(name);
+ // prepare a valid id prefix for the symbols (unfortunately libvisio doesn't give us a name)
+ Glib::RefPtr<Glib::Regex> regex1 = Glib::Regex::create("[^a-zA-Z0-9_-]");
+ Glib::ustring id = regex1->replace(name, 0, "_", Glib::REGEX_MATCH_PARTIAL);
+
Glib::ustring tmpSVGOutput;
tmpSVGOutput += "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
tmpSVGOutput += "<svg\n";
@@ -483,17 +501,10 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) {
tmpSVGOutput += " version=\"1.1\"\n";
tmpSVGOutput += " style=\"fill:none;stroke:#000000;stroke-width:2\">\n";
tmpSVGOutput += " <title>";
- tmpSVGOutput += filename;
+ tmpSVGOutput += title;
tmpSVGOutput += "</title>\n";
tmpSVGOutput += " <defs>\n";
- // Create a string we can use for the symbol id (libvisio doesn't give us a name)
- std::string sanitized( filename );
- sanitized.erase( sanitized.find_last_of(".vss")-3 );
- sanitized.erase( std::remove_if( sanitized.begin(), sanitized.end(), ispunct ), sanitized.end() );
- std::replace( sanitized.begin(), sanitized.end(), ' ', '_' );
- // std::cout << filename << " |" << sanitized << "|" << std::endl;
-
// Each "symbol" is in it's own SVG file, we wrap with <symbol> and merge into one file.
for (unsigned i=0; i<output.size(); ++i) {
@@ -501,7 +512,7 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) {
ss << i;
tmpSVGOutput += " <symbol id=\"";
- tmpSVGOutput += sanitized;
+ tmpSVGOutput += id;
tmpSVGOutput += "_";
tmpSVGOutput += ss.str();
tmpSVGOutput += "\">\n";
@@ -521,12 +532,12 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) {
tmpSVGOutput += " </defs>\n";
tmpSVGOutput += "</svg>\n";
-
+
return SPDocument::createNewDocFromMem( tmpSVGOutput.c_str(), strlen( tmpSVGOutput.c_str()), 0 );
}
#endif
-
+
/* Hunts preference directories for symbol files */
void SymbolsDialog::get_symbols() {
@@ -565,11 +576,14 @@ void SymbolsDialog::get_symbols() {
#ifdef WITH_LIBVISIO
if( tag.compare( "vss" ) == 0 ) {
+ // strip extension from filename and use it as name for the symbol set
+ Glib::ustring name = Glib::ustring(filename);
+ name = name.erase(name.rfind('.'));
- symbol_doc = read_vss( fullname, filename );
+ symbol_doc = read_vss( fullname, name );
if( symbol_doc ) {
- symbolSets[Glib::ustring(filename)]= symbol_doc;
- symbolSet->append(filename);
+ symbolSets[name]= symbol_doc;
+ symbolSet->append(name);
}
}
#endif
@@ -630,7 +644,7 @@ GSList* SymbolsDialog::symbols_in_doc( SPDocument* symbolDocument ) {
}
GSList* SymbolsDialog::use_in_doc_recursive (SPObject *r, GSList *l)
-{
+{
if ( dynamic_cast<SPUse *>(r) ) {
l = g_slist_prepend (l, r);
@@ -816,7 +830,7 @@ SPDocument* SymbolsDialog::symbols_preview_doc()
" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\""
" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\""
" xmlns:xlink=\"http://www.w3.org/1999/xlink\">"
-" <defs id=\"defs\">"
+" <defs id=\"defs\">"
" <symbol id=\"the_symbol\"/>"
" </defs>"
" <use id=\"the_use\" xlink:href=\"#the_symbol\"/>"
diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp
index d50c56b76..5e85065d1 100644
--- a/src/ui/interface.cpp
+++ b/src/ui/interface.cpp
@@ -72,6 +72,7 @@
#include "sp-mask.h"
#include "message-stack.h"
#include "ui/dialog/layer-properties.h"
+#include "extension/find_extension_by_mime.h"
using Inkscape::DocumentUndo;
@@ -1234,15 +1235,7 @@ sp_ui_drag_data_received(GtkWidget *widget,
case PNG_DATA:
case JPEG_DATA:
case IMAGE_DATA: {
- const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png");
-
- Inkscape::Extension::DB::InputList o;
- Inkscape::Extension::db.get_input_list(o);
- Inkscape::Extension::DB::InputList::const_iterator i = o.begin();
- while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
- ++i;
- }
- Inkscape::Extension::Extension *ext = *i;
+ Inkscape::Extension::Extension *ext = Inkscape::Extension::find_by_mime((info == JPEG_DATA ? "image/jpeg" : "image/png"));
bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0);
ext->set_param_optiongroup("link", "embed");
ext->set_gui(false);
diff --git a/src/ui/object-edit.cpp b/src/ui/object-edit.cpp
index cf2c03396..b11a61710 100644
--- a/src/ui/object-edit.cpp
+++ b/src/ui/object-edit.cpp
@@ -812,7 +812,9 @@ ArcKnotHolderEntityStart::knot_set(Geom::Point const &p, Geom::Point const &/*or
g_assert(arc != NULL);
gint side = sp_genericellipse_side(arc, p);
- if(side != 0) { arc->setClosed(side == -1); }
+ if(side != 0) { arc->setArcType( (side == -1) ?
+ SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE :
+ SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); }
Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed);
Geom::Scale sc(arc->rx.computed, arc->ry.computed);
@@ -861,7 +863,9 @@ ArcKnotHolderEntityEnd::knot_set(Geom::Point const &p, Geom::Point const &/*orig
g_assert(arc != NULL);
gint side = sp_genericellipse_side(arc, p);
- if(side != 0) { arc->setClosed(side == -1); }
+ if(side != 0) { arc->setArcType( (side == -1) ?
+ SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE :
+ SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); }
Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed);
Geom::Scale sc(arc->rx.computed, arc->ry.computed);
diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h
index c908cede2..4f152e0a2 100644
--- a/src/ui/tool/multi-path-manipulator.h
+++ b/src/ui/tool/multi-path-manipulator.h
@@ -82,8 +82,19 @@ private:
template <typename R>
void invokeForAll(R (PathManipulator::*method)()) {
- for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) {
- ((i->second.get())->*method)();
+ for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ) {
+ // Sometimes the PathManipulator got freed at loop end, thus
+ // invalidating the iterator so make sure that next_i will
+ // be a valid iterator and then assign i to it.
+ MapType::iterator next_i = i;
+ ++next_i;
+ // i->second is a boost::shared_ptr so try to hold on to it so
+ // it won't get freed prematurely by the WriteXML() method or
+ // whatever. See https://bugs.launchpad.net/inkscape/+bug/1617615
+ // Applicable to empty paths.
+ boost::shared_ptr<PathManipulator> hold(i->second);
+ ((hold.get())->*method)();
+ i = next_i;
}
}
template <typename R, typename A>
diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h
index 025c460e2..a05f0e3b9 100644
--- a/src/ui/tool/node.h
+++ b/src/ui/tool/node.h
@@ -20,12 +20,7 @@
#include <iosfwd>
#include <stdexcept>
#include <cstddef>
-
-#if __cplusplus >= 201103L
#include <functional>
-#else
-#include <tr1/functional>
-#endif
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
diff --git a/src/ui/tools/flood-tool.cpp b/src/ui/tools/flood-tool.cpp
index 1801a0ea0..6e1d085aa 100644
--- a/src/ui/tools/flood-tool.cpp
+++ b/src/ui/tools/flood-tool.cpp
@@ -21,6 +21,7 @@
#include <config.h>
#endif
+#include <cmath>
#include "trace/potrace/inkscape-potrace.h"
#include <2geom/pathvector.h>
#include <gdk/gdkkeysyms.h>
@@ -186,6 +187,21 @@ inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width
}
/**
+ * \brief Check whether two unsigned integers are close to each other
+ *
+ * \param[in] a The 1st unsigned int
+ * \param[in] b The 2nd unsigned int
+ * \param[in] d The threshold for comparison
+ *
+ * \return true if |a-b| <= d; false otherwise
+ */
+static bool compare_guint32(guint32 const a, guint32 const b, guint32 const d)
+{
+ const int difference = std::abs(static_cast<int>(a) - static_cast<int>(b));
+ return difference <= d;
+}
+
+/**
* Compare a pixel in a pixel buffer with another pixel to determine if a point should be included in the fill operation.
* @param check The pixel in the pixel buffer to check.
* @param orig The original selected pixel to use as the fill target color.
@@ -196,7 +212,6 @@ inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width
*/
static bool compare_pixels(guint32 check, guint32 orig, guint32 merged_orig_pixel, guint32 dtc, int threshold, PaintBucketChannels method)
{
- int diff = 0;
float hsl_check[3] = {0,0,0}, hsl_orig[3] = {0,0,0};
guint32 ac = 0, rc = 0, gc = 0, bc = 0;
@@ -222,27 +237,35 @@ static bool compare_pixels(guint32 check, guint32 orig, guint32 merged_orig_pixe
switch (method) {
case FLOOD_CHANNELS_ALPHA:
- return abs(static_cast<int>(ac) - ao) <= threshold;
+ return compare_guint32(ac, ao, threshold);
case FLOOD_CHANNELS_R:
- return abs(static_cast<int>(ac ? unpremul_alpha(rc, ac) : 0) - (ao ? unpremul_alpha(ro, ao) : 0)) <= threshold;
+ return compare_guint32(ac ? unpremul_alpha(rc, ac) : 0,
+ ao ? unpremul_alpha(ro, ao) : 0,
+ threshold);
case FLOOD_CHANNELS_G:
- return abs(static_cast<int>(ac ? unpremul_alpha(gc, ac) : 0) - (ao ? unpremul_alpha(go, ao) : 0)) <= threshold;
+ return compare_guint32(ac ? unpremul_alpha(gc, ac) : 0,
+ ao ? unpremul_alpha(go, ao) : 0,
+ threshold);
case FLOOD_CHANNELS_B:
- return abs(static_cast<int>(ac ? unpremul_alpha(bc, ac) : 0) - (ao ? unpremul_alpha(bo, ao) : 0)) <= threshold;
+ return compare_guint32(ac ? unpremul_alpha(bc, ac) : 0,
+ ao ? unpremul_alpha(bo, ao) : 0,
+ threshold);
case FLOOD_CHANNELS_RGB:
- guint32 amc, rmc, bmc, gmc;
- //amc = 255*255 - (255-ac)*(255-ad); amc = (amc + 127) / 255;
- //amc = (255-ac)*ad + 255*ac; amc = (amc + 127) / 255;
- amc = 255; // Why are we looking at desktop? Cairo version ignores destop alpha
- rmc = (255-ac)*rd + 255*rc; rmc = (rmc + 127) / 255;
- gmc = (255-ac)*gd + 255*gc; gmc = (gmc + 127) / 255;
- bmc = (255-ac)*bd + 255*bc; bmc = (bmc + 127) / 255;
-
- diff += abs(static_cast<int>(amc ? unpremul_alpha(rmc, amc) : 0) - (amop ? unpremul_alpha(rmop, amop) : 0));
- diff += abs(static_cast<int>(amc ? unpremul_alpha(gmc, amc) : 0) - (amop ? unpremul_alpha(gmop, amop) : 0));
- diff += abs(static_cast<int>(amc ? unpremul_alpha(bmc, amc) : 0) - (amop ? unpremul_alpha(bmop, amop) : 0));
- return ((diff / 3) <= ((threshold * 3) / 4));
-
+ {
+ guint32 amc, rmc, bmc, gmc;
+ //amc = 255*255 - (255-ac)*(255-ad); amc = (amc + 127) / 255;
+ //amc = (255-ac)*ad + 255*ac; amc = (amc + 127) / 255;
+ amc = 255; // Why are we looking at desktop? Cairo version ignores destop alpha
+ rmc = (255-ac)*rd + 255*rc; rmc = (rmc + 127) / 255;
+ gmc = (255-ac)*gd + 255*gc; gmc = (gmc + 127) / 255;
+ bmc = (255-ac)*bd + 255*bc; bmc = (bmc + 127) / 255;
+
+ int diff = 0; // The total difference between each of the 3 color components
+ diff += std::abs(static_cast<int>(amc ? unpremul_alpha(rmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(rmop, amop) : 0));
+ diff += std::abs(static_cast<int>(amc ? unpremul_alpha(gmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(gmop, amop) : 0));
+ diff += std::abs(static_cast<int>(amc ? unpremul_alpha(bmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(bmop, amop) : 0));
+ return ((diff / 3) <= ((threshold * 3) / 4));
+ }
case FLOOD_CHANNELS_H:
return ((int)(fabs(hsl_check[0] - hsl_orig[0]) * 100.0) <= threshold);
case FLOOD_CHANNELS_S:
diff --git a/src/ui/tools/freehand-base.h b/src/ui/tools/freehand-base.h
index a3e7b42f9..3ee4cd7d0 100644
--- a/src/ui/tools/freehand-base.h
+++ b/src/ui/tools/freehand-base.h
@@ -76,8 +76,10 @@ public:
GSList *white_curves;
GSList *white_anchors;
- //ALternative curve to use on continuing exisiting curve in case of bspline or spirolive
- //because usigh anchor curves give memory and random bugs, - and obscure code- in some plataform reported by su_v in mac
+ // Alternative curve to use on continuing the exisiting curve in case of
+ // bspline or spirolive, because using anchor curves gives random memory
+ // bugs as reported by su_v when running this code on macOS (as well as
+ // making the code hard to understand).
SPCurve *overwrite_curve;
// Start anchor
diff --git a/src/ui/tools/mesh-tool.cpp b/src/ui/tools/mesh-tool.cpp
index e628094d9..ac43b6c9d 100644
--- a/src/ui/tools/mesh-tool.cpp
+++ b/src/ui/tools/mesh-tool.cpp
@@ -1137,7 +1137,7 @@ static void sp_mesh_new_default(MeshTool &rc) {
Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
// Ensure mesh is immediately editable.
- // Editting both fill and stroke at same time doesn't work well so avoid.
+ // Editing both fill and stroke at same time doesn't work well so avoid.
if (fill_or_stroke == Inkscape::FOR_FILL) {
prefs->setBool("/tools/mesh/edit_fill", true );
prefs->setBool("/tools/mesh/edit_stroke", false);
diff --git a/src/ui/tools/select-tool.cpp b/src/ui/tools/select-tool.cpp
index 6c450b3dc..bae1793ed 100644
--- a/src/ui/tools/select-tool.cpp
+++ b/src/ui/tools/select-tool.cpp
@@ -142,7 +142,7 @@ void SelectTool::setup() {
this->_describer = new Inkscape::SelectionDescriber(
desktop->selection,
desktop->messageStack(),
- _("Click selection to toggle scale/rotation handles"),
+ _("Click selection to toggle scale/rotation handles (or Shift+s)"),
_("No objects selected. Click, Shift+click, Alt+scroll mouse on top of objects, or drag around objects to select.")
);
diff --git a/src/ui/tools/text-tool.cpp b/src/ui/tools/text-tool.cpp
index 559187764..649bbb045 100644
--- a/src/ui/tools/text-tool.cpp
+++ b/src/ui/tools/text-tool.cpp
@@ -48,6 +48,8 @@
#include "ui/control-manager.h"
#include "verbs.h"
#include "xml/node-event-vector.h"
+#include "xml/attribute-record.h"
+#include "xml/sp-css-attr.h"
using Inkscape::ControlManager;
using Inkscape::DocumentUndo;
@@ -1357,6 +1359,59 @@ SPCSSAttr *sp_text_get_style_at_cursor(ToolBase const *ec)
return NULL;
}
+static bool css_attrs_are_equal(SPCSSAttr const *first, SPCSSAttr const *second)
+{
+ Inkscape::Util::List<Inkscape::XML::AttributeRecord const> attrs = first->attributeList();
+ for ( ; attrs ; attrs++) {
+ gchar const *other_attr = second->attribute(g_quark_to_string(attrs->key));
+ if (other_attr == NULL || strcmp(attrs->value, other_attr))
+ return false;
+ }
+ attrs = second->attributeList();
+ for ( ; attrs ; attrs++) {
+ gchar const *other_attr = first->attribute(g_quark_to_string(attrs->key));
+ if (other_attr == NULL || strcmp(attrs->value, other_attr))
+ return false;
+ }
+ return true;
+}
+
+std::vector<SPCSSAttr*> sp_text_get_selected_style(ToolBase const *ec, unsigned *k, int *b, std::vector<unsigned> *positions)
+{
+ std::vector<SPCSSAttr*> vec;
+ SPCSSAttr *css, *css_new;
+ TextTool *tc = SP_TEXT_CONTEXT(ec);
+ Inkscape::Text::Layout::iterator i = std::min(tc->text_sel_start, tc->text_sel_end);
+ SPObject const *obj = sp_te_object_at_position(tc->text, i);
+ if (obj) {
+ css = take_style_from_item(const_cast<SPObject*>(obj));
+ }
+ vec.push_back(css);
+ positions->push_back(0);
+ i.nextCharacter();
+ *k = 1;
+ *b = 1;
+ while (i != std::max(tc->text_sel_start, tc->text_sel_end))
+ {
+ obj = sp_te_object_at_position(tc->text, i);
+ if (obj) {
+ css_new = take_style_from_item(const_cast<SPObject*>(obj));
+ }
+ if(!css_attrs_are_equal(css, css_new))
+ {
+ vec.push_back(css_new);
+ css = sp_repr_css_attr_new();
+ sp_repr_css_merge(css, css_new);
+ positions->push_back(*k);
+ (*b)++;
+ }
+ i.nextCharacter();
+ (*k)++;
+ }
+ positions->push_back(*k);
+ return vec;
+}
+
/**
Deletes the currently selected characters. Returns false if there is no
text selection currently.
@@ -1442,7 +1497,6 @@ bool TextTool::_styleSet(SPCSSAttr const *css)
_("Set text style"));
sp_text_context_update_cursor(this);
sp_text_context_update_text_selection(this);
-
return true;
}
diff --git a/src/ui/tools/text-tool.h b/src/ui/tools/text-tool.h
index 289ee180d..f2e6fea1a 100644
--- a/src/ui/tools/text-tool.h
+++ b/src/ui/tools/text-tool.h
@@ -94,6 +94,7 @@ private:
bool sp_text_paste_inline(ToolBase *ec);
Glib::ustring sp_text_get_selected_text(ToolBase const *ec);
SPCSSAttr *sp_text_get_style_at_cursor(ToolBase const *ec);
+std::vector<SPCSSAttr*> sp_text_get_selected_style(ToolBase const *ec, unsigned *k, int *b, std::vector<unsigned> *positions);
bool sp_text_delete_selection(ToolBase *ec);
void sp_text_context_place_cursor (TextTool *tc, SPObject *text, Inkscape::Text::Layout::iterator where);
void sp_text_context_place_cursor_at (TextTool *tc, SPObject *text, Geom::Point const p);
diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp
index aab256712..8c7f54d14 100644
--- a/src/ui/tools/tool-base.cpp
+++ b/src/ui/tools/tool-base.cpp
@@ -497,7 +497,7 @@ bool ToolBase::root_handler(GdkEvent* event) {
Geom::Point const motion_w(event->motion.x, event->motion.y);
Geom::Point const moved_w(motion_w - button_w);
- this->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw
+ this->desktop->scroll_relative(moved_w, true); // we're still scrolling, do not redraw
ret = TRUE;
}
} else if (zoom_rb) {
@@ -575,7 +575,7 @@ bool ToolBase::root_handler(GdkEvent* event) {
Geom::Point const motion_w(event->button.x, event->button.y);
Geom::Point const moved_w(motion_w - button_w);
- this->desktop->scroll_world(moved_w);
+ this->desktop->scroll_relative(moved_w);
desktop->updateNow();
ret = TRUE;
} else if (zoom_rb == event->button.button) {
@@ -675,7 +675,7 @@ bool ToolBase::root_handler(GdkEvent* event) {
acceleration, desktop->getCanvas()));
gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(i, 0);
+ this->desktop->scroll_relative(Geom::Point(i, 0));
ret = TRUE;
}
break;
@@ -688,7 +688,7 @@ bool ToolBase::root_handler(GdkEvent* event) {
acceleration, desktop->getCanvas()));
gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(0, i);
+ this->desktop->scroll_relative(Geom::Point(0, i));
ret = TRUE;
}
break;
@@ -701,7 +701,7 @@ bool ToolBase::root_handler(GdkEvent* event) {
acceleration, desktop->getCanvas()));
gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(-i, 0);
+ this->desktop->scroll_relative(Geom::Point(-i, 0));
ret = TRUE;
}
break;
@@ -714,7 +714,7 @@ bool ToolBase::root_handler(GdkEvent* event) {
acceleration, desktop->getCanvas()));
gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(0, -i);
+ this->desktop->scroll_relative(Geom::Point(0, -i));
ret = TRUE;
}
break;
@@ -856,11 +856,11 @@ bool ToolBase::root_handler(GdkEvent* event) {
if (event->scroll.state & GDK_SHIFT_MASK) {
switch (event->scroll.direction) {
case GDK_SCROLL_UP:
- desktop->scroll_world(wheel_scroll, 0);
+ desktop->scroll_relative(Geom::Point(wheel_scroll, 0));
break;
case GDK_SCROLL_DOWN:
- desktop->scroll_world(-wheel_scroll, 0);
+ desktop->scroll_relative(Geom::Point(-wheel_scroll, 0));
break;
default:
@@ -896,24 +896,24 @@ bool ToolBase::root_handler(GdkEvent* event) {
} else {
switch (event->scroll.direction) {
case GDK_SCROLL_UP:
- desktop->scroll_world(0, wheel_scroll);
+ desktop->scroll_relative(Geom::Point(0, wheel_scroll));
break;
case GDK_SCROLL_DOWN:
- desktop->scroll_world(0, -wheel_scroll);
+ desktop->scroll_relative(Geom::Point(0, -wheel_scroll));
break;
case GDK_SCROLL_LEFT:
- desktop->scroll_world(wheel_scroll, 0);
+ desktop->scroll_relative(Geom::Point(wheel_scroll, 0));
break;
case GDK_SCROLL_RIGHT:
- desktop->scroll_world(-wheel_scroll, 0);
+ desktop->scroll_relative(Geom::Point(-wheel_scroll, 0));
break;
case GDK_SCROLL_SMOOTH:
gdk_event_get_scroll_deltas(event, &delta_x, &delta_y);
- desktop->scroll_world(delta_x, delta_y);
+ desktop->scroll_relative(Geom::Point(delta_x, delta_y));
break;
}
}
diff --git a/src/ui/widget/panel.h b/src/ui/widget/panel.h
index 9cbf39de9..b5498498d 100644
--- a/src/ui/widget/panel.h
+++ b/src/ui/widget/panel.h
@@ -104,6 +104,7 @@ public:
void setDefaultResponse(int response_id);
void setResponseSensitive(int response_id, bool setting);
+ /* Return signals. Signals emited by PanelDialog. */
virtual sigc::signal<void, SPDesktop *, SPDocument *> &signalDocumentReplaced();
virtual sigc::signal<void, SPDesktop *> &signalActivateDesktop();
virtual sigc::signal<void, SPDesktop *> &signalDeactiveDesktop();