summaryrefslogtreecommitdiffstats
path: root/src/ui
diff options
context:
space:
mode:
authorDenis Declara <declara91@gmail.com>2012-03-26 15:59:00 +0000
committerDenis Declara <declara91@gmail.com>2012-03-26 15:59:00 +0000
commit8874f80dd60361ed721554497862ce3a6fb5ea8e (patch)
treefe114b3559c430d1a90f073a3458f4bd1c6d2285 /src/ui
parentAdded anchor-selection widget, it doesn't do much at the moment, next step wi... (diff)
parentpowerstroke: spiro join: more robust detection of tangent direction. fixes in... (diff)
downloadinkscape-8874f80dd60361ed721554497862ce3a6fb5ea8e.tar.gz
inkscape-8874f80dd60361ed721554497862ce3a6fb5ea8e.zip
Trunk merge
(bzr r11073.1.4)
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/CMakeLists.txt2
-rw-r--r--src/ui/context-menu.cpp1372
-rw-r--r--src/ui/dialog/Makefile_insert2
-rw-r--r--src/ui/dialog/clonetiler.cpp2833
-rw-r--r--src/ui/dialog/clonetiler.h183
-rw-r--r--src/ui/dialog/dialog-manager.cpp3
-rw-r--r--src/ui/dialog/find.cpp887
-rw-r--r--src/ui/dialog/find.h282
-rw-r--r--src/ui/dialog/guides.cpp6
-rw-r--r--src/ui/dialog/transformation.cpp2
-rw-r--r--src/ui/dialog/xml-tree.cpp23
-rw-r--r--src/ui/dialog/xml-tree.h7
-rw-r--r--src/ui/widget/button.cpp16
-rw-r--r--src/ui/widget/button.h14
-rw-r--r--src/ui/widget/entry.cpp1
-rw-r--r--src/ui/widget/entry.h2
16 files changed, 4672 insertions, 963 deletions
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index e21c3795f..bfd8cca95 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -25,6 +25,7 @@ set(ui_SRC
dialog/align-and-distribute.cpp
dialog/calligraphic-profile-rename.cpp
dialog/color-item.cpp
+ dialog/clonetiler.cpp
dialog/debug.cpp
dialog/desktop-tracker.cpp
dialog/dialog-manager.cpp
@@ -135,6 +136,7 @@ set(ui_SRC
dialog/behavior.h
dialog/calligraphic-profile-rename.h
dialog/color-item.h
+ dialog/clonetiler.h
dialog/debug.h
dialog/desktop-tracker.h
dialog/dialog-manager.h
diff --git a/src/ui/context-menu.cpp b/src/ui/context-menu.cpp
index bf116f5fb..9e5c65af9 100644
--- a/src/ui/context-menu.cpp
+++ b/src/ui/context-menu.cpp
@@ -1,679 +1,693 @@
-/*
- * Unser-interface related object extension
- *
- * Authors:
- * Lauris Kaplinski <lauris@kaplinski.com>
- * Jon A. Cruz <jon@joncruz.org>
- * Abhishek Sharma
- *
- * This code is in public domain
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "ui/dialog/dialog-manager.h"
-#include "context-menu.h"
-#include "../xml/repr.h"
-#include "desktop.h"
-#include "document.h"
-#include "document-undo.h"
-#include "helper/action.h" //sp_action_perform
-#include "inkscape.h"
-#include "message-stack.h"
-#include "preferences.h"
-#include "verbs.h"
-
-using Inkscape::DocumentUndo;
-
-static void sp_object_type_menu(GType type, SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-
-/* Append object-specific part to context menu */
-
-void sp_object_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu)
-{
- GObjectClass *klass;
- klass = G_OBJECT_GET_CLASS(object);
- while (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_OBJECT)) {
- GType type;
- type = G_TYPE_FROM_CLASS(klass);
- sp_object_type_menu(type, object, desktop, menu);
- klass = (GObjectClass*)g_type_class_peek_parent(klass);
- }
-}
-
-/* Implementation */
-
-#include <gtk/gtk.h>
-#include <glibmm/i18n.h>
-
-#include "selection.h"
-#include "selection-chemistry.h"
-#include "sp-anchor.h"
-#include "sp-clippath.h"
-#include "sp-image.h"
-#include "sp-mask.h"
-#include "sp-path.h"
-#include "sp-text.h"
-#include "desktop-handles.h"
-#include "ui/dialog/object-attributes.h"
-#include "ui/dialog/object-properties.h"
-#include "ui/dialog/spellcheck.h"
-
-
-static void sp_item_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-static void sp_group_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-static void sp_anchor_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-static void sp_image_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-static void sp_shape_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-static void sp_text_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-
-static void sp_object_type_menu(GType type, SPObject *object, SPDesktop *desktop, GtkMenu *menu)
-{
- static GHashTable *t2m = NULL;
- void (* handler)(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
- if (!t2m) {
- t2m = g_hash_table_new(NULL, NULL);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_ITEM), (void*)sp_item_menu);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_GROUP), (void*)sp_group_menu);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_ANCHOR), (void*)sp_anchor_menu);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_IMAGE), (void*)sp_image_menu);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_SHAPE), (void*)sp_shape_menu);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_TEXT), (void*)sp_text_menu);
- }
- handler = (void (*)(SPObject*, SPDesktop*, GtkMenu*))g_hash_table_lookup(t2m, GUINT_TO_POINTER(type));
- if (handler) handler(object, desktop, menu);
-}
-
-/* SPItem */
-
-static void sp_item_properties(GtkMenuItem *menuitem, SPItem *item);
-static void sp_item_select_this(GtkMenuItem *menuitem, SPItem *item);
-static void sp_item_create_link(GtkMenuItem *menuitem, SPItem *item);
-static void sp_set_mask(GtkMenuItem *menuitem, SPItem *item);
-static void sp_release_mask(GtkMenuItem *menuitem, SPItem *item);
-static void sp_set_clip(GtkMenuItem *menuitem, SPItem *item);
-static void sp_release_clip(GtkMenuItem *menuitem, SPItem *item);
-
-/* Generate context menu item section */
-static void sp_item_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
-{
- SPItem *item;
- GtkWidget *w;
-
- item = (SPItem *) object;
-
- /* Item dialog */
- w = gtk_menu_item_new_with_mnemonic(_("_Object Properties..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_properties), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Separator */
- w = gtk_menu_item_new();
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Select item */
- w = gtk_menu_item_new_with_mnemonic(_("_Select This"));
- if (sp_desktop_selection(desktop)->includes(item)) {
- gtk_widget_set_sensitive(w, FALSE);
- } else {
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_select_this), item);
- }
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Create link */
- w = gtk_menu_item_new_with_mnemonic(_("_Create Link"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_create_link), item);
- gtk_widget_set_sensitive(w, !SP_IS_ANCHOR(item));
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Set mask */
- w = gtk_menu_item_new_with_mnemonic(_("Set Mask"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_set_mask), item);
- if ((item && item->mask_ref && item->mask_ref->getObject()) || (item->clip_ref && item->clip_ref->getObject())) {
- gtk_widget_set_sensitive(w, FALSE);
- } else {
- gtk_widget_set_sensitive(w, TRUE);
- }
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Release mask */
- w = gtk_menu_item_new_with_mnemonic(_("Release Mask"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_release_mask), item);
- if (item && item->mask_ref && item->mask_ref->getObject()) {
- gtk_widget_set_sensitive(w, TRUE);
- } else {
- gtk_widget_set_sensitive(w, FALSE);
- }
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Set Clip */
- w = gtk_menu_item_new_with_mnemonic(_("Set _Clip"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_set_clip), item);
- if ((item && item->mask_ref && item->mask_ref->getObject()) || (item->clip_ref && item->clip_ref->getObject())) {
- gtk_widget_set_sensitive(w, FALSE);
- } else {
- gtk_widget_set_sensitive(w, TRUE);
- }
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Release Clip */
- w = gtk_menu_item_new_with_mnemonic(_("Release C_lip"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_release_clip), item);
- if (item && item->clip_ref && item->clip_ref->getObject()) {
- gtk_widget_set_sensitive(w, TRUE);
- } else {
- gtk_widget_set_sensitive(w, FALSE);
- }
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-
-}
-
-static void sp_item_properties(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_desktop_selection(desktop)->set(item);
-
- // sp_item_dialog();
- desktop->_dlg_mgr->showDialog("ObjectProperties");
-}
-
-
-static void sp_set_mask(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_selection_set_mask(desktop, false, false);
-}
-
-
-static void sp_release_mask(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_selection_unset_mask(desktop, false);
-}
-
-
-static void sp_set_clip(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_selection_set_mask(desktop, true, false);
-}
-
-
-static void sp_release_clip(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_selection_unset_mask(desktop, true);
-}
-
-
-static void sp_item_select_this(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_desktop_selection(desktop)->set(item);
-}
-
-static void sp_item_create_link(GtkMenuItem *menuitem, SPItem *item)
-{
- g_assert(SP_IS_ITEM(item));
- g_assert(!SP_IS_ANCHOR(item));
-
- SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
- Inkscape::XML::Node *repr = xml_doc->createElement("svg:a");
- item->parent->getRepr()->addChild(repr, item->getRepr());
- SPObject *object = item->document->getObjectByRepr(repr);
- g_return_if_fail(SP_IS_ANCHOR(object));
-
- const char *id = item->getRepr()->attribute("id");
- Inkscape::XML::Node *child = item->getRepr()->duplicate(xml_doc);
- item->deleteObject(false);
- repr->addChild(child, NULL);
- child->setAttribute("id", id);
-
- Inkscape::GC::release(repr);
- Inkscape::GC::release(child);
-
- DocumentUndo::done(object->document, SP_VERB_NONE,
- _("Create link"));
-
- sp_desktop_selection(desktop)->set(SP_ITEM(object));
- desktop->_dlg_mgr->showDialog("ObjectAttributes");
-}
-
-/* SPGroup */
-
-static void sp_item_group_ungroup_activate(GtkMenuItem *menuitem, SPGroup *group);
-
-static void sp_group_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu)
-{
- SPItem *item=SP_ITEM(object);
- GtkWidget *w;
-
- /* "Ungroup" */
- w = gtk_menu_item_new_with_mnemonic(_("_Ungroup"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_group_ungroup_activate), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), w);
-}
-
-static void sp_item_group_ungroup_activate(GtkMenuItem *menuitem, SPGroup *group)
-{
- SPDesktop *desktop;
- GSList *children;
-
- g_assert(SP_IS_GROUP(group));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- children = NULL;
- sp_item_group_ungroup(group, &children);
-
- sp_desktop_selection(desktop)->setList(children);
- g_slist_free(children);
-}
-
-/* SPAnchor */
-
-static void sp_anchor_link_properties(GtkMenuItem *menuitem, SPAnchor *anchor);
-static void sp_anchor_link_follow(GtkMenuItem *menuitem, SPAnchor *anchor);
-static void sp_anchor_link_remove(GtkMenuItem *menuitem, SPAnchor *anchor);
-
-static void sp_anchor_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
-{
- SPItem *item;
- GtkWidget *w;
-
- item = (SPItem *) object;
-
- /* Link dialog */
- w = gtk_menu_item_new_with_mnemonic(_("Link _Properties..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_properties), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Select item */
- w = gtk_menu_item_new_with_mnemonic(_("_Follow Link"));
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_follow), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Reset transformations */
- w = gtk_menu_item_new_with_mnemonic(_("_Remove Link"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_remove), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-}
-
-static void sp_anchor_link_properties(GtkMenuItem *menuitem, SPAnchor */*anchor*/)
-{
- SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
- desktop->_dlg_mgr->showDialog("ObjectAttributes");
-}
-
-static void sp_anchor_link_follow(GtkMenuItem */*menuitem*/, SPAnchor *anchor)
-{
- g_return_if_fail(anchor != NULL);
- g_return_if_fail(SP_IS_ANCHOR(anchor));
-
- /* shell out to an external browser here */
-}
-
-static void sp_anchor_link_remove(GtkMenuItem */*menuitem*/, SPAnchor *anchor)
-{
- GSList *children;
-
- g_return_if_fail(anchor != NULL);
- g_return_if_fail(SP_IS_ANCHOR(anchor));
-
- children = NULL;
- sp_item_group_ungroup(SP_GROUP(anchor), &children);
-
- g_slist_free(children);
-}
-
-/* Image */
-
-static void sp_image_image_properties(GtkMenuItem *menuitem, SPAnchor *anchor);
-static void sp_image_image_edit(GtkMenuItem *menuitem, SPAnchor *anchor);
-static void sp_image_image_embed(GtkMenuItem *menuitem, SPItem *item);
-static void sp_image_image_extract(GtkMenuItem *menuitem, SPItem *item);
-
-static void sp_image_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
-{
- SPItem *item = SP_ITEM(object);
- GtkWidget *w;
- Inkscape::XML::Node *ir = object->getRepr();
- const gchar *href = ir->attribute("xlink:href");
-
- /* Image properties */
- w = gtk_menu_item_new_with_mnemonic(_("Image _Properties..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_properties), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-
- /* Edit externally */
- w = gtk_menu_item_new_with_mnemonic(_("Edit Externally..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_edit), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) {
- gtk_widget_set_sensitive( w, FALSE );
- }
-
- /* Embed image */
- if (Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" )) {
- w = gtk_menu_item_new_with_mnemonic(C_("Context menu", "Embed Image"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_embed), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) {
- gtk_widget_set_sensitive( w, FALSE );
- }
- }
-
- /* Extract image */
- if (Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" )) {
- w = gtk_menu_item_new_with_mnemonic(C_("Context menu", "Extract Image"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_extract), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- if ( (!href) || ((strncmp(href, "data:", 5) != 0)) ) {
- gtk_widget_set_sensitive( w, FALSE );
- }
- }
-}
-
-/* Image Properties entry */
-static void sp_image_image_properties(GtkMenuItem *menuitem, SPAnchor */*anchor*/)
-{
- SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
- desktop->_dlg_mgr->showDialog("ObjectAttributes");
-}
-
-static gchar* getImageEditorName() {
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- gchar* value = 0;
- Glib::ustring choices = prefs->getString("/options/bitmapeditor/value");
- if (!choices.empty()) {
- value = g_strdup(choices.c_str());
- }
- if (!value) {
- value = g_strdup("gimp");
- }
- return value;
-}
-
-/* Edit Externally entry */
-static void sp_image_image_edit(GtkMenuItem *menuitem, SPAnchor *anchor)
-{
- SPObject* obj = anchor;
- Inkscape::XML::Node *ir = obj->getRepr();
- const gchar *href = ir->attribute("xlink:href");
-
- GError* errThing = 0;
- Glib::ustring cmdline = getImageEditorName();
- Glib::ustring name;
- Glib::ustring fullname;
-
-#ifdef WIN32
- // g_spawn_command_line_sync parsing is done according to Unix shell rules,
- // not Windows command interpreter rules. Thus we need to enclose the
- // executable path with sigle quotes.
- int index = cmdline.find(".exe");
- if ( index < 0 ) index = cmdline.find(".bat");
- if ( index < 0 ) index = cmdline.find(".com");
- if ( index >= 0 ) {
- Glib::ustring editorBin = cmdline.substr(0, index + 4).c_str();
- Glib::ustring args = cmdline.substr(index + 4, cmdline.length()).c_str();
- editorBin.insert(0, "'");
- editorBin.append("'");
- cmdline = editorBin;
- cmdline.append(args);
- } else {
- // Enclose the whole command line if no executable path can be extracted.
- cmdline.insert(0, "'");
- cmdline.append("'");
- }
-#endif
-
- if (strncmp (href,"file:",5) == 0) {
- // URI to filename conversion
- name = g_filename_from_uri(href, NULL, NULL);
- } else {
- name.append(href);
- }
-
- if (Glib::path_is_absolute(name)) {
- fullname = name;
- } else if (SP_ACTIVE_DOCUMENT->getBase()) {
- fullname = Glib::build_filename(SP_ACTIVE_DOCUMENT->getBase(), name);
- } else {
- fullname = Glib::build_filename(Glib::get_current_dir(), name);
- }
-
- cmdline.append(" '");
- cmdline.append(fullname.c_str());
- cmdline.append("'");
-
- //g_warning("##Command line: %s\n", cmdline.c_str());
-
- g_spawn_command_line_async(cmdline.c_str(),
- &errThing);
-
- if ( errThing ) {
- g_warning("Problem launching editor (%d). %s", errThing->code, errThing->message);
- SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, errThing->message);
- g_error_free(errThing);
- errThing = 0;
- }
-}
-
-/* Embed Image entry */
-static void sp_image_image_embed(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- if (sp_desktop_selection(desktop)->isEmpty()) {
- sp_desktop_selection(desktop)->set(item);
- }
-
- Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" );
- if (verb) {
- SPAction *action = verb->get_action(desktop);
- if (action) {
- sp_action_perform(action, NULL);
- }
- }
-}
-
-/* Extract Image entry */
-static void sp_image_image_extract(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- if (sp_desktop_selection(desktop)->isEmpty()) {
- sp_desktop_selection(desktop)->set(item);
- }
-
- Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" );
- if (verb) {
- SPAction *action = verb->get_action(desktop);
- if (action) {
- sp_action_perform(action, NULL);
- }
- }
-}
-
-/* Fill and Stroke entry */
-static void sp_fill_settings(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- if (sp_desktop_selection(desktop)->isEmpty()) {
- sp_desktop_selection(desktop)->set(item);
- }
-
- desktop->_dlg_mgr->showDialog("FillAndStroke");
-}
-
-/* SPShape */
-static void sp_shape_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
-{
- SPItem *item;
- GtkWidget *w;
-
- item = (SPItem *) object;
-
- /* Item dialog */
- w = gtk_menu_item_new_with_mnemonic(_("_Fill and Stroke..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_fill_settings), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-}
-
-/* Edit Text entry */
-static void sp_text_settings(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- if (sp_desktop_selection(desktop)->isEmpty()) {
- sp_desktop_selection(desktop)->set(item);
- }
-
- desktop->_dlg_mgr->showDialog("TextFont");
-}
-
-/* Spellcheck entry */
-static void sp_spellcheck_settings(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- if (sp_desktop_selection(desktop)->isEmpty()) {
- sp_desktop_selection(desktop)->set(item);
- }
-
- desktop->_dlg_mgr->showDialog("SpellCheck");
-}
-
-/* SPText */
-static void sp_text_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
-{
- SPItem *item;
- GtkWidget *w;
-
- item = (SPItem *) object;
-
- /* Fill and Stroke dialog */
- w = gtk_menu_item_new_with_mnemonic(_("_Fill and Stroke..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_fill_settings), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-
- /* Edit Text dialog */
- w = gtk_menu_item_new_with_mnemonic(_("_Text and Font..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_text_settings), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-
- /* Spellcheck dialog */
- w = gtk_menu_item_new_with_mnemonic(_("Check Spellin_g..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_spellcheck_settings), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-}
-/*
- 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 :
+/*
+ * Unser-interface related object extension
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Abhishek Sharma
+ *
+ * This code is in public domain
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "ui/dialog/dialog-manager.h"
+#include "context-menu.h"
+#include "../xml/repr.h"
+#include "desktop.h"
+#include "document.h"
+#include "document-undo.h"
+#include "helper/action.h" //sp_action_perform
+#include "inkscape.h"
+#include "message-stack.h"
+#include "preferences.h"
+#include "verbs.h"
+
+using Inkscape::DocumentUndo;
+
+static void sp_object_type_menu(GType type, SPObject *object, SPDesktop *desktop, GtkMenu *menu);
+
+/* Append object-specific part to context menu */
+
+void sp_object_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu)
+{
+ GObjectClass *klass;
+ klass = G_OBJECT_GET_CLASS(object);
+ while (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_OBJECT)) {
+ GType type;
+ type = G_TYPE_FROM_CLASS(klass);
+ sp_object_type_menu(type, object, desktop, menu);
+ klass = (GObjectClass*)g_type_class_peek_parent(klass);
+ }
+}
+
+/* Implementation */
+
+#include <gtk/gtk.h>
+#include <glibmm/i18n.h>
+
+#include "selection.h"
+#include "selection-chemistry.h"
+#include "sp-anchor.h"
+#include "sp-clippath.h"
+#include "sp-image.h"
+#include "sp-mask.h"
+#include "sp-path.h"
+#include "sp-text.h"
+#include "desktop-handles.h"
+#include "ui/dialog/object-attributes.h"
+#include "ui/dialog/object-properties.h"
+#include "ui/dialog/spellcheck.h"
+
+
+static void sp_item_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
+static void sp_group_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
+static void sp_anchor_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
+static void sp_image_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
+static void sp_shape_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
+static void sp_text_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
+
+static void sp_object_type_menu(GType type, SPObject *object, SPDesktop *desktop, GtkMenu *menu)
+{
+ static GHashTable *t2m = NULL;
+ void (* handler)(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
+ if (!t2m) {
+ t2m = g_hash_table_new(NULL, NULL);
+ g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_ITEM), (void*)sp_item_menu);
+ g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_GROUP), (void*)sp_group_menu);
+ g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_ANCHOR), (void*)sp_anchor_menu);
+ g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_IMAGE), (void*)sp_image_menu);
+ g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_SHAPE), (void*)sp_shape_menu);
+ g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_TEXT), (void*)sp_text_menu);
+ }
+ handler = (void (*)(SPObject*, SPDesktop*, GtkMenu*))g_hash_table_lookup(t2m, GUINT_TO_POINTER(type));
+ if (handler) handler(object, desktop, menu);
+}
+
+/* SPItem */
+
+static void sp_item_properties(GtkMenuItem *menuitem, SPItem *item);
+static void sp_item_select_this(GtkMenuItem *menuitem, SPItem *item);
+static void sp_item_create_link(GtkMenuItem *menuitem, SPItem *item);
+static void sp_set_mask(GtkMenuItem *menuitem, SPItem *item);
+static void sp_release_mask(GtkMenuItem *menuitem, SPItem *item);
+static void sp_set_clip(GtkMenuItem *menuitem, SPItem *item);
+static void sp_release_clip(GtkMenuItem *menuitem, SPItem *item);
+
+/* Generate context menu item section */
+static void sp_item_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
+{
+ SPItem *item;
+ GtkWidget *w;
+
+ item = (SPItem *) object;
+
+ /* Item dialog */
+ w = gtk_menu_item_new_with_mnemonic(_("_Object Properties..."));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_properties), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ /* Separator */
+ w = gtk_menu_item_new();
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ /* Select item */
+ w = gtk_menu_item_new_with_mnemonic(_("_Select This"));
+ if (sp_desktop_selection(desktop)->includes(item)) {
+ gtk_widget_set_sensitive(w, FALSE);
+ } else {
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_select_this), item);
+ }
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ /* Create link */
+ w = gtk_menu_item_new_with_mnemonic(_("_Create Link"));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_create_link), item);
+ gtk_widget_set_sensitive(w, !SP_IS_ANCHOR(item));
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ /* Set mask */
+ w = gtk_menu_item_new_with_mnemonic(_("Set Mask"));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_set_mask), item);
+ if ((item && item->mask_ref && item->mask_ref->getObject()) || (item->clip_ref && item->clip_ref->getObject())) {
+ gtk_widget_set_sensitive(w, FALSE);
+ } else {
+ gtk_widget_set_sensitive(w, TRUE);
+ }
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ /* Release mask */
+ w = gtk_menu_item_new_with_mnemonic(_("Release Mask"));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_release_mask), item);
+ if (item && item->mask_ref && item->mask_ref->getObject()) {
+ gtk_widget_set_sensitive(w, TRUE);
+ } else {
+ gtk_widget_set_sensitive(w, FALSE);
+ }
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ /* Set Clip */
+ w = gtk_menu_item_new_with_mnemonic(_("Set _Clip"));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_set_clip), item);
+ if ((item && item->mask_ref && item->mask_ref->getObject()) || (item->clip_ref && item->clip_ref->getObject())) {
+ gtk_widget_set_sensitive(w, FALSE);
+ } else {
+ gtk_widget_set_sensitive(w, TRUE);
+ }
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ /* Release Clip */
+ w = gtk_menu_item_new_with_mnemonic(_("Release C_lip"));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_release_clip), item);
+ if (item && item->clip_ref && item->clip_ref->getObject()) {
+ gtk_widget_set_sensitive(w, TRUE);
+ } else {
+ gtk_widget_set_sensitive(w, FALSE);
+ }
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+
+}
+
+static void sp_item_properties(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ sp_desktop_selection(desktop)->set(item);
+
+ // sp_item_dialog();
+ desktop->_dlg_mgr->showDialog("ObjectProperties");
+}
+
+
+static void sp_set_mask(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ sp_selection_set_mask(desktop, false, false);
+}
+
+
+static void sp_release_mask(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ sp_selection_unset_mask(desktop, false);
+}
+
+
+static void sp_set_clip(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ sp_selection_set_mask(desktop, true, false);
+}
+
+
+static void sp_release_clip(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ sp_selection_unset_mask(desktop, true);
+}
+
+
+static void sp_item_select_this(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ sp_desktop_selection(desktop)->set(item);
+}
+
+static void sp_item_create_link(GtkMenuItem *menuitem, SPItem *item)
+{
+ g_assert(SP_IS_ITEM(item));
+ g_assert(!SP_IS_ANCHOR(item));
+
+ SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
+ Inkscape::XML::Node *repr = xml_doc->createElement("svg:a");
+ item->parent->getRepr()->addChild(repr, item->getRepr());
+ SPObject *object = item->document->getObjectByRepr(repr);
+ g_return_if_fail(SP_IS_ANCHOR(object));
+
+ const char *id = item->getRepr()->attribute("id");
+ Inkscape::XML::Node *child = item->getRepr()->duplicate(xml_doc);
+ item->deleteObject(false);
+ repr->addChild(child, NULL);
+ child->setAttribute("id", id);
+
+ Inkscape::GC::release(repr);
+ Inkscape::GC::release(child);
+
+ DocumentUndo::done(object->document, SP_VERB_NONE,
+ _("Create link"));
+
+ sp_desktop_selection(desktop)->set(SP_ITEM(object));
+ desktop->_dlg_mgr->showDialog("ObjectAttributes");
+}
+
+/* SPGroup */
+
+static void sp_item_group_ungroup_activate(GtkMenuItem *menuitem, SPGroup *group);
+
+static void sp_group_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu)
+{
+ SPItem *item=SP_ITEM(object);
+ GtkWidget *w;
+
+ /* "Ungroup" */
+ w = gtk_menu_item_new_with_mnemonic(_("_Ungroup"));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_group_ungroup_activate), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), w);
+}
+
+static void sp_item_group_ungroup_activate(GtkMenuItem *menuitem, SPGroup *group)
+{
+ SPDesktop *desktop;
+ GSList *children;
+
+ g_assert(SP_IS_GROUP(group));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ children = NULL;
+ sp_item_group_ungroup(group, &children);
+
+ sp_desktop_selection(desktop)->setList(children);
+ g_slist_free(children);
+}
+
+/* SPAnchor */
+
+static void sp_anchor_link_properties(GtkMenuItem *menuitem, SPAnchor *anchor);
+static void sp_anchor_link_follow(GtkMenuItem *menuitem, SPAnchor *anchor);
+static void sp_anchor_link_remove(GtkMenuItem *menuitem, SPAnchor *anchor);
+
+static void sp_anchor_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
+{
+ SPItem *item;
+ GtkWidget *w;
+
+ item = (SPItem *) object;
+
+ /* Link dialog */
+ w = gtk_menu_item_new_with_mnemonic(_("Link _Properties..."));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_properties), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ /* Select item */
+ w = gtk_menu_item_new_with_mnemonic(_("_Follow Link"));
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_follow), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ /* Reset transformations */
+ w = gtk_menu_item_new_with_mnemonic(_("_Remove Link"));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_remove), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+}
+
+static void sp_anchor_link_properties(GtkMenuItem *menuitem, SPAnchor */*anchor*/)
+{
+ SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+ desktop->_dlg_mgr->showDialog("ObjectAttributes");
+}
+
+static void sp_anchor_link_follow(GtkMenuItem */*menuitem*/, SPAnchor *anchor)
+{
+ g_return_if_fail(anchor != NULL);
+ g_return_if_fail(SP_IS_ANCHOR(anchor));
+
+ /* shell out to an external browser here */
+}
+
+static void sp_anchor_link_remove(GtkMenuItem */*menuitem*/, SPAnchor *anchor)
+{
+ GSList *children;
+
+ g_return_if_fail(anchor != NULL);
+ g_return_if_fail(SP_IS_ANCHOR(anchor));
+
+ children = NULL;
+ sp_item_group_ungroup(SP_GROUP(anchor), &children);
+
+ g_slist_free(children);
+}
+
+/* Image */
+
+static void sp_image_image_properties(GtkMenuItem *menuitem, SPAnchor *anchor);
+static void sp_image_image_edit(GtkMenuItem *menuitem, SPItem *item);
+static void sp_image_image_embed(GtkMenuItem *menuitem, SPItem *item);
+static void sp_image_image_extract(GtkMenuItem *menuitem, SPItem *item);
+
+static void sp_image_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
+{
+ SPItem *item = SP_ITEM(object);
+ GtkWidget *w;
+ Inkscape::XML::Node *ir = object->getRepr();
+ const gchar *href = ir->attribute("xlink:href");
+
+ /* Image properties */
+ w = gtk_menu_item_new_with_mnemonic(_("Image _Properties..."));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_properties), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+
+ /* Edit externally */
+ w = gtk_menu_item_new_with_mnemonic(_("Edit Externally..."));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_edit), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) {
+ gtk_widget_set_sensitive( w, FALSE );
+ }
+
+ /* Embed image */
+ if (Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" )) {
+ w = gtk_menu_item_new_with_mnemonic(C_("Context menu", "Embed Image"));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_embed), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) {
+ gtk_widget_set_sensitive( w, FALSE );
+ }
+ }
+
+ /* Extract image */
+ if (Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" )) {
+ w = gtk_menu_item_new_with_mnemonic(C_("Context menu", "Extract Image..."));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_extract), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+ if ( (!href) || ((strncmp(href, "data:", 5) != 0)) ) {
+ gtk_widget_set_sensitive( w, FALSE );
+ }
+ }
+}
+
+/* Image Properties entry */
+static void sp_image_image_properties(GtkMenuItem *menuitem, SPAnchor */*anchor*/)
+{
+ SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+ desktop->_dlg_mgr->showDialog("ObjectAttributes");
+}
+
+static gchar* getImageEditorName() {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ gchar* value = 0;
+ Glib::ustring choices = prefs->getString("/options/bitmapeditor/value");
+ if (!choices.empty()) {
+ value = g_strdup(choices.c_str());
+ }
+ if (!value) {
+ value = g_strdup("gimp");
+ }
+ return value;
+}
+
+/* Edit Externally entry */
+static void sp_image_image_edit(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop = NULL;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ if (sp_desktop_selection(desktop)->isEmpty()) {
+ sp_desktop_selection(desktop)->set(item);
+ }
+
+ GSList const *selected = sp_desktop_selection(desktop)->itemList();
+
+ GError* errThing = 0;
+ Glib::ustring cmdline = getImageEditorName();
+ Glib::ustring name;
+ Glib::ustring fullname;
+
+#ifdef WIN32
+ // g_spawn_command_line_sync parsing is done according to Unix shell rules,
+ // not Windows command interpreter rules. Thus we need to enclose the
+ // executable path with sigle quotes.
+ int index = cmdline.find(".exe");
+ if ( index < 0 ) index = cmdline.find(".bat");
+ if ( index < 0 ) index = cmdline.find(".com");
+ if ( index >= 0 ) {
+ Glib::ustring editorBin = cmdline.substr(0, index + 4).c_str();
+ Glib::ustring args = cmdline.substr(index + 4, cmdline.length()).c_str();
+ editorBin.insert(0, "'");
+ editorBin.append("'");
+ cmdline = editorBin;
+ cmdline.append(args);
+ } else {
+ // Enclose the whole command line if no executable path can be extracted.
+ cmdline.insert(0, "'");
+ cmdline.append("'");
+ }
+#endif
+
+ for (GSList const *iter = selected; iter != NULL; iter = iter->next) {
+ Inkscape::XML::Node *ir = SP_ITEM(iter->data)->getRepr();
+ const gchar *href = ir->attribute("xlink:href");
+
+ if (strncmp (href,"file:",5) == 0) {
+ // URI to filename conversion
+ name = g_filename_from_uri(href, NULL, NULL);
+ } else {
+ name.append(href);
+ }
+
+ if (Glib::path_is_absolute(name)) {
+ fullname = name;
+ } else if (SP_ACTIVE_DOCUMENT->getBase()) {
+ fullname = Glib::build_filename(SP_ACTIVE_DOCUMENT->getBase(), name);
+ } else {
+ fullname = Glib::build_filename(Glib::get_current_dir(), name);
+ }
+
+ cmdline.append(" '");
+ cmdline.append(fullname.c_str());
+ cmdline.append("'");
+ }
+
+ //g_warning("##Command line: %s\n", cmdline.c_str());
+
+ g_spawn_command_line_async(cmdline.c_str(),
+ &errThing);
+
+ if ( errThing ) {
+ g_warning("Problem launching editor (%d). %s", errThing->code, errThing->message);
+ SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, errThing->message);
+ g_error_free(errThing);
+ errThing = 0;
+ }
+}
+
+/* Embed Image entry */
+static void sp_image_image_embed(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ if (sp_desktop_selection(desktop)->isEmpty()) {
+ sp_desktop_selection(desktop)->set(item);
+ }
+
+ Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" );
+ if (verb) {
+ SPAction *action = verb->get_action(desktop);
+ if (action) {
+ sp_action_perform(action, NULL);
+ }
+ }
+}
+
+/* Extract Image entry */
+static void sp_image_image_extract(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ if (sp_desktop_selection(desktop)->isEmpty()) {
+ sp_desktop_selection(desktop)->set(item);
+ }
+
+ Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" );
+ if (verb) {
+ SPAction *action = verb->get_action(desktop);
+ if (action) {
+ sp_action_perform(action, NULL);
+ }
+ }
+}
+
+/* Fill and Stroke entry */
+static void sp_fill_settings(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ if (sp_desktop_selection(desktop)->isEmpty()) {
+ sp_desktop_selection(desktop)->set(item);
+ }
+
+ desktop->_dlg_mgr->showDialog("FillAndStroke");
+}
+
+/* SPShape */
+static void sp_shape_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
+{
+ SPItem *item;
+ GtkWidget *w;
+
+ item = (SPItem *) object;
+
+ /* Item dialog */
+ w = gtk_menu_item_new_with_mnemonic(_("_Fill and Stroke..."));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_fill_settings), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+}
+
+/* Edit Text entry */
+static void sp_text_settings(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ if (sp_desktop_selection(desktop)->isEmpty()) {
+ sp_desktop_selection(desktop)->set(item);
+ }
+
+ desktop->_dlg_mgr->showDialog("TextFont");
+}
+
+/* Spellcheck entry */
+static void sp_spellcheck_settings(GtkMenuItem *menuitem, SPItem *item)
+{
+ SPDesktop *desktop;
+
+ g_assert(SP_IS_ITEM(item));
+
+ desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
+ g_return_if_fail(desktop != NULL);
+
+ if (sp_desktop_selection(desktop)->isEmpty()) {
+ sp_desktop_selection(desktop)->set(item);
+ }
+
+ desktop->_dlg_mgr->showDialog("SpellCheck");
+}
+
+/* SPText */
+static void sp_text_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
+{
+ SPItem *item;
+ GtkWidget *w;
+
+ item = (SPItem *) object;
+
+ /* Fill and Stroke dialog */
+ w = gtk_menu_item_new_with_mnemonic(_("_Fill and Stroke..."));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_fill_settings), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+
+ /* Edit Text dialog */
+ w = gtk_menu_item_new_with_mnemonic(_("_Text and Font..."));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_text_settings), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+
+ /* Spellcheck dialog */
+ w = gtk_menu_item_new_with_mnemonic(_("Check Spellin_g..."));
+ g_object_set_data(G_OBJECT(w), "desktop", desktop);
+ g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_spellcheck_settings), item);
+ gtk_widget_show(w);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
+}
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert
index 91b1ccaf6..f8b95247a 100644
--- a/src/ui/dialog/Makefile_insert
+++ b/src/ui/dialog/Makefile_insert
@@ -8,6 +8,8 @@ ink_common_sources += \
ui/dialog/behavior.h \
ui/dialog/calligraphic-profile-rename.h \
ui/dialog/calligraphic-profile-rename.cpp \
+ ui/dialog/clonetiler.cpp \
+ ui/dialog/clonetiler.h \
ui/dialog/color-item.cpp \
ui/dialog/color-item.h \
ui/dialog/debug.cpp \
diff --git a/src/ui/dialog/clonetiler.cpp b/src/ui/dialog/clonetiler.cpp
new file mode 100644
index 000000000..36e79a682
--- /dev/null
+++ b/src/ui/dialog/clonetiler.cpp
@@ -0,0 +1,2833 @@
+
+/** @file
+ * Clone tiling dialog
+ */
+/* Authors:
+ * bulia byak <buliabyak@users.sf.net>
+ * Johan Engelen <goejendaagh@zonnet.nl>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Abhishek Sharma
+ * Romain de Bossoreille
+ *
+ * Copyright (C) 2004-2011 Authors
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "clonetiler.h"
+
+#include <climits>
+
+#include <glib.h>
+#include <glibmm/i18n.h>
+#include <2geom/transforms.h>
+#include <gtkmm/adjustment.h>
+
+#include "desktop.h"
+#include "desktop-handles.h"
+#include "display/cairo-utils.h"
+#include "display/drawing.h"
+#include "display/drawing-context.h"
+#include "display/drawing-item.h"
+#include "document.h"
+#include "document-undo.h"
+#include "filter-chemistry.h"
+#include "helper/unit-menu.h"
+#include "helper/units.h"
+#include "helper/window.h"
+#include "inkscape.h"
+#include "interface.h"
+#include "macros.h"
+#include "message-stack.h"
+#include "preferences.h"
+#include "selection.h"
+#include "sp-filter.h"
+#include "sp-namedview.h"
+#include "sp-use.h"
+#include "style.h"
+#include "svg/svg-color.h"
+#include "svg/svg.h"
+#include "ui/icon-names.h"
+#include "ui/widget/spinbutton.h"
+#include "unclump.h"
+#include "verbs.h"
+#include "widgets/icon.h"
+#include "xml/repr.h"
+#include "sp-root.h"
+
+using Inkscape::DocumentUndo;
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+#define SB_MARGIN 1
+#define VB_MARGIN 4
+
+static Glib::ustring const prefs_path = "/dialogs/clonetiler/";
+
+static Inkscape::Drawing *trace_drawing = NULL;
+static unsigned trace_visionkey;
+static gdouble trace_zoom;
+static SPDocument *trace_doc = NULL;
+
+
+CloneTiler::CloneTiler (void) :
+ UI::Widget::Panel ("", "/dialogs/clonetiler/", SP_VERB_DIALOG_CLONETILER),
+ dlg(NULL),
+ desktop(NULL),
+ deskTrack(),
+ table_row_labels(NULL),
+ selectChangedConn(),
+ subselChangedConn(),
+ selectModifiedConn()
+{
+ Gtk::Box *contents = _getContents();
+ contents->set_spacing(0);
+
+ {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ dlg = GTK_WIDGET(gobj());
+
+ GtkWidget *mainbox = gtk_vbox_new(FALSE, 4);
+ gtk_container_set_border_width (GTK_CONTAINER (mainbox), 6);
+
+ contents->pack_start (*Gtk::manage(Glib::wrap(mainbox)), true, true, 0);
+
+ GtkWidget *nb = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (mainbox), nb, FALSE, FALSE, 0);
+
+
+ // Symmetry
+ {
+ GtkWidget *vb = clonetiler_new_tab (nb, _("_Symmetry"));
+
+ /* TRANSLATORS: For the following 17 symmetry groups, see
+ * http://www.bib.ulb.ac.be/coursmath/doc/17.htm (visual examples);
+ * http://www.clarku.edu/~djoyce/wallpaper/seventeen.html (English vocabulary); or
+ * http://membres.lycos.fr/villemingerard/Geometri/Sym1D.htm (French vocabulary).
+ */
+ struct SymGroups {
+ gint group;
+ gchar const *label;
+ } const sym_groups[] = {
+ // TRANSLATORS: "translation" means "shift" / "displacement" here.
+ {TILE_P1, _("<b>P1</b>: simple translation")},
+ {TILE_P2, _("<b>P2</b>: 180&#176; rotation")},
+ {TILE_PM, _("<b>PM</b>: reflection")},
+ // TRANSLATORS: "glide reflection" is a reflection and a translation combined.
+ // For more info, see http://mathforum.org/sum95/suzanne/symsusan.html
+ {TILE_PG, _("<b>PG</b>: glide reflection")},
+ {TILE_CM, _("<b>CM</b>: reflection + glide reflection")},
+ {TILE_PMM, _("<b>PMM</b>: reflection + reflection")},
+ {TILE_PMG, _("<b>PMG</b>: reflection + 180&#176; rotation")},
+ {TILE_PGG, _("<b>PGG</b>: glide reflection + 180&#176; rotation")},
+ {TILE_CMM, _("<b>CMM</b>: reflection + reflection + 180&#176; rotation")},
+ {TILE_P4, _("<b>P4</b>: 90&#176; rotation")},
+ {TILE_P4M, _("<b>P4M</b>: 90&#176; rotation + 45&#176; reflection")},
+ {TILE_P4G, _("<b>P4G</b>: 90&#176; rotation + 90&#176; reflection")},
+ {TILE_P3, _("<b>P3</b>: 120&#176; rotation")},
+ {TILE_P31M, _("<b>P31M</b>: reflection + 120&#176; rotation, dense")},
+ {TILE_P3M1, _("<b>P3M1</b>: reflection + 120&#176; rotation, sparse")},
+ {TILE_P6, _("<b>P6</b>: 60&#176; rotation")},
+ {TILE_P6M, _("<b>P6M</b>: reflection + 60&#176; rotation")},
+ };
+
+ gint current = prefs->getInt(prefs_path + "symmetrygroup", 0);
+
+ // Create a list structure containing all the data to be displayed in
+ // the symmetry group combo box.
+ GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING);
+ GtkTreeIter iter;
+
+ for (unsigned j = 0; j < G_N_ELEMENTS(sym_groups); ++j) {
+ SymGroups const &sg = sym_groups[j];
+
+ // Add the description of the symgroup to a new row
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, sg.label, -1);
+ }
+
+ // Add a new combo box widget with the list of symmetry groups to the vbox
+ GtkWidget *combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
+ gtk_widget_set_tooltip_text (combo, _("Select one of the 17 symmetry groups for the tiling"));
+ gtk_box_pack_start (GTK_BOX (vb), combo, FALSE, FALSE, SB_MARGIN);
+
+ // Specify the rendering of data from the list in a combo box cell
+ GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
+ gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, "markup", 0, NULL);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), current);
+
+ g_signal_connect(G_OBJECT(combo), "changed",
+ G_CALLBACK(clonetiler_symgroup_changed), NULL);
+ }
+
+ table_row_labels = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+
+ // Shift
+ {
+ GtkWidget *vb = clonetiler_new_tab (nb, _("S_hift"));
+
+ GtkWidget *table = clonetiler_table_x_y_rand (3);
+ gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0);
+
+ // X
+ {
+ GtkWidget *l = gtk_label_new ("");
+ // TRANSLATORS: "shift" means: the tiles will be shifted (offset) horizontally by this amount
+ // xgettext:no-c-format
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>Shift X:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 2, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ // xgettext:no-c-format
+ _("Horizontal shift per row (in % of tile width)"), "shiftx_per_j",
+ -10000, 10000, "%");
+ clonetiler_table_attach (table, l, 0, 2, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ // xgettext:no-c-format
+ _("Horizontal shift per column (in % of tile width)"), "shiftx_per_i",
+ -10000, 10000, "%");
+ clonetiler_table_attach (table, l, 0, 2, 3);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Randomize the horizontal shift by this percentage"), "shiftx_rand",
+ 0, 1000, "%");
+ clonetiler_table_attach (table, l, 0, 2, 4);
+ }
+
+ // Y
+ {
+ GtkWidget *l = gtk_label_new ("");
+ // TRANSLATORS: "shift" means: the tiles will be shifted (offset) vertically by this amount
+ // xgettext:no-c-format
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>Shift Y:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 3, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ // xgettext:no-c-format
+ _("Vertical shift per row (in % of tile height)"), "shifty_per_j",
+ -10000, 10000, "%");
+ clonetiler_table_attach (table, l, 0, 3, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ // xgettext:no-c-format
+ _("Vertical shift per column (in % of tile height)"), "shifty_per_i",
+ -10000, 10000, "%");
+ clonetiler_table_attach (table, l, 0, 3, 3);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ _("Randomize the vertical shift by this percentage"), "shifty_rand",
+ 0, 1000, "%");
+ clonetiler_table_attach (table, l, 0, 3, 4);
+ }
+
+ // Exponent
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>Exponent:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 4, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ _("Whether rows are spaced evenly (1), converge (<1) or diverge (>1)"), "shifty_exp",
+ 0, 10, "", true);
+ clonetiler_table_attach (table, l, 0, 4, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ _("Whether columns are spaced evenly (1), converge (<1) or diverge (>1)"), "shiftx_exp",
+ 0, 10, "", true);
+ clonetiler_table_attach (table, l, 0, 4, 3);
+ }
+
+ { // alternates
+ GtkWidget *l = gtk_label_new ("");
+ // TRANSLATORS: "Alternate" is a verb here
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 5, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of shifts for each row"), "shifty_alternate");
+ clonetiler_table_attach (table, l, 0, 5, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of shifts for each column"), "shiftx_alternate");
+ clonetiler_table_attach (table, l, 0, 5, 3);
+ }
+
+ { // Cumulate
+ GtkWidget *l = gtk_label_new ("");
+ // TRANSLATORS: "Cumulate" is a verb here
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Cumulate:</small>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 6, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Cumulate the shifts for each row"), "shifty_cumulate");
+ clonetiler_table_attach (table, l, 0, 6, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Cumulate the shifts for each column"), "shiftx_cumulate");
+ clonetiler_table_attach (table, l, 0, 6, 3);
+ }
+
+ { // Exclude tile width and height in shift
+ GtkWidget *l = gtk_label_new ("");
+ // TRANSLATORS: "Cumulate" is a verb here
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Exclude tile:</small>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 7, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Exclude tile height in shift"), "shifty_excludeh");
+ clonetiler_table_attach (table, l, 0, 7, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Exclude tile width in shift"), "shiftx_excludew");
+ clonetiler_table_attach (table, l, 0, 7, 3);
+ }
+
+ }
+
+
+ // Scale
+ {
+ GtkWidget *vb = clonetiler_new_tab (nb, _("Sc_ale"));
+
+ GtkWidget *table = clonetiler_table_x_y_rand (2);
+ gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0);
+
+ // X
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>Scale X:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 2, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ // xgettext:no-c-format
+ _("Horizontal scale per row (in % of tile width)"), "scalex_per_j",
+ -100, 1000, "%");
+ clonetiler_table_attach (table, l, 0, 2, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ // xgettext:no-c-format
+ _("Horizontal scale per column (in % of tile width)"), "scalex_per_i",
+ -100, 1000, "%");
+ clonetiler_table_attach (table, l, 0, 2, 3);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Randomize the horizontal scale by this percentage"), "scalex_rand",
+ 0, 1000, "%");
+ clonetiler_table_attach (table, l, 0, 2, 4);
+ }
+
+ // Y
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>Scale Y:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 3, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ // xgettext:no-c-format
+ _("Vertical scale per row (in % of tile height)"), "scaley_per_j",
+ -100, 1000, "%");
+ clonetiler_table_attach (table, l, 0, 3, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ // xgettext:no-c-format
+ _("Vertical scale per column (in % of tile height)"), "scaley_per_i",
+ -100, 1000, "%");
+ clonetiler_table_attach (table, l, 0, 3, 3);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Randomize the vertical scale by this percentage"), "scaley_rand",
+ 0, 1000, "%");
+ clonetiler_table_attach (table, l, 0, 3, 4);
+ }
+
+ // Exponent
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>Exponent:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 4, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Whether row scaling is uniform (1), converge (<1) or diverge (>1)"), "scaley_exp",
+ 0, 10, "", true);
+ clonetiler_table_attach (table, l, 0, 4, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Whether column scaling is uniform (1), converge (<1) or diverge (>1)"), "scalex_exp",
+ 0, 10, "", true);
+ clonetiler_table_attach (table, l, 0, 4, 3);
+ }
+
+ // Logarithmic (as in logarithmic spiral)
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>Base:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 5, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Base for a logarithmic spiral: not used (0), converge (<1), or diverge (>1)"), "scaley_log",
+ 0, 10, "", false);
+ clonetiler_table_attach (table, l, 0, 5, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Base for a logarithmic spiral: not used (0), converge (<1), or diverge (>1)"), "scalex_log",
+ 0, 10, "", false);
+ clonetiler_table_attach (table, l, 0, 5, 3);
+ }
+
+ { // alternates
+ GtkWidget *l = gtk_label_new ("");
+ // TRANSLATORS: "Alternate" is a verb here
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 6, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of scales for each row"), "scaley_alternate");
+ clonetiler_table_attach (table, l, 0, 6, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of scales for each column"), "scalex_alternate");
+ clonetiler_table_attach (table, l, 0, 6, 3);
+ }
+
+ { // Cumulate
+ GtkWidget *l = gtk_label_new ("");
+ // TRANSLATORS: "Cumulate" is a verb here
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Cumulate:</small>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 7, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Cumulate the scales for each row"), "scaley_cumulate");
+ clonetiler_table_attach (table, l, 0, 7, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Cumulate the scales for each column"), "scalex_cumulate");
+ clonetiler_table_attach (table, l, 0, 7, 3);
+ }
+
+ }
+
+
+ // Rotation
+ {
+ GtkWidget *vb = clonetiler_new_tab (nb, _("_Rotation"));
+
+ GtkWidget *table = clonetiler_table_x_y_rand (1);
+ gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0);
+
+ // Angle
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>Angle:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 2, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ // xgettext:no-c-format
+ _("Rotate tiles by this angle for each row"), "rotate_per_j",
+ -180, 180, "&#176;");
+ clonetiler_table_attach (table, l, 0, 2, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (
+ // xgettext:no-c-format
+ _("Rotate tiles by this angle for each column"), "rotate_per_i",
+ -180, 180, "&#176;");
+ clonetiler_table_attach (table, l, 0, 2, 3);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Randomize the rotation angle by this percentage"), "rotate_rand",
+ 0, 100, "%");
+ clonetiler_table_attach (table, l, 0, 2, 4);
+ }
+
+ { // alternates
+ GtkWidget *l = gtk_label_new ("");
+ // TRANSLATORS: "Alternate" is a verb here
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 3, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the rotation direction for each row"), "rotate_alternatej");
+ clonetiler_table_attach (table, l, 0, 3, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the rotation direction for each column"), "rotate_alternatei");
+ clonetiler_table_attach (table, l, 0, 3, 3);
+ }
+
+ { // Cumulate
+ GtkWidget *l = gtk_label_new ("");
+ // TRANSLATORS: "Cumulate" is a verb here
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Cumulate:</small>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 4, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Cumulate the rotation for each row"), "rotate_cumulatej");
+ clonetiler_table_attach (table, l, 0, 4, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Cumulate the rotation for each column"), "rotate_cumulatei");
+ clonetiler_table_attach (table, l, 0, 4, 3);
+ }
+
+ }
+
+
+ // Blur and opacity
+ {
+ GtkWidget *vb = clonetiler_new_tab (nb, _("_Blur & opacity"));
+
+ GtkWidget *table = clonetiler_table_x_y_rand (1);
+ gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0);
+
+
+ // Blur
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>Blur:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 2, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Blur tiles by this percentage for each row"), "blur_per_j",
+ 0, 100, "%");
+ clonetiler_table_attach (table, l, 0, 2, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Blur tiles by this percentage for each column"), "blur_per_i",
+ 0, 100, "%");
+ clonetiler_table_attach (table, l, 0, 2, 3);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Randomize the tile blur by this percentage"), "blur_rand",
+ 0, 100, "%");
+ clonetiler_table_attach (table, l, 0, 2, 4);
+ }
+
+ { // alternates
+ GtkWidget *l = gtk_label_new ("");
+ // TRANSLATORS: "Alternate" is a verb here
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 3, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of blur change for each row"), "blur_alternatej");
+ clonetiler_table_attach (table, l, 0, 3, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of blur change for each column"), "blur_alternatei");
+ clonetiler_table_attach (table, l, 0, 3, 3);
+ }
+
+
+
+ // Dissolve
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>Opacity:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 4, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Decrease tile opacity by this percentage for each row"), "opacity_per_j",
+ 0, 100, "%");
+ clonetiler_table_attach (table, l, 0, 4, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Decrease tile opacity by this percentage for each column"), "opacity_per_i",
+ 0, 100, "%");
+ clonetiler_table_attach (table, l, 0, 4, 3);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Randomize the tile opacity by this percentage"), "opacity_rand",
+ 0, 100, "%");
+ clonetiler_table_attach (table, l, 0, 4, 4);
+ }
+
+ { // alternates
+ GtkWidget *l = gtk_label_new ("");
+ // TRANSLATORS: "Alternate" is a verb here
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 5, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of opacity change for each row"), "opacity_alternatej");
+ clonetiler_table_attach (table, l, 0, 5, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of opacity change for each column"), "opacity_alternatei");
+ clonetiler_table_attach (table, l, 0, 5, 3);
+ }
+ }
+
+
+ // Color
+ {
+ GtkWidget *vb = clonetiler_new_tab (nb, _("Co_lor"));
+
+ {
+ GtkWidget *hb = gtk_hbox_new (FALSE, 0);
+
+ GtkWidget *l = gtk_label_new (_("Initial color: "));
+ gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0);
+
+ guint32 rgba = 0x000000ff | sp_svg_read_color (prefs->getString(prefs_path + "initial_color").data(), 0x000000ff);
+ color_picker = new Inkscape::UI::Widget::ColorPicker (*new Glib::ustring(_("Initial color of tiled clones")), *new Glib::ustring(_("Initial color for clones (works only if the original has unset fill or stroke)")), rgba, false);
+ color_changed_connection = color_picker->connectChanged (sigc::ptr_fun(on_picker_color_changed));
+
+ gtk_box_pack_start (GTK_BOX (hb), reinterpret_cast<GtkWidget*>(color_picker->gobj()), FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0);
+ }
+
+
+ GtkWidget *table = clonetiler_table_x_y_rand (3);
+ gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0);
+
+ // Hue
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>H:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 2, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Change the tile hue by this percentage for each row"), "hue_per_j",
+ -100, 100, "%");
+ clonetiler_table_attach (table, l, 0, 2, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Change the tile hue by this percentage for each column"), "hue_per_i",
+ -100, 100, "%");
+ clonetiler_table_attach (table, l, 0, 2, 3);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Randomize the tile hue by this percentage"), "hue_rand",
+ 0, 100, "%");
+ clonetiler_table_attach (table, l, 0, 2, 4);
+ }
+
+
+ // Saturation
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>S:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 3, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Change the color saturation by this percentage for each row"), "saturation_per_j",
+ -100, 100, "%");
+ clonetiler_table_attach (table, l, 0, 3, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Change the color saturation by this percentage for each column"), "saturation_per_i",
+ -100, 100, "%");
+ clonetiler_table_attach (table, l, 0, 3, 3);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Randomize the color saturation by this percentage"), "saturation_rand",
+ 0, 100, "%");
+ clonetiler_table_attach (table, l, 0, 3, 4);
+ }
+
+ // Lightness
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<b>L:</b>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 4, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Change the color lightness by this percentage for each row"), "lightness_per_j",
+ -100, 100, "%");
+ clonetiler_table_attach (table, l, 0, 4, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Change the color lightness by this percentage for each column"), "lightness_per_i",
+ -100, 100, "%");
+ clonetiler_table_attach (table, l, 0, 4, 3);
+ }
+
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Randomize the color lightness by this percentage"), "lightness_rand",
+ 0, 100, "%");
+ clonetiler_table_attach (table, l, 0, 4, 4);
+ }
+
+
+ { // alternates
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>"));
+ gtk_size_group_add_widget(table_row_labels, l);
+ clonetiler_table_attach (table, l, 1, 5, 1);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of color changes for each row"), "color_alternatej");
+ clonetiler_table_attach (table, l, 0, 5, 2);
+ }
+
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of color changes for each column"), "color_alternatei");
+ clonetiler_table_attach (table, l, 0, 5, 3);
+ }
+
+ }
+
+ // Trace
+ {
+ GtkWidget *vb = clonetiler_new_tab (nb, _("_Trace"));
+
+
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN);
+ gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0);
+
+ GtkWidget *b = gtk_check_button_new_with_label (_("Trace the drawing under the tiles"));
+ g_object_set_data (G_OBJECT(b), "uncheckable", GINT_TO_POINTER(TRUE));
+ bool old = prefs->getBool(prefs_path + "dotrace");
+ gtk_toggle_button_set_active ((GtkToggleButton *) b, old);
+ gtk_widget_set_tooltip_text (b, _("For each clone, pick a value from the drawing in that clone's location and apply it to the clone"));
+ gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
+
+ g_signal_connect(G_OBJECT(b), "toggled",
+ G_CALLBACK(clonetiler_do_pick_toggled), (gpointer)dlg);
+ }
+
+ {
+ GtkWidget *vvb = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vb), vvb, FALSE, FALSE, 0);
+ g_object_set_data (G_OBJECT(dlg), "dotrace", (gpointer) vvb);
+
+
+ {
+ GtkWidget *frame = gtk_frame_new (_("1. Pick from the drawing:"));
+ gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, 0);
+
+ GtkWidget *table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add(GTK_CONTAINER(frame), table);
+
+
+ GtkWidget* radio;
+ {
+ radio = gtk_radio_button_new_with_label (NULL, _("Color"));
+ gtk_widget_set_tooltip_text (radio, _("Pick the visible color and opacity"));
+ clonetiler_table_attach (table, radio, 0.0, 1, 1);
+ g_signal_connect (G_OBJECT (radio), "toggled",
+ G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_COLOR));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_COLOR);
+ }
+ {
+ radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("Opacity"));
+ gtk_widget_set_tooltip_text (radio, _("Pick the total accumulated opacity"));
+ clonetiler_table_attach (table, radio, 0.0, 2, 1);
+ g_signal_connect (G_OBJECT (radio), "toggled",
+ G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_OPACITY));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_OPACITY);
+ }
+ {
+ radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("R"));
+ gtk_widget_set_tooltip_text (radio, _("Pick the Red component of the color"));
+ clonetiler_table_attach (table, radio, 0.0, 1, 2);
+ g_signal_connect (G_OBJECT (radio), "toggled",
+ G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_R));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_R);
+ }
+ {
+ radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("G"));
+ gtk_widget_set_tooltip_text (radio, _("Pick the Green component of the color"));
+ clonetiler_table_attach (table, radio, 0.0, 2, 2);
+ g_signal_connect (G_OBJECT (radio), "toggled",
+ G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_G));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_G);
+ }
+ {
+ radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("B"));
+ gtk_widget_set_tooltip_text (radio, _("Pick the Blue component of the color"));
+ clonetiler_table_attach (table, radio, 0.0, 3, 2);
+ g_signal_connect (G_OBJECT (radio), "toggled",
+ G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_B));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_B);
+ }
+ {
+ radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), C_("Clonetiler color hue", "H"));
+ gtk_widget_set_tooltip_text (radio, _("Pick the hue of the color"));
+ clonetiler_table_attach (table, radio, 0.0, 1, 3);
+ g_signal_connect (G_OBJECT (radio), "toggled",
+ G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_H));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_H);
+ }
+ {
+ radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), C_("Clonetiler color saturation", "S"));
+ gtk_widget_set_tooltip_text (radio, _("Pick the saturation of the color"));
+ clonetiler_table_attach (table, radio, 0.0, 2, 3);
+ g_signal_connect (G_OBJECT (radio), "toggled",
+ G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_S));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_S);
+ }
+ {
+ radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), C_("Clonetiler color lightness", "L"));
+ gtk_widget_set_tooltip_text (radio, _("Pick the lightness of the color"));
+ clonetiler_table_attach (table, radio, 0.0, 3, 3);
+ g_signal_connect (G_OBJECT (radio), "toggled",
+ G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_L));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_L);
+ }
+
+ }
+
+ {
+ GtkWidget *frame = gtk_frame_new (_("2. Tweak the picked value:"));
+ gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, VB_MARGIN);
+
+ GtkWidget *table = gtk_table_new (4, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add(GTK_CONTAINER(frame), table);
+
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("Gamma-correct:"));
+ clonetiler_table_attach (table, l, 1.0, 1, 1);
+ }
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Shift the mid-range of the picked value upwards (>0) or downwards (<0)"), "gamma_picked",
+ -10, 10, "");
+ clonetiler_table_attach (table, l, 0.0, 1, 2);
+ }
+
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("Randomize:"));
+ clonetiler_table_attach (table, l, 1.0, 1, 3);
+ }
+ {
+ GtkWidget *l = clonetiler_spinbox (_("Randomize the picked value by this percentage"), "rand_picked",
+ 0, 100, "%");
+ clonetiler_table_attach (table, l, 0.0, 1, 4);
+ }
+
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("Invert:"));
+ clonetiler_table_attach (table, l, 1.0, 2, 1);
+ }
+ {
+ GtkWidget *l = clonetiler_checkbox (_("Invert the picked value"), "invert_picked");
+ clonetiler_table_attach (table, l, 0.0, 2, 2);
+ }
+ }
+
+ {
+ GtkWidget *frame = gtk_frame_new (_("3. Apply the value to the clones':"));
+ gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, 0);
+
+
+ GtkWidget *table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add(GTK_CONTAINER(frame), table);
+
+ {
+ GtkWidget *b = gtk_check_button_new_with_label (_("Presence"));
+ bool old = prefs->getBool(prefs_path + "pick_to_presence", true);
+ gtk_toggle_button_set_active ((GtkToggleButton *) b, old);
+ gtk_widget_set_tooltip_text (b, _("Each clone is created with the probability determined by the picked value in that point"));
+ clonetiler_table_attach (table, b, 0.0, 1, 1);
+ g_signal_connect(G_OBJECT(b), "toggled",
+ G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_presence");
+ }
+
+ {
+ GtkWidget *b = gtk_check_button_new_with_label (_("Size"));
+ bool old = prefs->getBool(prefs_path + "pick_to_size");
+ gtk_toggle_button_set_active ((GtkToggleButton *) b, old);
+ gtk_widget_set_tooltip_text (b, _("Each clone's size is determined by the picked value in that point"));
+ clonetiler_table_attach (table, b, 0.0, 2, 1);
+ g_signal_connect(G_OBJECT(b), "toggled",
+ G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_size");
+ }
+
+ {
+ GtkWidget *b = gtk_check_button_new_with_label (_("Color"));
+ bool old = prefs->getBool(prefs_path + "pick_to_color", 0);
+ gtk_toggle_button_set_active ((GtkToggleButton *) b, old);
+ gtk_widget_set_tooltip_text (b, _("Each clone is painted by the picked color (the original must have unset fill or stroke)"));
+ clonetiler_table_attach (table, b, 0.0, 1, 2);
+ g_signal_connect(G_OBJECT(b), "toggled",
+ G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_color");
+ }
+
+ {
+ GtkWidget *b = gtk_check_button_new_with_label (_("Opacity"));
+ bool old = prefs->getBool(prefs_path + "pick_to_opacity", 0);
+ gtk_toggle_button_set_active ((GtkToggleButton *) b, old);
+ gtk_widget_set_tooltip_text (b, _("Each clone's opacity is determined by the picked value in that point"));
+ clonetiler_table_attach (table, b, 0.0, 2, 2);
+ g_signal_connect(G_OBJECT(b), "toggled",
+ G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_opacity");
+ }
+ }
+ gtk_widget_set_sensitive (vvb, prefs->getBool(prefs_path + "dotrace"));
+ }
+ }
+
+ // Rows/columns, width/height
+ {
+ GtkWidget *table = gtk_table_new (2, 2, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table), VB_MARGIN);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (mainbox), table, FALSE, FALSE, 0);
+
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN);
+ g_object_set_data (G_OBJECT(dlg), "rowscols", (gpointer) hb);
+
+ {
+ Gtk::Adjustment *a = new Gtk::Adjustment (0.0, 1, 500, 1, 10, 0);
+ int value = prefs->getInt(prefs_path + "jmax", 2);
+ a->set_value (value);
+
+ Inkscape::UI::Widget::SpinButton *sb = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 0);
+ sb->set_tooltip_text (_("How many rows in the tiling"));
+ sb->set_width_chars (7);
+ gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(sb->gobj()), TRUE, TRUE, 0);
+
+ // TODO: C++ification
+ g_signal_connect(G_OBJECT(a->gobj()), "value_changed",
+ G_CALLBACK(clonetiler_xy_changed), (gpointer) "jmax");
+ }
+
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), "&#215;");
+ gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0);
+ }
+
+ {
+ Gtk::Adjustment *a = new Gtk::Adjustment (0.0, 1, 500, 1, 10, 0);
+ int value = prefs->getInt(prefs_path + "imax", 2);
+ a->set_value (value);
+
+ Inkscape::UI::Widget::SpinButton *sb = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 0);
+ sb->set_tooltip_text (_("How many columns in the tiling"));
+ sb->set_width_chars (7);
+ gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(sb->gobj()), TRUE, TRUE, 0);
+
+ // TODO: C++ification
+ g_signal_connect(G_OBJECT(a->gobj()), "value_changed",
+ G_CALLBACK(clonetiler_xy_changed), (gpointer) "imax");
+ }
+
+ clonetiler_table_attach (table, hb, 0.0, 1, 2);
+ }
+
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN);
+ g_object_set_data (G_OBJECT(dlg), "widthheight", (gpointer) hb);
+
+ // unitmenu
+ GtkWidget *u = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
+ sp_unit_selector_set_unit (SP_UNIT_SELECTOR(u), sp_desktop_namedview(SP_ACTIVE_DESKTOP)->doc_units);
+
+ {
+ // Width spinbutton
+ Gtk::Adjustment *a = new Gtk::Adjustment (0.0, -1e6, 1e6, 1.0, 10.0, 0);
+ sp_unit_selector_add_adjustment (SP_UNIT_SELECTOR (u), GTK_ADJUSTMENT (a->gobj()));
+
+ double value = prefs->getDouble(prefs_path + "fillwidth", 50.0);
+ SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u));
+ gdouble const units = sp_pixels_get_units (value, unit);
+ a->set_value (units);
+
+ Inkscape::UI::Widget::SpinButton *e = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 2);
+ e->set_tooltip_text (_("Width of the rectangle to be filled"));
+ e->set_width_chars (7);
+ e->set_digits (4);
+ gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(e->gobj()), TRUE, TRUE, 0);
+ // TODO: C++ification
+ g_signal_connect(G_OBJECT(a->gobj()), "value_changed",
+ G_CALLBACK(clonetiler_fill_width_changed), u);
+ }
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), "&#215;");
+ gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0);
+ }
+
+ {
+ // Height spinbutton
+ Gtk::Adjustment *a = new Gtk::Adjustment (0.0, -1e6, 1e6, 1.0, 10.0, 0);
+ sp_unit_selector_add_adjustment (SP_UNIT_SELECTOR (u), GTK_ADJUSTMENT (a->gobj()));
+
+ double value = prefs->getDouble(prefs_path + "fillheight", 50.0);
+ SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u));
+ gdouble const units = sp_pixels_get_units (value, unit);
+ a->set_value (units);
+
+ Inkscape::UI::Widget::SpinButton *e = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 2);
+ e->set_tooltip_text (_("Height of the rectangle to be filled"));
+ e->set_width_chars (7);
+ e->set_digits (4);
+ gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(e->gobj()), TRUE, TRUE, 0);
+ // TODO: C++ification
+ g_signal_connect(G_OBJECT(a->gobj()), "value_changed",
+ G_CALLBACK(clonetiler_fill_height_changed), u);
+ }
+
+ gtk_box_pack_start (GTK_BOX (hb), u, TRUE, TRUE, 0);
+ clonetiler_table_attach (table, hb, 0.0, 2, 2);
+
+ }
+
+ // Switch
+ GtkWidget* radio;
+ {
+ radio = gtk_radio_button_new_with_label (NULL, _("Rows, columns: "));
+ gtk_widget_set_tooltip_text (radio, _("Create the specified number of rows and columns"));
+ clonetiler_table_attach (table, radio, 0.0, 1, 1);
+ g_signal_connect (G_OBJECT (radio), "toggled", G_CALLBACK (clonetiler_switch_to_create), (gpointer) dlg);
+ }
+ if (!prefs->getBool(prefs_path + "fillrect")) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE);
+ gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (radio));
+ }
+ {
+ radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("Width, height: "));
+ gtk_widget_set_tooltip_text (radio, _("Fill the specified width and height with the tiling"));
+ clonetiler_table_attach (table, radio, 0.0, 2, 1);
+ g_signal_connect (G_OBJECT (radio), "toggled", G_CALLBACK (clonetiler_switch_to_fill), (gpointer) dlg);
+ }
+ if (prefs->getBool(prefs_path + "fillrect")) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE);
+ gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (radio));
+ }
+ }
+
+
+ // Use saved pos
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN);
+ gtk_box_pack_start (GTK_BOX (mainbox), hb, FALSE, FALSE, 0);
+
+ GtkWidget *b = gtk_check_button_new_with_label (_("Use saved size and position of the tile"));
+ bool keepbbox = prefs->getBool(prefs_path + "keepbbox", true);
+ gtk_toggle_button_set_active ((GtkToggleButton *) b, keepbbox);
+ gtk_widget_set_tooltip_text (b, _("Pretend that the size and position of the tile are the same as the last time you tiled it (if any), instead of using the current size"));
+ gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
+
+ g_signal_connect(G_OBJECT(b), "toggled",
+ G_CALLBACK(clonetiler_keep_bbox_toggled), NULL);
+ }
+
+ // Statusbar
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN);
+ gtk_box_pack_end (GTK_BOX (mainbox), hb, FALSE, FALSE, 0);
+ GtkWidget *l = gtk_label_new("");
+ g_object_set_data (G_OBJECT(dlg), "status", (gpointer) l);
+ gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0);
+ }
+
+ // Buttons
+ {
+ GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN);
+ gtk_box_pack_start (GTK_BOX (mainbox), hb, FALSE, FALSE, 0);
+
+ {
+ GtkWidget *b = gtk_button_new ();
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Create</b> "));
+ gtk_container_add (GTK_CONTAINER(b), l);
+ gtk_widget_set_tooltip_text (b, _("Create and tile the clones of the selection"));
+ g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_apply), dlg);
+ gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
+ }
+
+ { // buttons which are enabled only when there are tiled clones
+ GtkWidget *sb = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (hb), sb, FALSE, FALSE, 0);
+ g_object_set_data (G_OBJECT(dlg), "buttons_on_tiles", (gpointer) sb);
+ {
+ // TRANSLATORS: if a group of objects are "clumped" together, then they
+ // are unevenly spread in the given amount of space - as shown in the
+ // diagrams on the left in the following screenshot:
+ // http://www.inkscape.org/screenshots/gallery/inkscape-0.42-CVS-tiles-unclump.png
+ // So unclumping is the process of spreading a number of objects out more evenly.
+ GtkWidget *b = gtk_button_new_with_mnemonic (_(" _Unclump "));
+ gtk_widget_set_tooltip_text (b, _("Spread out clones to reduce clumping; can be applied repeatedly"));
+ g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_unclump), NULL);
+ gtk_box_pack_end (GTK_BOX (sb), b, FALSE, FALSE, 0);
+ }
+
+ {
+ GtkWidget *b = gtk_button_new_with_mnemonic (_(" Re_move "));
+ gtk_widget_set_tooltip_text (b, _("Remove existing tiled clones of the selected object (siblings only)"));
+ g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_remove), gpointer(dlg));
+ gtk_box_pack_end (GTK_BOX (sb), b, FALSE, FALSE, 0);
+ }
+
+ // connect to global selection changed signal (so we can change desktops) and
+ // external_change (so we're not fooled by undo)
+ g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (clonetiler_change_selection), dlg);
+ g_signal_connect (G_OBJECT (INKSCAPE), "external_change", G_CALLBACK (clonetiler_external_change), dlg);
+ g_signal_connect(G_OBJECT(dlg), "destroy", G_CALLBACK(clonetiler_disconnect_gsignal), G_OBJECT (INKSCAPE));
+
+ // update now
+ clonetiler_change_selection (NULL, sp_desktop_selection(SP_ACTIVE_DESKTOP), dlg);
+ }
+
+ {
+ GtkWidget *b = gtk_button_new_with_mnemonic (_(" R_eset "));
+ // TRANSLATORS: "change" is a noun here
+ gtk_widget_set_tooltip_text (b, _("Reset all shifts, scales, rotates, opacity and color changes in the dialog to zero"));
+ g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_reset), dlg);
+ gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
+ }
+ }
+
+ gtk_widget_show_all (mainbox);
+
+ }
+
+ show_all();
+
+ desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &CloneTiler::setTargetDesktop) );
+ deskTrack.connect(GTK_WIDGET(gobj()));
+
+}
+
+CloneTiler::~CloneTiler (void)
+{
+ //subselChangedConn.disconnect();
+ //selectChangedConn.disconnect();
+ //selectModifiedConn.disconnect();
+ desktopChangeConn.disconnect();
+ deskTrack.disconnect();
+ color_changed_connection.disconnect();
+}
+
+void CloneTiler::setDesktop(SPDesktop *desktop)
+{
+ Panel::setDesktop(desktop);
+ deskTrack.setBase(desktop);
+}
+
+void CloneTiler::setTargetDesktop(SPDesktop *desktop)
+{
+ if (this->desktop != desktop) {
+ if (this->desktop) {
+ //selectModifiedConn.disconnect();
+ //subselChangedConn.disconnect();
+ //selectChangedConn.disconnect();
+ }
+ this->desktop = desktop;
+ if (desktop && desktop->selection) {
+ //selectChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(*this, &CloneTiler::clonetiler_change_selection)));
+ //subselChangedConn = desktop->connectToolSubselectionChanged(sigc::hide(sigc::mem_fun(*this, &CloneTiler::clonetiler_change_selection)));
+ //selectModifiedConn = desktop->selection->connectModified(sigc::hide<0>(sigc::mem_fun(*this, &CloneTiler::clonetiler_change_selection)));
+ }
+ }
+}
+
+void CloneTiler::on_picker_color_changed(guint rgba)
+{
+ static bool is_updating = false;
+ if (is_updating || !SP_ACTIVE_DESKTOP)
+ return;
+
+ is_updating = true;
+
+ gchar c[32];
+ sp_svg_write_color(c, sizeof(c), rgba);
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setString(prefs_path + "initial_color", c);
+
+ is_updating = false;
+}
+
+void CloneTiler::clonetiler_change_selection(Inkscape::Application * /*inkscape*/, Inkscape::Selection *selection, GtkWidget *dlg)
+{
+ GtkWidget *buttons = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "buttons_on_tiles");
+ GtkWidget *status = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "status");
+
+ if (selection->isEmpty()) {
+ gtk_widget_set_sensitive (buttons, FALSE);
+ gtk_label_set_markup (GTK_LABEL(status), _("<small>Nothing selected.</small>"));
+ return;
+ }
+
+ if (g_slist_length ((GSList *) selection->itemList()) > 1) {
+ gtk_widget_set_sensitive (buttons, FALSE);
+ gtk_label_set_markup (GTK_LABEL(status), _("<small>More than one object selected.</small>"));
+ return;
+ }
+
+ guint n = clonetiler_number_of_clones(selection->singleItem());
+ if (n > 0) {
+ gtk_widget_set_sensitive (buttons, TRUE);
+ gchar *sta = g_strdup_printf (_("<small>Object has <b>%d</b> tiled clones.</small>"), n);
+ gtk_label_set_markup (GTK_LABEL(status), sta);
+ g_free (sta);
+ } else {
+ gtk_widget_set_sensitive (buttons, FALSE);
+ gtk_label_set_markup (GTK_LABEL(status), _("<small>Object has no tiled clones.</small>"));
+ }
+}
+
+void CloneTiler::clonetiler_external_change(Inkscape::Application * /*inkscape*/, GtkWidget *dlg)
+{
+ clonetiler_change_selection (NULL, sp_desktop_selection(SP_ACTIVE_DESKTOP), dlg);
+}
+
+void CloneTiler::clonetiler_disconnect_gsignal(GObject *widget, gpointer source)
+{
+ if (source && G_IS_OBJECT(source)) {
+ sp_signal_disconnect_by_data (source, widget);
+ }
+}
+
+Geom::Affine CloneTiler::clonetiler_get_transform(
+ // symmetry group
+ int type,
+
+ // row, column
+ int i, int j,
+
+ // center, width, height of the tile
+ double cx, double cy,
+ double w, double h,
+
+ // values from the dialog:
+ // Shift
+ double shiftx_per_i, double shifty_per_i,
+ double shiftx_per_j, double shifty_per_j,
+ double shiftx_rand, double shifty_rand,
+ double shiftx_exp, double shifty_exp,
+ int shiftx_alternate, int shifty_alternate,
+ int shiftx_cumulate, int shifty_cumulate,
+ int shiftx_excludew, int shifty_excludeh,
+
+ // Scale
+ double scalex_per_i, double scaley_per_i,
+ double scalex_per_j, double scaley_per_j,
+ double scalex_rand, double scaley_rand,
+ double scalex_exp, double scaley_exp,
+ double scalex_log, double scaley_log,
+ int scalex_alternate, int scaley_alternate,
+ int scalex_cumulate, int scaley_cumulate,
+
+ // Rotation
+ double rotate_per_i, double rotate_per_j,
+ double rotate_rand,
+ int rotate_alternatei, int rotate_alternatej,
+ int rotate_cumulatei, int rotate_cumulatej
+ )
+{
+
+ // Shift (in units of tile width or height) -------------
+ double delta_shifti = 0.0;
+ double delta_shiftj = 0.0;
+
+ if( shiftx_alternate ) {
+ delta_shifti = (double)(i%2);
+ } else {
+ if( shiftx_cumulate ) { // Should the delta shifts be cumulative (i.e. 1, 1+2, 1+2+3, ...)
+ delta_shifti = (double)(i*i);
+ } else {
+ delta_shifti = (double)i;
+ }
+ }
+
+ if( shifty_alternate ) {
+ delta_shiftj = (double)(j%2);
+ } else {
+ if( shifty_cumulate ) {
+ delta_shiftj = (double)(j*j);
+ } else {
+ delta_shiftj = (double)j;
+ }
+ }
+
+ // Random shift, only calculate if non-zero.
+ double delta_shiftx_rand = 0.0;
+ double delta_shifty_rand = 0.0;
+ if( shiftx_rand != 0.0 ) delta_shiftx_rand = shiftx_rand * g_random_double_range (-1, 1);
+ if( shifty_rand != 0.0 ) delta_shifty_rand = shifty_rand * g_random_double_range (-1, 1);
+
+
+ // Delta shift (units of tile width/height)
+ double di = shiftx_per_i * delta_shifti + shiftx_per_j * delta_shiftj + delta_shiftx_rand;
+ double dj = shifty_per_i * delta_shifti + shifty_per_j * delta_shiftj + delta_shifty_rand;
+
+ // Shift in actual x and y, used below
+ double dx = w * di;
+ double dy = h * dj;
+
+ double shifti = di;
+ double shiftj = dj;
+
+ // Include tile width and height in shift if required
+ if( !shiftx_excludew ) shifti += i;
+ if( !shifty_excludeh ) shiftj += j;
+
+ // Add exponential shift if necessary
+ if ( shiftx_exp != 1.0 ) shifti = pow( shifti, shiftx_exp );
+ if ( shifty_exp != 1.0 ) shiftj = pow( shiftj, shifty_exp );
+
+ // Final shift
+ Geom::Affine rect_translate (Geom::Translate (w * shifti, h * shiftj));
+
+ // Rotation (in degrees) ------------
+ double delta_rotationi = 0.0;
+ double delta_rotationj = 0.0;
+
+ if( rotate_alternatei ) {
+ delta_rotationi = (double)(i%2);
+ } else {
+ if( rotate_cumulatei ) {
+ delta_rotationi = (double)(i*i + i)/2.0;
+ } else {
+ delta_rotationi = (double)i;
+ }
+ }
+
+ if( rotate_alternatej ) {
+ delta_rotationj = (double)(j%2);
+ } else {
+ if( rotate_cumulatej ) {
+ delta_rotationj = (double)(j*j + j)/2.0;
+ } else {
+ delta_rotationj = (double)j;
+ }
+ }
+
+ double delta_rotate_rand = 0.0;
+ if( rotate_rand != 0.0 ) delta_rotate_rand = rotate_rand * 180.0 * g_random_double_range (-1, 1);
+
+ double dr = rotate_per_i * delta_rotationi + rotate_per_j * delta_rotationj + delta_rotate_rand;
+
+ // Scale (times the original) -----------
+ double delta_scalei = 0.0;
+ double delta_scalej = 0.0;
+
+ if( scalex_alternate ) {
+ delta_scalei = (double)(i%2);
+ } else {
+ if( scalex_cumulate ) { // Should the delta scales be cumulative (i.e. 1, 1+2, 1+2+3, ...)
+ delta_scalei = (double)(i*i + i)/2.0;
+ } else {
+ delta_scalei = (double)i;
+ }
+ }
+
+ if( scaley_alternate ) {
+ delta_scalej = (double)(j%2);
+ } else {
+ if( scaley_cumulate ) {
+ delta_scalej = (double)(j*j + j)/2.0;
+ } else {
+ delta_scalej = (double)j;
+ }
+ }
+
+ // Random scale, only calculate if non-zero.
+ double delta_scalex_rand = 0.0;
+ double delta_scaley_rand = 0.0;
+ if( scalex_rand != 0.0 ) delta_scalex_rand = scalex_rand * g_random_double_range (-1, 1);
+ if( scaley_rand != 0.0 ) delta_scaley_rand = scaley_rand * g_random_double_range (-1, 1);
+ // But if random factors are same, scale x and y proportionally
+ if( scalex_rand == scaley_rand ) delta_scalex_rand = delta_scaley_rand;
+
+ // Total delta scale
+ double scalex = 1.0 + scalex_per_i * delta_scalei + scalex_per_j * delta_scalej + delta_scalex_rand;
+ double scaley = 1.0 + scaley_per_i * delta_scalei + scaley_per_j * delta_scalej + delta_scaley_rand;
+
+ if( scalex < 0.0 ) scalex = 0.0;
+ if( scaley < 0.0 ) scaley = 0.0;
+
+ // Add exponential scale if necessary
+ if ( scalex_exp != 1.0 ) scalex = pow( scalex, scalex_exp );
+ if ( scaley_exp != 1.0 ) scaley = pow( scaley, scaley_exp );
+
+ // Add logarithmic factor if necessary
+ if ( scalex_log > 0.0 ) scalex = pow( scalex_log, scalex - 1.0 );
+ if ( scaley_log > 0.0 ) scaley = pow( scaley_log, scaley - 1.0 );
+ // Alternative using rotation angle
+ //if ( scalex_log != 1.0 ) scalex *= pow( scalex_log, M_PI*dr/180 );
+ //if ( scaley_log != 1.0 ) scaley *= pow( scaley_log, M_PI*dr/180 );
+
+
+ // Calculate transformation matrices, translating back to "center of tile" (rotation center) before transforming
+ Geom::Affine drot_c = Geom::Translate(-cx, -cy) * Geom::Rotate (M_PI*dr/180) * Geom::Translate(cx, cy);
+
+ Geom::Affine dscale_c = Geom::Translate(-cx, -cy) * Geom::Scale (scalex, scaley) * Geom::Translate(cx, cy);
+
+ Geom::Affine d_s_r = dscale_c * drot_c;
+
+ Geom::Affine rotate_180_c = Geom::Translate(-cx, -cy) * Geom::Rotate (M_PI) * Geom::Translate(cx, cy);
+
+ Geom::Affine rotate_90_c = Geom::Translate(-cx, -cy) * Geom::Rotate (-M_PI/2) * Geom::Translate(cx, cy);
+ Geom::Affine rotate_m90_c = Geom::Translate(-cx, -cy) * Geom::Rotate ( M_PI/2) * Geom::Translate(cx, cy);
+
+ Geom::Affine rotate_120_c = Geom::Translate(-cx, -cy) * Geom::Rotate (-2*M_PI/3) * Geom::Translate(cx, cy);
+ Geom::Affine rotate_m120_c = Geom::Translate(-cx, -cy) * Geom::Rotate ( 2*M_PI/3) * Geom::Translate(cx, cy);
+
+ Geom::Affine rotate_60_c = Geom::Translate(-cx, -cy) * Geom::Rotate (-M_PI/3) * Geom::Translate(cx, cy);
+ Geom::Affine rotate_m60_c = Geom::Translate(-cx, -cy) * Geom::Rotate ( M_PI/3) * Geom::Translate(cx, cy);
+
+ Geom::Affine flip_x = Geom::Translate(-cx, -cy) * Geom::Scale (-1, 1) * Geom::Translate(cx, cy);
+ Geom::Affine flip_y = Geom::Translate(-cx, -cy) * Geom::Scale (1, -1) * Geom::Translate(cx, cy);
+
+
+ // Create tile with required symmetry
+ const double cos60 = cos(M_PI/3);
+ const double sin60 = sin(M_PI/3);
+ const double cos30 = cos(M_PI/6);
+ const double sin30 = sin(M_PI/6);
+
+ switch (type) {
+
+ case TILE_P1:
+ return d_s_r * rect_translate;
+ break;
+
+ case TILE_P2:
+ if (i % 2 == 0) {
+ return d_s_r * rect_translate;
+ } else {
+ return d_s_r * rotate_180_c * rect_translate;
+ }
+ break;
+
+ case TILE_PM:
+ if (i % 2 == 0) {
+ return d_s_r * rect_translate;
+ } else {
+ return d_s_r * flip_x * rect_translate;
+ }
+ break;
+
+ case TILE_PG:
+ if (j % 2 == 0) {
+ return d_s_r * rect_translate;
+ } else {
+ return d_s_r * flip_x * rect_translate;
+ }
+ break;
+
+ case TILE_CM:
+ if ((i + j) % 2 == 0) {
+ return d_s_r * rect_translate;
+ } else {
+ return d_s_r * flip_x * rect_translate;
+ }
+ break;
+
+ case TILE_PMM:
+ if (j % 2 == 0) {
+ if (i % 2 == 0) {
+ return d_s_r * rect_translate;
+ } else {
+ return d_s_r * flip_x * rect_translate;
+ }
+ } else {
+ if (i % 2 == 0) {
+ return d_s_r * flip_y * rect_translate;
+ } else {
+ return d_s_r * flip_x * flip_y * rect_translate;
+ }
+ }
+ break;
+
+ case TILE_PMG:
+ if (j % 2 == 0) {
+ if (i % 2 == 0) {
+ return d_s_r * rect_translate;
+ } else {
+ return d_s_r * rotate_180_c * rect_translate;
+ }
+ } else {
+ if (i % 2 == 0) {
+ return d_s_r * flip_y * rect_translate;
+ } else {
+ return d_s_r * rotate_180_c * flip_y * rect_translate;
+ }
+ }
+ break;
+
+ case TILE_PGG:
+ if (j % 2 == 0) {
+ if (i % 2 == 0) {
+ return d_s_r * rect_translate;
+ } else {
+ return d_s_r * flip_y * rect_translate;
+ }
+ } else {
+ if (i % 2 == 0) {
+ return d_s_r * rotate_180_c * rect_translate;
+ } else {
+ return d_s_r * rotate_180_c * flip_y * rect_translate;
+ }
+ }
+ break;
+
+ case TILE_CMM:
+ if (j % 4 == 0) {
+ if (i % 2 == 0) {
+ return d_s_r * rect_translate;
+ } else {
+ return d_s_r * flip_x * rect_translate;
+ }
+ } else if (j % 4 == 1) {
+ if (i % 2 == 0) {
+ return d_s_r * flip_y * rect_translate;
+ } else {
+ return d_s_r * flip_x * flip_y * rect_translate;
+ }
+ } else if (j % 4 == 2) {
+ if (i % 2 == 1) {
+ return d_s_r * rect_translate;
+ } else {
+ return d_s_r * flip_x * rect_translate;
+ }
+ } else {
+ if (i % 2 == 1) {
+ return d_s_r * flip_y * rect_translate;
+ } else {
+ return d_s_r * flip_x * flip_y * rect_translate;
+ }
+ }
+ break;
+
+ case TILE_P4:
+ {
+ Geom::Affine ori (Geom::Translate ((w + h) * pow((i/2), shiftx_exp) + dx, (h + w) * pow((j/2), shifty_exp) + dy));
+ Geom::Affine dia1 (Geom::Translate (w/2 + h/2, -h/2 + w/2));
+ Geom::Affine dia2 (Geom::Translate (-w/2 + h/2, h/2 + w/2));
+ if (j % 2 == 0) {
+ if (i % 2 == 0) {
+ return d_s_r * ori;
+ } else {
+ return d_s_r * rotate_m90_c * dia1 * ori;
+ }
+ } else {
+ if (i % 2 == 0) {
+ return d_s_r * rotate_90_c * dia2 * ori;
+ } else {
+ return d_s_r * rotate_180_c * dia1 * dia2 * ori;
+ }
+ }
+ }
+ break;
+
+ case TILE_P4M:
+ {
+ double max = MAX(w, h);
+ Geom::Affine ori (Geom::Translate ((max + max) * pow((i/4), shiftx_exp) + dx, (max + max) * pow((j/2), shifty_exp) + dy));
+ Geom::Affine dia1 (Geom::Translate ( w/2 - h/2, h/2 - w/2));
+ Geom::Affine dia2 (Geom::Translate (-h/2 + w/2, w/2 - h/2));
+ if (j % 2 == 0) {
+ if (i % 4 == 0) {
+ return d_s_r * ori;
+ } else if (i % 4 == 1) {
+ return d_s_r * flip_y * rotate_m90_c * dia1 * ori;
+ } else if (i % 4 == 2) {
+ return d_s_r * rotate_m90_c * dia1 * Geom::Translate (h, 0) * ori;
+ } else if (i % 4 == 3) {
+ return d_s_r * flip_x * Geom::Translate (w, 0) * ori;
+ }
+ } else {
+ if (i % 4 == 0) {
+ return d_s_r * flip_y * Geom::Translate(0, h) * ori;
+ } else if (i % 4 == 1) {
+ return d_s_r * rotate_90_c * dia2 * Geom::Translate(0, h) * ori;
+ } else if (i % 4 == 2) {
+ return d_s_r * flip_y * rotate_90_c * dia2 * Geom::Translate(h, 0) * Geom::Translate(0, h) * ori;
+ } else if (i % 4 == 3) {
+ return d_s_r * flip_y * flip_x * Geom::Translate(w, 0) * Geom::Translate(0, h) * ori;
+ }
+ }
+ }
+ break;
+
+ case TILE_P4G:
+ {
+ double max = MAX(w, h);
+ Geom::Affine ori (Geom::Translate ((max + max) * pow((i/4), shiftx_exp) + dx, (max + max) * pow(j, shifty_exp) + dy));
+ Geom::Affine dia1 (Geom::Translate ( w/2 + h/2, h/2 - w/2));
+ Geom::Affine dia2 (Geom::Translate (-h/2 + w/2, w/2 + h/2));
+ if (((i/4) + j) % 2 == 0) {
+ if (i % 4 == 0) {
+ return d_s_r * ori;
+ } else if (i % 4 == 1) {
+ return d_s_r * rotate_m90_c * dia1 * ori;
+ } else if (i % 4 == 2) {
+ return d_s_r * rotate_90_c * dia2 * ori;
+ } else if (i % 4 == 3) {
+ return d_s_r * rotate_180_c * dia1 * dia2 * ori;
+ }
+ } else {
+ if (i % 4 == 0) {
+ return d_s_r * flip_y * Geom::Translate (0, h) * ori;
+ } else if (i % 4 == 1) {
+ return d_s_r * flip_y * rotate_m90_c * dia1 * Geom::Translate (-h, 0) * ori;
+ } else if (i % 4 == 2) {
+ return d_s_r * flip_y * rotate_90_c * dia2 * Geom::Translate (h, 0) * ori;
+ } else if (i % 4 == 3) {
+ return d_s_r * flip_x * Geom::Translate (w, 0) * ori;
+ }
+ }
+ }
+ break;
+
+ case TILE_P3:
+ {
+ double width;
+ double height;
+ Geom::Affine dia1;
+ Geom::Affine dia2;
+ if (w > h) {
+ width = w + w * cos60;
+ height = 2 * w * sin60;
+ dia1 = Geom::Affine (Geom::Translate (w/2 + w/2 * cos60, -(w/2 * sin60)));
+ dia2 = dia1 * Geom::Affine (Geom::Translate (0, 2 * (w/2 * sin60)));
+ } else {
+ width = h * cos (M_PI/6);
+ height = h;
+ dia1 = Geom::Affine (Geom::Translate (h/2 * cos30, -(h/2 * sin30)));
+ dia2 = dia1 * Geom::Affine (Geom::Translate (0, h/2));
+ }
+ Geom::Affine ori (Geom::Translate (width * pow((2*(i/3) + j%2), shiftx_exp) + dx, (height/2) * pow(j, shifty_exp) + dy));
+ if (i % 3 == 0) {
+ return d_s_r * ori;
+ } else if (i % 3 == 1) {
+ return d_s_r * rotate_m120_c * dia1 * ori;
+ } else if (i % 3 == 2) {
+ return d_s_r * rotate_120_c * dia2 * ori;
+ }
+ }
+ break;
+
+ case TILE_P31M:
+ {
+ Geom::Affine ori;
+ Geom::Affine dia1;
+ Geom::Affine dia2;
+ Geom::Affine dia3;
+ Geom::Affine dia4;
+ if (w > h) {
+ ori = Geom::Affine(Geom::Translate (w * pow((i/6) + 0.5*(j%2), shiftx_exp) + dx, (w * cos30) * pow(j, shifty_exp) + dy));
+ dia1 = Geom::Affine (Geom::Translate (0, h/2) * Geom::Translate (w/2, 0) * Geom::Translate (w/2 * cos60, -w/2 * sin60) * Geom::Translate (-h/2 * cos30, -h/2 * sin30) );
+ dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
+ dia3 = dia2 * Geom::Affine (Geom::Translate (0, 2 * (w/2 * sin60 - h/2 * sin30)));
+ dia4 = dia3 * Geom::Affine (Geom::Translate (-h * cos30, h * sin30));
+ } else {
+ ori = Geom::Affine (Geom::Translate (2*h * cos30 * pow((i/6 + 0.5*(j%2)), shiftx_exp) + dx, (2*h - h * sin30) * pow(j, shifty_exp) + dy));
+ dia1 = Geom::Affine (Geom::Translate (0, -h/2) * Geom::Translate (h/2 * cos30, h/2 * sin30));
+ dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
+ dia3 = dia2 * Geom::Affine (Geom::Translate (0, h/2));
+ dia4 = dia3 * Geom::Affine (Geom::Translate (-h * cos30, h * sin30));
+ }
+ if (i % 6 == 0) {
+ return d_s_r * ori;
+ } else if (i % 6 == 1) {
+ return d_s_r * flip_y * rotate_m120_c * dia1 * ori;
+ } else if (i % 6 == 2) {
+ return d_s_r * rotate_m120_c * dia2 * ori;
+ } else if (i % 6 == 3) {
+ return d_s_r * flip_y * rotate_120_c * dia3 * ori;
+ } else if (i % 6 == 4) {
+ return d_s_r * rotate_120_c * dia4 * ori;
+ } else if (i % 6 == 5) {
+ return d_s_r * flip_y * Geom::Translate(0, h) * ori;
+ }
+ }
+ break;
+
+ case TILE_P3M1:
+ {
+ double width;
+ double height;
+ Geom::Affine dia1;
+ Geom::Affine dia2;
+ Geom::Affine dia3;
+ Geom::Affine dia4;
+ if (w > h) {
+ width = w + w * cos60;
+ height = 2 * w * sin60;
+ dia1 = Geom::Affine (Geom::Translate (0, h/2) * Geom::Translate (w/2, 0) * Geom::Translate (w/2 * cos60, -w/2 * sin60) * Geom::Translate (-h/2 * cos30, -h/2 * sin30) );
+ dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
+ dia3 = dia2 * Geom::Affine (Geom::Translate (0, 2 * (w/2 * sin60 - h/2 * sin30)));
+ dia4 = dia3 * Geom::Affine (Geom::Translate (-h * cos30, h * sin30));
+ } else {
+ width = 2 * h * cos (M_PI/6);
+ height = 2 * h;
+ dia1 = Geom::Affine (Geom::Translate (0, -h/2) * Geom::Translate (h/2 * cos30, h/2 * sin30));
+ dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
+ dia3 = dia2 * Geom::Affine (Geom::Translate (0, h/2));
+ dia4 = dia3 * Geom::Affine (Geom::Translate (-h * cos30, h * sin30));
+ }
+ Geom::Affine ori (Geom::Translate (width * pow((2*(i/6) + j%2), shiftx_exp) + dx, (height/2) * pow(j, shifty_exp) + dy));
+ if (i % 6 == 0) {
+ return d_s_r * ori;
+ } else if (i % 6 == 1) {
+ return d_s_r * flip_y * rotate_m120_c * dia1 * ori;
+ } else if (i % 6 == 2) {
+ return d_s_r * rotate_m120_c * dia2 * ori;
+ } else if (i % 6 == 3) {
+ return d_s_r * flip_y * rotate_120_c * dia3 * ori;
+ } else if (i % 6 == 4) {
+ return d_s_r * rotate_120_c * dia4 * ori;
+ } else if (i % 6 == 5) {
+ return d_s_r * flip_y * Geom::Translate(0, h) * ori;
+ }
+ }
+ break;
+
+ case TILE_P6:
+ {
+ Geom::Affine ori;
+ Geom::Affine dia1;
+ Geom::Affine dia2;
+ Geom::Affine dia3;
+ Geom::Affine dia4;
+ Geom::Affine dia5;
+ if (w > h) {
+ ori = Geom::Affine(Geom::Translate (w * pow((2*(i/6) + (j%2)), shiftx_exp) + dx, (2*w * sin60) * pow(j, shifty_exp) + dy));
+ dia1 = Geom::Affine (Geom::Translate (w/2 * cos60, -w/2 * sin60));
+ dia2 = dia1 * Geom::Affine (Geom::Translate (w/2, 0));
+ dia3 = dia2 * Geom::Affine (Geom::Translate (w/2 * cos60, w/2 * sin60));
+ dia4 = dia3 * Geom::Affine (Geom::Translate (-w/2 * cos60, w/2 * sin60));
+ dia5 = dia4 * Geom::Affine (Geom::Translate (-w/2, 0));
+ } else {
+ ori = Geom::Affine(Geom::Translate (2*h * cos30 * pow((i/6 + 0.5*(j%2)), shiftx_exp) + dx, (h + h * sin30) * pow(j, shifty_exp) + dy));
+ dia1 = Geom::Affine (Geom::Translate (-w/2, -h/2) * Geom::Translate (h/2 * cos30, -h/2 * sin30) * Geom::Translate (w/2 * cos60, w/2 * sin60));
+ dia2 = dia1 * Geom::Affine (Geom::Translate (-w/2 * cos60, -w/2 * sin60) * Geom::Translate (h/2 * cos30, -h/2 * sin30) * Geom::Translate (h/2 * cos30, h/2 * sin30) * Geom::Translate (-w/2 * cos60, w/2 * sin60));
+ dia3 = dia2 * Geom::Affine (Geom::Translate (w/2 * cos60, -w/2 * sin60) * Geom::Translate (h/2 * cos30, h/2 * sin30) * Geom::Translate (-w/2, h/2));
+ dia4 = dia3 * dia1.inverse();
+ dia5 = dia3 * dia2.inverse();
+ }
+ if (i % 6 == 0) {
+ return d_s_r * ori;
+ } else if (i % 6 == 1) {
+ return d_s_r * rotate_m60_c * dia1 * ori;
+ } else if (i % 6 == 2) {
+ return d_s_r * rotate_m120_c * dia2 * ori;
+ } else if (i % 6 == 3) {
+ return d_s_r * rotate_180_c * dia3 * ori;
+ } else if (i % 6 == 4) {
+ return d_s_r * rotate_120_c * dia4 * ori;
+ } else if (i % 6 == 5) {
+ return d_s_r * rotate_60_c * dia5 * ori;
+ }
+ }
+ break;
+
+ case TILE_P6M:
+ {
+
+ Geom::Affine ori;
+ Geom::Affine dia1, dia2, dia3, dia4, dia5, dia6, dia7, dia8, dia9, dia10;
+ if (w > h) {
+ ori = Geom::Affine(Geom::Translate (w * pow((2*(i/12) + (j%2)), shiftx_exp) + dx, (2*w * sin60) * pow(j, shifty_exp) + dy));
+ dia1 = Geom::Affine (Geom::Translate (w/2, h/2) * Geom::Translate (-w/2 * cos60, -w/2 * sin60) * Geom::Translate (-h/2 * cos30, h/2 * sin30));
+ dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, -h * sin30));
+ dia3 = dia2 * Geom::Affine (Geom::Translate (-h/2 * cos30, h/2 * sin30) * Geom::Translate (w * cos60, 0) * Geom::Translate (-h/2 * cos30, -h/2 * sin30));
+ dia4 = dia3 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
+ dia5 = dia4 * Geom::Affine (Geom::Translate (-h/2 * cos30, -h/2 * sin30) * Geom::Translate (-w/2 * cos60, w/2 * sin60) * Geom::Translate (w/2, -h/2));
+ dia6 = dia5 * Geom::Affine (Geom::Translate (0, h));
+ dia7 = dia6 * dia1.inverse();
+ dia8 = dia6 * dia2.inverse();
+ dia9 = dia6 * dia3.inverse();
+ dia10 = dia6 * dia4.inverse();
+ } else {
+ ori = Geom::Affine(Geom::Translate (4*h * cos30 * pow((i/12 + 0.5*(j%2)), shiftx_exp) + dx, (2*h + 2*h * sin30) * pow(j, shifty_exp) + dy));
+ dia1 = Geom::Affine (Geom::Translate (-w/2, -h/2) * Geom::Translate (h/2 * cos30, -h/2 * sin30) * Geom::Translate (w/2 * cos60, w/2 * sin60));
+ dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, -h * sin30));
+ dia3 = dia2 * Geom::Affine (Geom::Translate (-w/2 * cos60, -w/2 * sin60) * Geom::Translate (h * cos30, 0) * Geom::Translate (-w/2 * cos60, w/2 * sin60));
+ dia4 = dia3 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
+ dia5 = dia4 * Geom::Affine (Geom::Translate (w/2 * cos60, -w/2 * sin60) * Geom::Translate (h/2 * cos30, h/2 * sin30) * Geom::Translate (-w/2, h/2));
+ dia6 = dia5 * Geom::Affine (Geom::Translate (0, h));
+ dia7 = dia6 * dia1.inverse();
+ dia8 = dia6 * dia2.inverse();
+ dia9 = dia6 * dia3.inverse();
+ dia10 = dia6 * dia4.inverse();
+ }
+ if (i % 12 == 0) {
+ return d_s_r * ori;
+ } else if (i % 12 == 1) {
+ return d_s_r * flip_y * rotate_m60_c * dia1 * ori;
+ } else if (i % 12 == 2) {
+ return d_s_r * rotate_m60_c * dia2 * ori;
+ } else if (i % 12 == 3) {
+ return d_s_r * flip_y * rotate_m120_c * dia3 * ori;
+ } else if (i % 12 == 4) {
+ return d_s_r * rotate_m120_c * dia4 * ori;
+ } else if (i % 12 == 5) {
+ return d_s_r * flip_x * dia5 * ori;
+ } else if (i % 12 == 6) {
+ return d_s_r * flip_x * flip_y * dia6 * ori;
+ } else if (i % 12 == 7) {
+ return d_s_r * flip_y * rotate_120_c * dia7 * ori;
+ } else if (i % 12 == 8) {
+ return d_s_r * rotate_120_c * dia8 * ori;
+ } else if (i % 12 == 9) {
+ return d_s_r * flip_y * rotate_60_c * dia9 * ori;
+ } else if (i % 12 == 10) {
+ return d_s_r * rotate_60_c * dia10 * ori;
+ } else if (i % 12 == 11) {
+ return d_s_r * flip_y * Geom::Translate (0, h) * ori;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return Geom::identity();
+}
+
+bool CloneTiler::clonetiler_is_a_clone_of(SPObject *tile, SPObject *obj)
+{
+ bool result = false;
+ char *id_href = NULL;
+
+ if (obj) {
+ Inkscape::XML::Node *obj_repr = obj->getRepr();
+ id_href = g_strdup_printf("#%s", obj_repr->attribute("id"));
+ }
+
+ if (SP_IS_USE(tile) &&
+ tile->getRepr()->attribute("xlink:href") &&
+ (!id_href || !strcmp(id_href, tile->getRepr()->attribute("xlink:href"))) &&
+ tile->getRepr()->attribute("inkscape:tiled-clone-of") &&
+ (!id_href || !strcmp(id_href, tile->getRepr()->attribute("inkscape:tiled-clone-of"))))
+ {
+ result = true;
+ } else {
+ result = false;
+ }
+ if (id_href) {
+ g_free(id_href);
+ id_href = 0;
+ }
+ return result;
+}
+
+void CloneTiler::clonetiler_trace_hide_tiled_clones_recursively(SPObject *from)
+{
+ if (!trace_drawing)
+ return;
+
+ for (SPObject *o = from->firstChild(); o != NULL; o = o->next) {
+ if (SP_IS_ITEM(o) && clonetiler_is_a_clone_of (o, NULL))
+ SP_ITEM(o)->invoke_hide(trace_visionkey); // FIXME: hide each tiled clone's original too!
+ clonetiler_trace_hide_tiled_clones_recursively (o);
+ }
+}
+
+void CloneTiler::clonetiler_trace_setup(SPDocument *doc, gdouble zoom, SPItem *original)
+{
+ trace_drawing = new Inkscape::Drawing();
+ /* Create ArenaItem and set transform */
+ trace_visionkey = SPItem::display_key_new(1);
+ trace_doc = doc;
+ trace_drawing->setRoot(trace_doc->getRoot()->invoke_show(*trace_drawing, trace_visionkey, SP_ITEM_SHOW_DISPLAY));
+
+ // hide the (current) original and any tiled clones, we only want to pick the background
+ original->invoke_hide(trace_visionkey);
+ clonetiler_trace_hide_tiled_clones_recursively(trace_doc->getRoot());
+
+ trace_doc->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ trace_doc->ensureUpToDate();
+
+ trace_zoom = zoom;
+}
+
+guint32 CloneTiler::clonetiler_trace_pick(Geom::Rect box)
+{
+ if (!trace_drawing) {
+ return 0;
+ }
+
+ trace_drawing->root()->setTransform(Geom::Scale(trace_zoom));
+ trace_drawing->update();
+
+ /* Item integer bbox in points */
+ Geom::IntRect ibox = (box * Geom::Scale(trace_zoom)).roundOutwards();
+
+ /* Find visible area */
+ cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ibox.width(), ibox.height());
+ Inkscape::DrawingContext ct(s, ibox.min());
+ /* Render */
+ trace_drawing->render(ct, ibox);
+ double R = 0, G = 0, B = 0, A = 0;
+ ink_cairo_surface_average_color(s, R, G, B, A);
+ cairo_surface_destroy(s);
+
+ return SP_RGBA32_F_COMPOSE (R, G, B, A);
+}
+
+void CloneTiler::clonetiler_trace_finish()
+{
+ if (trace_doc) {
+ trace_doc->getRoot()->invoke_hide(trace_visionkey);
+ delete trace_drawing;
+ trace_doc = NULL;
+ trace_drawing = NULL;
+ }
+}
+
+void CloneTiler::clonetiler_unclump(GtkWidget */*widget*/, void *)
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop == NULL) {
+ return;
+ }
+
+ Inkscape::Selection *selection = sp_desktop_selection(desktop);
+
+ // check if something is selected
+ if (selection->isEmpty() || g_slist_length((GSList *) selection->itemList()) > 1) {
+ sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>one object</b> whose tiled clones to unclump."));
+ return;
+ }
+
+ SPObject *obj = selection->singleItem();
+ SPObject *parent = obj->parent;
+
+ GSList *to_unclump = NULL; // not including the original
+
+ for (SPObject *child = parent->firstChild(); child != NULL; child = child->next) {
+ if (clonetiler_is_a_clone_of (child, obj)) {
+ to_unclump = g_slist_prepend (to_unclump, child);
+ }
+ }
+
+ sp_desktop_document(desktop)->ensureUpToDate();
+
+ unclump (to_unclump);
+
+ g_slist_free (to_unclump);
+
+ DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_DIALOG_CLONETILER,
+ _("Unclump tiled clones"));
+}
+
+guint CloneTiler::clonetiler_number_of_clones(SPObject *obj)
+{
+ SPObject *parent = obj->parent;
+
+ guint n = 0;
+
+ for (SPObject *child = parent->firstChild(); child != NULL; child = child->next) {
+ if (clonetiler_is_a_clone_of (child, obj)) {
+ n ++;
+ }
+ }
+
+ return n;
+}
+
+void CloneTiler::clonetiler_remove(GtkWidget */*widget*/, GtkWidget *dlg, bool do_undo/* = true*/)
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop == NULL) {
+ return;
+ }
+
+ Inkscape::Selection *selection = sp_desktop_selection(desktop);
+
+ // check if something is selected
+ if (selection->isEmpty() || g_slist_length((GSList *) selection->itemList()) > 1) {
+ sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>one object</b> whose tiled clones to remove."));
+ return;
+ }
+
+ SPObject *obj = selection->singleItem();
+ SPObject *parent = obj->parent;
+
+// remove old tiling
+ GSList *to_delete = NULL;
+ for (SPObject *child = parent->firstChild(); child != NULL; child = child->next) {
+ if (clonetiler_is_a_clone_of (child, obj)) {
+ to_delete = g_slist_prepend (to_delete, child);
+ }
+ }
+ for (GSList *i = to_delete; i; i = i->next) {
+ SP_OBJECT(i->data)->deleteObject();
+ }
+ g_slist_free (to_delete);
+
+ clonetiler_change_selection (NULL, selection, dlg);
+
+ if (do_undo) {
+ DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_DIALOG_CLONETILER,
+ _("Delete tiled clones"));
+ }
+}
+
+Geom::Rect CloneTiler::transform_rect(Geom::Rect const &r, Geom::Affine const &m)
+{
+ using Geom::X;
+ using Geom::Y;
+ Geom::Point const p1 = r.corner(1) * m;
+ Geom::Point const p2 = r.corner(2) * m;
+ Geom::Point const p3 = r.corner(3) * m;
+ Geom::Point const p4 = r.corner(4) * m;
+ return Geom::Rect(
+ Geom::Point(
+ std::min(std::min(p1[X], p2[X]), std::min(p3[X], p4[X])),
+ std::min(std::min(p1[Y], p2[Y]), std::min(p3[Y], p4[Y]))),
+ Geom::Point(
+ std::max(std::max(p1[X], p2[X]), std::max(p3[X], p4[X])),
+ std::max(std::max(p1[Y], p2[Y]), std::max(p3[Y], p4[Y]))));
+}
+
+/**
+Randomizes \a val by \a rand, with 0 < val < 1 and all values (including 0, 1) having the same
+probability of being displaced.
+ */
+double CloneTiler::randomize01(double val, double rand)
+{
+ double base = MIN (val - rand, 1 - 2*rand);
+ if (base < 0) {
+ base = 0;
+ }
+ val = base + g_random_double_range (0, MIN (2 * rand, 1 - base));
+ return CLAMP(val, 0, 1); // this should be unnecessary with the above provisions, but just in case...
+}
+
+
+void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg)
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop == NULL) {
+ return;
+ }
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Inkscape::Selection *selection = sp_desktop_selection(desktop);
+
+ // check if something is selected
+ if (selection->isEmpty()) {
+ sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select an <b>object</b> to clone."));
+ return;
+ }
+
+ // Check if more than one object is selected.
+ if (g_slist_length((GSList *) selection->itemList()) > 1) {
+ sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("If you want to clone several objects, <b>group</b> them and <b>clone the group</b>."));
+ return;
+ }
+
+ // set "busy" cursor
+ desktop->setWaitingCursor();
+
+ // set statusbar text
+ GtkWidget *status = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "status");
+ gtk_label_set_markup (GTK_LABEL(status), _("<small>Creating tiled clones...</small>"));
+ gtk_widget_queue_draw(GTK_WIDGET(status));
+ gdk_window_process_all_updates();
+
+ SPObject *obj = selection->singleItem();
+ SPItem *item = SP_IS_ITEM(obj) ? SP_ITEM(obj) : 0;
+ Inkscape::XML::Node *obj_repr = obj->getRepr();
+ const char *id_href = g_strdup_printf("#%s", obj_repr->attribute("id"));
+ SPObject *parent = obj->parent;
+
+ clonetiler_remove (NULL, dlg, false);
+
+ double shiftx_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "shiftx_per_i", 0, -10000, 10000);
+ double shifty_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "shifty_per_i", 0, -10000, 10000);
+ double shiftx_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "shiftx_per_j", 0, -10000, 10000);
+ double shifty_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "shifty_per_j", 0, -10000, 10000);
+ double shiftx_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "shiftx_rand", 0, 0, 1000);
+ double shifty_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "shifty_rand", 0, 0, 1000);
+ double shiftx_exp = prefs->getDoubleLimited(prefs_path + "shiftx_exp", 1, 0, 10);
+ double shifty_exp = prefs->getDoubleLimited(prefs_path + "shifty_exp", 1, 0, 10);
+ bool shiftx_alternate = prefs->getBool(prefs_path + "shiftx_alternate");
+ bool shifty_alternate = prefs->getBool(prefs_path + "shifty_alternate");
+ bool shiftx_cumulate = prefs->getBool(prefs_path + "shiftx_cumulate");
+ bool shifty_cumulate = prefs->getBool(prefs_path + "shifty_cumulate");
+ bool shiftx_excludew = prefs->getBool(prefs_path + "shiftx_excludew");
+ bool shifty_excludeh = prefs->getBool(prefs_path + "shifty_excludeh");
+
+ double scalex_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "scalex_per_i", 0, -100, 1000);
+ double scaley_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "scaley_per_i", 0, -100, 1000);
+ double scalex_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "scalex_per_j", 0, -100, 1000);
+ double scaley_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "scaley_per_j", 0, -100, 1000);
+ double scalex_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "scalex_rand", 0, 0, 1000);
+ double scaley_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "scaley_rand", 0, 0, 1000);
+ double scalex_exp = prefs->getDoubleLimited(prefs_path + "scalex_exp", 1, 0, 10);
+ double scaley_exp = prefs->getDoubleLimited(prefs_path + "scaley_exp", 1, 0, 10);
+ double scalex_log = prefs->getDoubleLimited(prefs_path + "scalex_log", 0, 0, 10);
+ double scaley_log = prefs->getDoubleLimited(prefs_path + "scaley_log", 0, 0, 10);
+ bool scalex_alternate = prefs->getBool(prefs_path + "scalex_alternate");
+ bool scaley_alternate = prefs->getBool(prefs_path + "scaley_alternate");
+ bool scalex_cumulate = prefs->getBool(prefs_path + "scalex_cumulate");
+ bool scaley_cumulate = prefs->getBool(prefs_path + "scaley_cumulate");
+
+ double rotate_per_i = prefs->getDoubleLimited(prefs_path + "rotate_per_i", 0, -180, 180);
+ double rotate_per_j = prefs->getDoubleLimited(prefs_path + "rotate_per_j", 0, -180, 180);
+ double rotate_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "rotate_rand", 0, 0, 100);
+ bool rotate_alternatei = prefs->getBool(prefs_path + "rotate_alternatei");
+ bool rotate_alternatej = prefs->getBool(prefs_path + "rotate_alternatej");
+ bool rotate_cumulatei = prefs->getBool(prefs_path + "rotate_cumulatei");
+ bool rotate_cumulatej = prefs->getBool(prefs_path + "rotate_cumulatej");
+
+ double blur_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "blur_per_i", 0, 0, 100);
+ double blur_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "blur_per_j", 0, 0, 100);
+ bool blur_alternatei = prefs->getBool(prefs_path + "blur_alternatei");
+ bool blur_alternatej = prefs->getBool(prefs_path + "blur_alternatej");
+ double blur_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "blur_rand", 0, 0, 100);
+
+ double opacity_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "opacity_per_i", 0, 0, 100);
+ double opacity_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "opacity_per_j", 0, 0, 100);
+ bool opacity_alternatei = prefs->getBool(prefs_path + "opacity_alternatei");
+ bool opacity_alternatej = prefs->getBool(prefs_path + "opacity_alternatej");
+ double opacity_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "opacity_rand", 0, 0, 100);
+
+ Glib::ustring initial_color = prefs->getString(prefs_path + "initial_color");
+ double hue_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "hue_per_j", 0, -100, 100);
+ double hue_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "hue_per_i", 0, -100, 100);
+ double hue_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "hue_rand", 0, 0, 100);
+ double saturation_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "saturation_per_j", 0, -100, 100);
+ double saturation_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "saturation_per_i", 0, -100, 100);
+ double saturation_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "saturation_rand", 0, 0, 100);
+ double lightness_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "lightness_per_j", 0, -100, 100);
+ double lightness_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "lightness_per_i", 0, -100, 100);
+ double lightness_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "lightness_rand", 0, 0, 100);
+ bool color_alternatej = prefs->getBool(prefs_path + "color_alternatej");
+ bool color_alternatei = prefs->getBool(prefs_path + "color_alternatei");
+
+ int type = prefs->getInt(prefs_path + "symmetrygroup", 0);
+ bool keepbbox = prefs->getBool(prefs_path + "keepbbox", true);
+ int imax = prefs->getInt(prefs_path + "imax", 2);
+ int jmax = prefs->getInt(prefs_path + "jmax", 2);
+
+ bool fillrect = prefs->getBool(prefs_path + "fillrect");
+ double fillwidth = prefs->getDoubleLimited(prefs_path + "fillwidth", 50, 0, 1e6);
+ double fillheight = prefs->getDoubleLimited(prefs_path + "fillheight", 50, 0, 1e6);
+
+ bool dotrace = prefs->getBool(prefs_path + "dotrace");
+ int pick = prefs->getInt(prefs_path + "pick");
+ bool pick_to_presence = prefs->getBool(prefs_path + "pick_to_presence");
+ bool pick_to_size = prefs->getBool(prefs_path + "pick_to_size");
+ bool pick_to_color = prefs->getBool(prefs_path + "pick_to_color");
+ bool pick_to_opacity = prefs->getBool(prefs_path + "pick_to_opacity");
+ double rand_picked = 0.01 * prefs->getDoubleLimited(prefs_path + "rand_picked", 0, 0, 100);
+ bool invert_picked = prefs->getBool(prefs_path + "invert_picked");
+ double gamma_picked = prefs->getDoubleLimited(prefs_path + "gamma_picked", 0, -10, 10);
+
+ if (dotrace) {
+ clonetiler_trace_setup (sp_desktop_document(desktop), 1.0, item);
+ }
+
+ Geom::Point center;
+ double w = 0;
+ double h = 0;
+ double x0 = 0;
+ double y0 = 0;
+
+ if (keepbbox &&
+ obj_repr->attribute("inkscape:tile-w") &&
+ obj_repr->attribute("inkscape:tile-h") &&
+ obj_repr->attribute("inkscape:tile-x0") &&
+ obj_repr->attribute("inkscape:tile-y0") &&
+ obj_repr->attribute("inkscape:tile-cx") &&
+ obj_repr->attribute("inkscape:tile-cy")) {
+
+ double cx = 0;
+ double cy = 0;
+ sp_repr_get_double (obj_repr, "inkscape:tile-cx", &cx);
+ sp_repr_get_double (obj_repr, "inkscape:tile-cy", &cy);
+ center = Geom::Point (cx, cy);
+
+ sp_repr_get_double (obj_repr, "inkscape:tile-w", &w);
+ sp_repr_get_double (obj_repr, "inkscape:tile-h", &h);
+ sp_repr_get_double (obj_repr, "inkscape:tile-x0", &x0);
+ sp_repr_get_double (obj_repr, "inkscape:tile-y0", &y0);
+ } else {
+ bool prefs_bbox = prefs->getBool("/tools/bounding_box", false);
+ SPItem::BBoxType bbox_type = ( !prefs_bbox ?
+ SPItem::VISUAL_BBOX : SPItem::GEOMETRIC_BBOX );
+ Geom::OptRect r = item->documentBounds(bbox_type);
+ if (r) {
+ w = r->dimensions()[Geom::X];
+ h = r->dimensions()[Geom::Y];
+ x0 = r->min()[Geom::X];
+ y0 = r->min()[Geom::Y];
+ center = desktop->dt2doc(item->getCenter());
+
+ sp_repr_set_svg_double(obj_repr, "inkscape:tile-cx", center[Geom::X]);
+ sp_repr_set_svg_double(obj_repr, "inkscape:tile-cy", center[Geom::Y]);
+ sp_repr_set_svg_double(obj_repr, "inkscape:tile-w", w);
+ sp_repr_set_svg_double(obj_repr, "inkscape:tile-h", h);
+ sp_repr_set_svg_double(obj_repr, "inkscape:tile-x0", x0);
+ sp_repr_set_svg_double(obj_repr, "inkscape:tile-y0", y0);
+ } else {
+ center = Geom::Point(0, 0);
+ w = h = 0;
+ x0 = y0 = 0;
+ }
+ }
+
+ Geom::Point cur(0, 0);
+ Geom::Rect bbox_original (Geom::Point (x0, y0), Geom::Point (x0 + w, y0 + h));
+ double perimeter_original = (w + h)/4;
+
+ // The integers i and j are reserved for tile column and row.
+ // The doubles x and y are used for coordinates
+ for (int i = 0;
+ fillrect?
+ (fabs(cur[Geom::X]) < fillwidth && i < 200) // prevent "freezing" with too large fillrect, arbitrarily limit rows
+ : (i < imax);
+ i ++) {
+ for (int j = 0;
+ fillrect?
+ (fabs(cur[Geom::Y]) < fillheight && j < 200) // prevent "freezing" with too large fillrect, arbitrarily limit cols
+ : (j < jmax);
+ j ++) {
+
+ // Note: We create a clone at 0,0 too, right over the original, in case our clones are colored
+
+ // Get transform from symmetry, shift, scale, rotation
+ Geom::Affine t = clonetiler_get_transform (type, i, j, center[Geom::X], center[Geom::Y], w, h,
+ shiftx_per_i, shifty_per_i,
+ shiftx_per_j, shifty_per_j,
+ shiftx_rand, shifty_rand,
+ shiftx_exp, shifty_exp,
+ shiftx_alternate, shifty_alternate,
+ shiftx_cumulate, shifty_cumulate,
+ shiftx_excludew, shifty_excludeh,
+ scalex_per_i, scaley_per_i,
+ scalex_per_j, scaley_per_j,
+ scalex_rand, scaley_rand,
+ scalex_exp, scaley_exp,
+ scalex_log, scaley_log,
+ scalex_alternate, scaley_alternate,
+ scalex_cumulate, scaley_cumulate,
+ rotate_per_i, rotate_per_j,
+ rotate_rand,
+ rotate_alternatei, rotate_alternatej,
+ rotate_cumulatei, rotate_cumulatej );
+
+ cur = center * t - center;
+ if (fillrect) {
+ if ((cur[Geom::X] > fillwidth) || (cur[Geom::Y] > fillheight)) { // off limits
+ continue;
+ }
+ }
+
+ gchar color_string[32]; *color_string = 0;
+
+ // Color tab
+ if (!initial_color.empty()) {
+ guint32 rgba = sp_svg_read_color (initial_color.data(), 0x000000ff);
+ float hsl[3];
+ sp_color_rgb_to_hsl_floatv (hsl, SP_RGBA32_R_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_B_F(rgba));
+
+ double eff_i = (color_alternatei? (i%2) : (i));
+ double eff_j = (color_alternatej? (j%2) : (j));
+
+ hsl[0] += hue_per_i * eff_i + hue_per_j * eff_j + hue_rand * g_random_double_range (-1, 1);
+ double notused;
+ hsl[0] = modf( hsl[0], &notused ); // Restrict to 0-1
+ hsl[1] += saturation_per_i * eff_i + saturation_per_j * eff_j + saturation_rand * g_random_double_range (-1, 1);
+ hsl[1] = CLAMP (hsl[1], 0, 1);
+ hsl[2] += lightness_per_i * eff_i + lightness_per_j * eff_j + lightness_rand * g_random_double_range (-1, 1);
+ hsl[2] = CLAMP (hsl[2], 0, 1);
+
+ float rgb[3];
+ sp_color_hsl_to_rgb_floatv (rgb, hsl[0], hsl[1], hsl[2]);
+ sp_svg_write_color(color_string, sizeof(color_string), SP_RGBA32_F_COMPOSE(rgb[0], rgb[1], rgb[2], 1.0));
+ }
+
+ // Blur
+ double blur = 0.0;
+ {
+ int eff_i = (blur_alternatei? (i%2) : (i));
+ int eff_j = (blur_alternatej? (j%2) : (j));
+ blur = (blur_per_i * eff_i + blur_per_j * eff_j + blur_rand * g_random_double_range (-1, 1));
+ blur = CLAMP (blur, 0, 1);
+ }
+
+ // Opacity
+ double opacity = 1.0;
+ {
+ int eff_i = (opacity_alternatei? (i%2) : (i));
+ int eff_j = (opacity_alternatej? (j%2) : (j));
+ opacity = 1 - (opacity_per_i * eff_i + opacity_per_j * eff_j + opacity_rand * g_random_double_range (-1, 1));
+ opacity = CLAMP (opacity, 0, 1);
+ }
+
+ // Trace tab
+ if (dotrace) {
+ Geom::Rect bbox_t = transform_rect (bbox_original, t);
+
+ guint32 rgba = clonetiler_trace_pick (bbox_t);
+ float r = SP_RGBA32_R_F(rgba);
+ float g = SP_RGBA32_G_F(rgba);
+ float b = SP_RGBA32_B_F(rgba);
+ float a = SP_RGBA32_A_F(rgba);
+
+ float hsl[3];
+ sp_color_rgb_to_hsl_floatv (hsl, r, g, b);
+
+ gdouble val = 0;
+ switch (pick) {
+ case PICK_COLOR:
+ val = 1 - hsl[2]; // inverse lightness; to match other picks where black = max
+ break;
+ case PICK_OPACITY:
+ val = a;
+ break;
+ case PICK_R:
+ val = r;
+ break;
+ case PICK_G:
+ val = g;
+ break;
+ case PICK_B:
+ val = b;
+ break;
+ case PICK_H:
+ val = hsl[0];
+ break;
+ case PICK_S:
+ val = hsl[1];
+ break;
+ case PICK_L:
+ val = 1 - hsl[2];
+ break;
+ default:
+ break;
+ }
+
+ if (rand_picked > 0) {
+ val = randomize01 (val, rand_picked);
+ r = randomize01 (r, rand_picked);
+ g = randomize01 (g, rand_picked);
+ b = randomize01 (b, rand_picked);
+ }
+
+ if (gamma_picked != 0) {
+ double power;
+ if (gamma_picked > 0)
+ power = 1/(1 + fabs(gamma_picked));
+ else
+ power = 1 + fabs(gamma_picked);
+
+ val = pow (val, power);
+ r = pow (r, power);
+ g = pow (g, power);
+ b = pow (b, power);
+ }
+
+ if (invert_picked) {
+ val = 1 - val;
+ r = 1 - r;
+ g = 1 - g;
+ b = 1 - b;
+ }
+
+ val = CLAMP (val, 0, 1);
+ r = CLAMP (r, 0, 1);
+ g = CLAMP (g, 0, 1);
+ b = CLAMP (b, 0, 1);
+
+ // recompose tweaked color
+ rgba = SP_RGBA32_F_COMPOSE(r, g, b, a);
+
+ if (pick_to_presence) {
+ if (g_random_double_range (0, 1) > val) {
+ continue; // skip!
+ }
+ }
+ if (pick_to_size) {
+ t = Geom::Translate(-center[Geom::X], -center[Geom::Y]) * Geom::Scale (val, val) * Geom::Translate(center[Geom::X], center[Geom::Y]) * t;
+ }
+ if (pick_to_opacity) {
+ opacity *= val;
+ }
+ if (pick_to_color) {
+ sp_svg_write_color(color_string, sizeof(color_string), rgba);
+ }
+ }
+
+ if (opacity < 1e-6) { // invisibly transparent, skip
+ continue;
+ }
+
+ if (fabs(t[0]) + fabs (t[1]) + fabs(t[2]) + fabs(t[3]) < 1e-6) { // too small, skip
+ continue;
+ }
+
+ // Create the clone
+ Inkscape::XML::Node *clone = obj_repr->document()->createElement("svg:use");
+ clone->setAttribute("x", "0");
+ clone->setAttribute("y", "0");
+ clone->setAttribute("inkscape:tiled-clone-of", id_href);
+ clone->setAttribute("xlink:href", id_href);
+
+ Geom::Point new_center;
+ bool center_set = false;
+ if (obj_repr->attribute("inkscape:transform-center-x") || obj_repr->attribute("inkscape:transform-center-y")) {
+ new_center = desktop->dt2doc(item->getCenter()) * t;
+ center_set = true;
+ }
+
+ gchar *affinestr=sp_svg_transform_write(t);
+ clone->setAttribute("transform", affinestr);
+ g_free(affinestr);
+
+ if (opacity < 1.0) {
+ sp_repr_set_css_double(clone, "opacity", opacity);
+ }
+
+ if (*color_string) {
+ clone->setAttribute("fill", color_string);
+ clone->setAttribute("stroke", color_string);
+ }
+
+ // add the new clone to the top of the original's parent
+ parent->getRepr()->appendChild(clone);
+
+ if (blur > 0.0) {
+ SPObject *clone_object = sp_desktop_document(desktop)->getObjectByRepr(clone);
+ double perimeter = perimeter_original * t.descrim();
+ double radius = blur * perimeter;
+ // this is necessary for all newly added clones to have correct bboxes,
+ // otherwise filters won't work:
+ sp_desktop_document(desktop)->ensureUpToDate();
+ // it's hard to figure out exact width/height of the tile without having an object
+ // that we can take bbox of; however here we only need a lower bound so that blur
+ // margins are not too small, and the perimeter should work
+ SPFilter *constructed = new_filter_gaussian_blur(sp_desktop_document(desktop), radius, t.descrim(), t.expansionX(), t.expansionY(), perimeter, perimeter);
+ sp_style_set_property_url (clone_object, "filter", constructed, false);
+ }
+
+ if (center_set) {
+ SPObject *clone_object = sp_desktop_document(desktop)->getObjectByRepr(clone);
+ if (clone_object && SP_IS_ITEM(clone_object)) {
+ clone_object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ SP_ITEM(clone_object)->setCenter(desktop->doc2dt(new_center));
+ clone_object->updateRepr();
+ }
+ }
+
+ Inkscape::GC::release(clone);
+ }
+ cur[Geom::Y] = 0;
+ }
+
+ if (dotrace) {
+ clonetiler_trace_finish ();
+ }
+
+ clonetiler_change_selection (NULL, selection, dlg);
+
+ desktop->clearWaitingCursor();
+
+ DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_DIALOG_CLONETILER,
+ _("Create tiled clones"));
+}
+
+GtkWidget * CloneTiler::clonetiler_new_tab(GtkWidget *nb, const gchar *label)
+{
+ GtkWidget *l = gtk_label_new_with_mnemonic (label);
+ GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
+ gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
+ gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
+ return vb;
+}
+
+void CloneTiler::clonetiler_checkbox_toggled(GtkToggleButton *tb, gpointer *data)
+{
+ const gchar *attr = (const gchar *) data;
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setBool(prefs_path + attr, gtk_toggle_button_get_active(tb));
+}
+
+GtkWidget * CloneTiler::clonetiler_checkbox(const char *tip, const char *attr)
+{
+ GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN);
+
+ GtkWidget *b = gtk_check_button_new ();
+ gtk_widget_set_tooltip_text (b, tip);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool value = prefs->getBool(prefs_path + attr);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(b), value);
+
+ gtk_box_pack_end (GTK_BOX (hb), b, FALSE, TRUE, 0);
+ g_signal_connect ( G_OBJECT (b), "clicked",
+ G_CALLBACK (clonetiler_checkbox_toggled), (gpointer) attr);
+
+ g_object_set_data (G_OBJECT(b), "uncheckable", GINT_TO_POINTER(TRUE));
+
+ return hb;
+}
+
+void CloneTiler::clonetiler_value_changed(GtkAdjustment *adj, gpointer data)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ const gchar *pref = (const gchar *) data;
+ prefs->setDouble(prefs_path + pref, gtk_adjustment_get_value (adj));
+}
+
+GtkWidget * CloneTiler::clonetiler_spinbox(const char *tip, const char *attr, double lower, double upper, const gchar *suffix, bool exponent/* = false*/)
+{
+ GtkWidget *hb = gtk_hbox_new(FALSE, 0);
+
+ {
+ Gtk::Adjustment *a;
+ if (exponent) {
+ a = new Gtk::Adjustment (1.0, lower, upper, 0.01, 0.05, 0);
+ } else {
+ a = new Gtk::Adjustment (0.0, lower, upper, 0.1, 0.5, 0);
+ }
+
+ Inkscape::UI::Widget::SpinButton *sb;
+ if (exponent) {
+ sb = new Inkscape::UI::Widget::SpinButton (*a, 0.01, 2);
+ } else {
+ sb = new Inkscape::UI::Widget::SpinButton (*a, 0.1, 1);
+ }
+
+ sb->set_tooltip_text (tip);
+ sb->set_width_chars (5);
+ sb->set_digits(3);
+ gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(sb->gobj()), FALSE, FALSE, SB_MARGIN);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ double value = prefs->getDoubleLimited(prefs_path + attr, exponent? 1.0 : 0.0, lower, upper);
+ a->set_value (value);
+ // TODO: C++ification
+ g_signal_connect(G_OBJECT(a->gobj()), "value_changed",
+ G_CALLBACK(clonetiler_value_changed), (gpointer) attr);
+
+ if (exponent) {
+ sb->set_data ("oneable", GINT_TO_POINTER(TRUE));
+ } else {
+ sb->set_data ("zeroable", GINT_TO_POINTER(TRUE));
+ }
+ }
+
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), suffix);
+ gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0);
+ gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0);
+ }
+
+ return hb;
+}
+
+void CloneTiler::clonetiler_symgroup_changed(GtkComboBox *cb, gpointer /*data*/)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ gint group_new = gtk_combo_box_get_active (cb);
+ prefs->setInt(prefs_path + "symmetrygroup", group_new);
+}
+
+void CloneTiler::clonetiler_xy_changed(GtkAdjustment *adj, gpointer data)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ const gchar *pref = (const gchar *) data;
+ prefs->setInt(prefs_path + pref, (int) floor(gtk_adjustment_get_value (adj) + 0.5));
+}
+
+void CloneTiler::clonetiler_keep_bbox_toggled(GtkToggleButton *tb, gpointer /*data*/)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setBool(prefs_path + "keepbbox", gtk_toggle_button_get_active(tb));
+}
+
+void CloneTiler::clonetiler_pick_to(GtkToggleButton *tb, gpointer data)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ const gchar *pref = (const gchar *) data;
+ prefs->setBool(prefs_path + pref, gtk_toggle_button_get_active(tb));
+}
+
+
+void CloneTiler::clonetiler_reset_recursive(GtkWidget *w)
+{
+ if (w && GTK_IS_OBJECT(w)) {
+ {
+ int r = GPOINTER_TO_INT (g_object_get_data(G_OBJECT(w), "zeroable"));
+ if (r && GTK_IS_SPIN_BUTTON(w)) { // spinbutton
+ GtkAdjustment *a = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(w));
+ gtk_adjustment_set_value (a, 0);
+ }
+ }
+ {
+ int r = GPOINTER_TO_INT (g_object_get_data(G_OBJECT(w), "oneable"));
+ if (r && GTK_IS_SPIN_BUTTON(w)) { // spinbutton
+ GtkAdjustment *a = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(w));
+ gtk_adjustment_set_value (a, 1);
+ }
+ }
+ {
+ int r = GPOINTER_TO_INT (g_object_get_data(G_OBJECT(w), "uncheckable"));
+ if (r && GTK_IS_TOGGLE_BUTTON(w)) { // checkbox
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), FALSE);
+ }
+ }
+ }
+
+ if (GTK_IS_CONTAINER(w)) {
+ GList *ch = gtk_container_get_children (GTK_CONTAINER(w));
+ for (GList *i = ch; i != NULL; i = i->next) {
+ clonetiler_reset_recursive (GTK_WIDGET(i->data));
+ }
+ g_list_free (ch);
+ }
+}
+
+void CloneTiler::clonetiler_reset(GtkWidget */*widget*/, GtkWidget *dlg)
+{
+ clonetiler_reset_recursive (dlg);
+}
+
+void CloneTiler::clonetiler_table_attach(GtkWidget *table, GtkWidget *widget, float align, int row, int col)
+{
+ GtkWidget *a = gtk_alignment_new (align, 0, 0, 0);
+ gtk_container_add(GTK_CONTAINER(a), widget);
+ gtk_table_attach ( GTK_TABLE (table), a, col, col + 1, row, row + 1, (GtkAttachOptions)4, (GtkAttachOptions)0, 0, 0 );
+}
+
+GtkWidget * CloneTiler::clonetiler_table_x_y_rand(int values)
+{
+ GtkWidget *table = gtk_table_new (values + 2, 5, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table), VB_MARGIN);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 8);
+
+ {
+ GtkWidget *hb = gtk_hbox_new (FALSE, 0);
+
+ GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_DECORATION, INKSCAPE_ICON("object-rows"));
+ gtk_box_pack_start (GTK_BOX (hb), i, FALSE, FALSE, 2);
+
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Per row:</small>"));
+ gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 2);
+
+ clonetiler_table_attach (table, hb, 0, 1, 2);
+ }
+
+ {
+ GtkWidget *hb = gtk_hbox_new (FALSE, 0);
+
+ GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_DECORATION, INKSCAPE_ICON("object-columns"));
+ gtk_box_pack_start (GTK_BOX (hb), i, FALSE, FALSE, 2);
+
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Per column:</small>"));
+ gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 2);
+
+ clonetiler_table_attach (table, hb, 0, 1, 3);
+ }
+
+ {
+ GtkWidget *l = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL(l), _("<small>Randomize:</small>"));
+ clonetiler_table_attach (table, l, 0, 1, 4);
+ }
+
+ return table;
+}
+
+void CloneTiler::clonetiler_pick_switched(GtkToggleButton */*tb*/, gpointer data)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ guint v = GPOINTER_TO_INT (data);
+ prefs->setInt(prefs_path + "pick", v);
+}
+
+
+void CloneTiler::clonetiler_switch_to_create(GtkToggleButton */*tb*/, GtkWidget *dlg)
+{
+ GtkWidget *rowscols = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "rowscols");
+ GtkWidget *widthheight = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "widthheight");
+
+ if (rowscols) {
+ gtk_widget_set_sensitive (rowscols, TRUE);
+ }
+ if (widthheight) {
+ gtk_widget_set_sensitive (widthheight, FALSE);
+ }
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setBool(prefs_path + "fillrect", false);
+}
+
+
+void CloneTiler::clonetiler_switch_to_fill(GtkToggleButton */*tb*/, GtkWidget *dlg)
+{
+ GtkWidget *rowscols = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "rowscols");
+ GtkWidget *widthheight = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "widthheight");
+
+ if (rowscols) {
+ gtk_widget_set_sensitive (rowscols, FALSE);
+ }
+ if (widthheight) {
+ gtk_widget_set_sensitive (widthheight, TRUE);
+ }
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setBool(prefs_path + "fillrect", true);
+}
+
+
+
+
+void CloneTiler::clonetiler_fill_width_changed(GtkAdjustment *adj, GtkWidget *u)
+{
+ gdouble const raw_dist = gtk_adjustment_get_value (adj);
+ SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u));
+ gdouble const pixels = sp_units_get_pixels (raw_dist, unit);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setDouble(prefs_path + "fillwidth", pixels);
+}
+
+void CloneTiler::clonetiler_fill_height_changed(GtkAdjustment *adj, GtkWidget *u)
+{
+ gdouble const raw_dist = gtk_adjustment_get_value (adj);
+ SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u));
+ gdouble const pixels = sp_units_get_pixels (raw_dist, unit);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setDouble(prefs_path + "fillheight", pixels);
+}
+
+
+void CloneTiler::clonetiler_do_pick_toggled(GtkToggleButton *tb, GtkWidget *dlg)
+{
+ GtkWidget *vvb = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "dotrace");
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setBool(prefs_path + "dotrace", gtk_toggle_button_get_active (tb));
+
+ if (vvb) {
+ gtk_widget_set_sensitive (vvb, gtk_toggle_button_get_active (tb));
+ }
+}
+
+
+}
+}
+}
+
+
+/*
+ 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/clonetiler.h b/src/ui/dialog/clonetiler.h
new file mode 100644
index 000000000..2cad30e9e
--- /dev/null
+++ b/src/ui/dialog/clonetiler.h
@@ -0,0 +1,183 @@
+/** @file
+ * @brief Clone tiling dialog
+ */
+/* Authors:
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 2004 Authors
+ * Released under the GNU GPL, read the file 'COPYING' for more information
+ */
+#ifndef __SP_CLONE_TILER_H__
+#define __SP_CLONE_TILER_H__
+
+#include "ui/widget/panel.h"
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "ui/dialog/desktop-tracker.h"
+#include "ui/widget/color-picker.h"
+#include "sp-root.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+class CloneTiler : public Widget::Panel {
+public:
+ CloneTiler();
+ virtual ~CloneTiler();
+
+ static CloneTiler &getInstance() { return *new CloneTiler(); }
+
+protected:
+
+ GtkWidget * clonetiler_new_tab(GtkWidget *nb, const gchar *label);
+ GtkWidget * clonetiler_table_x_y_rand(int values);
+ GtkWidget * clonetiler_spinbox(const char *tip, const char *attr, double lower, double upper, const gchar *suffix, bool exponent = false);
+ GtkWidget * clonetiler_checkbox(const char *tip, const char *attr);
+ void clonetiler_table_attach(GtkWidget *table, GtkWidget *widget, float align, int row, int col);
+
+ static void clonetiler_symgroup_changed(GtkComboBox *cb, gpointer /*data*/);
+ static void clonetiler_remove(GtkWidget */*widget*/, GtkWidget *dlg, bool do_undo = true);
+ static void on_picker_color_changed(guint rgba);
+ static void clonetiler_trace_hide_tiled_clones_recursively(SPObject *from);
+ static void clonetiler_checkbox_toggled(GtkToggleButton *tb, gpointer *data);
+ static void clonetiler_pick_switched(GtkToggleButton */*tb*/, gpointer data);
+ static void clonetiler_do_pick_toggled(GtkToggleButton *tb, GtkWidget *dlg);
+ static void clonetiler_pick_to(GtkToggleButton *tb, gpointer data);
+ static void clonetiler_xy_changed(GtkAdjustment *adj, gpointer data);
+ static void clonetiler_fill_width_changed(GtkAdjustment *adj, GtkWidget *u);
+ static void clonetiler_fill_height_changed(GtkAdjustment *adj, GtkWidget *u);
+ static void clonetiler_switch_to_create(GtkToggleButton */*tb*/, GtkWidget *dlg);
+ static void clonetiler_switch_to_fill(GtkToggleButton */*tb*/, GtkWidget *dlg);
+ static void clonetiler_keep_bbox_toggled(GtkToggleButton *tb, gpointer /*data*/);
+ static void clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg);
+ static void clonetiler_unclump(GtkWidget */*widget*/, void *);
+ static void clonetiler_change_selection(Inkscape::Application * /*inkscape*/, Inkscape::Selection *selection, GtkWidget *dlg);
+ static void clonetiler_external_change(Inkscape::Application * /*inkscape*/, GtkWidget *dlg);
+ static void clonetiler_disconnect_gsignal(GObject *widget, gpointer source);
+ static void clonetiler_reset(GtkWidget */*widget*/, GtkWidget *dlg);
+ static guint clonetiler_number_of_clones(SPObject *obj);
+ static void clonetiler_trace_setup(SPDocument *doc, gdouble zoom, SPItem *original);
+ static guint32 clonetiler_trace_pick(Geom::Rect box);
+ static void clonetiler_trace_finish();
+ static bool clonetiler_is_a_clone_of(SPObject *tile, SPObject *obj);
+ static Geom::Rect transform_rect(Geom::Rect const &r, Geom::Affine const &m);
+ static double randomize01(double val, double rand);
+ static void clonetiler_value_changed(GtkAdjustment *adj, gpointer data);
+ static void clonetiler_reset_recursive(GtkWidget *w);
+
+ static Geom::Affine clonetiler_get_transform( // symmetry group
+ int type,
+
+ // row, column
+ int i, int j,
+
+ // center, width, height of the tile
+ double cx, double cy,
+ double w, double h,
+
+ // values from the dialog:
+ // Shift
+ double shiftx_per_i, double shifty_per_i,
+ double shiftx_per_j, double shifty_per_j,
+ double shiftx_rand, double shifty_rand,
+ double shiftx_exp, double shifty_exp,
+ int shiftx_alternate, int shifty_alternate,
+ int shiftx_cumulate, int shifty_cumulate,
+ int shiftx_excludew, int shifty_excludeh,
+
+ // Scale
+ double scalex_per_i, double scaley_per_i,
+ double scalex_per_j, double scaley_per_j,
+ double scalex_rand, double scaley_rand,
+ double scalex_exp, double scaley_exp,
+ double scalex_log, double scaley_log,
+ int scalex_alternate, int scaley_alternate,
+ int scalex_cumulate, int scaley_cumulate,
+
+ // Rotation
+ double rotate_per_i, double rotate_per_j,
+ double rotate_rand,
+ int rotate_alternatei, int rotate_alternatej,
+ int rotate_cumulatei, int rotate_cumulatej
+ );
+
+
+private:
+ CloneTiler(CloneTiler const &d);
+ CloneTiler& operator=(CloneTiler const &d);
+
+ GtkWidget *dlg;
+ SPDesktop *desktop;
+ DesktopTracker deskTrack;
+ Inkscape::UI::Widget::ColorPicker *color_picker;
+ GtkSizeGroup* table_row_labels;
+
+ sigc::connection desktopChangeConn;
+ sigc::connection selectChangedConn;
+ sigc::connection subselChangedConn;
+ sigc::connection selectModifiedConn;
+ sigc::connection color_changed_connection;
+
+ /**
+ * Can be invoked for setting the desktop. Currently not used.
+ */
+ void setDesktop(SPDesktop *desktop);
+
+ /**
+ * Is invoked by the desktop tracker when the desktop changes.
+ */
+ void setTargetDesktop(SPDesktop *desktop);
+
+};
+
+
+enum {
+ PICK_COLOR,
+ PICK_OPACITY,
+ PICK_R,
+ PICK_G,
+ PICK_B,
+ PICK_H,
+ PICK_S,
+ PICK_L
+};
+
+enum {
+ TILE_P1,
+ TILE_P2,
+ TILE_PM,
+ TILE_PG,
+ TILE_CM,
+ TILE_PMM,
+ TILE_PMG,
+ TILE_PGG,
+ TILE_CMM,
+ TILE_P4,
+ TILE_P4M,
+ TILE_P4G,
+ TILE_P3,
+ TILE_P31M,
+ TILE_P3M1,
+ TILE_P6,
+ TILE_P6M
+};
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp
index 2e7ffa1c2..60011ca9d 100644
--- a/src/ui/dialog/dialog-manager.cpp
+++ b/src/ui/dialog/dialog-manager.cpp
@@ -51,6 +51,7 @@
#include "ui/dialog/spellcheck.h"
#include "ui/dialog/export.h"
#include "ui/dialog/xml-tree.h"
+#include "ui/dialog/clonetiler.h"
#ifdef ENABLE_SVG_FONTS
#include "ui/dialog/svg-fonts-dialog.h"
@@ -129,6 +130,7 @@ DialogManager::DialogManager() {
registerFactory("SpellCheck", &create<SpellCheck, FloatingBehavior>);
registerFactory("Export", &create<Export, FloatingBehavior>);
registerFactory("XmlTree", &create<XmlTree, FloatingBehavior>);
+ registerFactory("CloneTiler", &create<CloneTiler, FloatingBehavior>);
} else {
@@ -163,6 +165,7 @@ DialogManager::DialogManager() {
registerFactory("SpellCheck", &create<SpellCheck, DockBehavior>);
registerFactory("Export", &create<Export, DockBehavior>);
registerFactory("XmlTree", &create<XmlTree, DockBehavior>);
+ registerFactory("CloneTiler", &create<CloneTiler, DockBehavior>);
}
}
diff --git a/src/ui/dialog/find.cpp b/src/ui/dialog/find.cpp
index 9d3508c53..f5745e02b 100644
--- a/src/ui/dialog/find.cpp
+++ b/src/ui/dialog/find.cpp
@@ -24,12 +24,14 @@
#include "inkscape.h"
#include "desktop.h"
#include "document.h"
+#include "document-undo.h"
#include "selection.h"
#include "desktop-handles.h"
#include "dialogs/dialog-events.h"
#include "verbs.h"
#include "interface.h"
+#include "preferences.h"
#include "sp-text.h"
#include "sp-flowtext.h"
#include "text-editing.h"
@@ -50,6 +52,8 @@
#include "sp-offset.h"
#include "sp-root.h"
#include "xml/repr.h"
+#include "xml/node-iterators.h"
+#include "xml/attribute-record.h"
#include <glibmm/i18n.h>
@@ -59,112 +63,278 @@ namespace Dialog {
Find::Find()
: UI::Widget::Panel("", "/dialogs/find", SP_VERB_DIALOG_FIND),
- _entry_text(_("_Text:"), _("Find objects by their text content (exact or partial match)")),
- _entry_id(_("_ID:"), _("Find objects by the value of the id attribute (exact or partial match)")),
- _entry_style(_("_Style:"), _("Find objects by the value of the style attribute (exact or partial match)")),
- _entry_attribute(_("_Attribute:"), _("Find objects by the name of an attribute (exact or partial match)")),
- _check_search_selection(_("Search in s_election"), _("Limit search to the current selection")),
- _check_search_layer(_("Search in current _layer"), _("Limit search to the current layer")),
- _check_include_hidden(_("Include _hidden"), _("Include hidden objects in search")),
- _check_include_locked(_("Include l_ocked"), _("Include locked objects in search")),
-
- _check_all(_("All types"), _("Search in all object types")),
- _check_all_shapes(_("All shapes"), _("Search all shapes")),
- _check_rects(_("Rectangles"), _("Search rectangles")),
- _check_ellipses(_("Ellipses"), _("Search ellipses, arcs, circles")),
- _check_stars(_("Stars"), _("Search stars and polygons")),
- _check_spirals(_("Spirals"), _("Search spirals")),
- _check_paths(_("Paths"), _("Search paths, lines, polylines")),
- _check_texts(_("Texts"), _("Search text objects")),
- _check_groups(_("Groups"), _("Search groups")),
- _check_clones(
+
+ entry_find(_("F_ind:"), _("Find objects by their content (exact or partial match)")),
+ entry_replace(_("Re_place:"), _("Replace found objects with this value ")),
+
+ check_scope_all(_("_All"), _("Search in all layers")),
+ check_scope_layer(_("Current _layer"), _("Limit search to the current layer")),
+ check_scope_selection(_("S_election"), _("Limit search to the current selection")),
+ check_searchin_text(_("Te_xt"), _("Search in text objects")),
+ check_searchin_property(_("_Properties"), _("Search in object properties, styles, attributes and IDs")),
+ frame_searchin(_("Search in")),
+ frame_scope(_("Scope")),
+
+
+ check_case_sensitive(_("Case sensiti_ve"), _("Match upper/lower case"), false),
+ check_exact_match(_("E_xact match"), _("Match whole objects only"), false),
+ check_include_hidden(_("Include _hidden"), _("Include hidden objects in search"), false),
+ check_include_locked(_("Include loc_ked"), _("Include locked objects in search"), false),
+ expander_options(_("Options")),
+ frame_options(_("General")),
+
+ check_ids(_("_ID"), _("Search id name"), true),
+ check_attributename(_("Attribute _Name"), _("Search attribute name"), false),
+ check_attributevalue(_("Attribute _Value"), _("Search attribute value"), true),
+ check_style(_("_Style"), _("Search style"), true),
+ check_font(_("_Font"), _("Search fonts"), false),
+ frame_properties(_("Properties")),
+
+ check_alltypes(_("All types"), _("Search all object types"), true),
+ check_rects(_("Rectangles"), _("Search rectangles"), false),
+ check_ellipses(_("Ellipses"), _("Search ellipses, arcs, circles"), false),
+ check_stars(_("Stars"), _("Search stars and polygons"), false),
+ check_spirals(_("Spirals"), _("Search spirals"), false),
+ check_paths(_("Paths"), _("Search paths, lines, polylines"), false),
+ check_texts(_("Texts"), _("Search text objects"), false),
+ check_groups(_("Groups"), _("Search groups"), false),
+ check_clones(
//TRANSLATORS: "Clones" is a noun indicating type of object to find
- C_("Find dialog", "Clones"), _("Search clones")),
- _check_images(_("Images"), _("Search images")),
- _check_offsets(_("Offsets"), _("Search offset objects")),
-
- _button_clear(_("_Clear"), _("Clear values")),
- _button_find(_("_Find"), _("Select objects matching all of the fields you filled in"))
+ C_("Find dialog", "Clones"), _("Search clones"), false),
+
+ check_images(_("Images"), _("Search images"), false),
+ check_offsets(_("Offsets"), _("Search offset objects"), false),
+ frame_types(_("Object Types")),
+
+ status(""),
+ button_find(_("_Find"), _("Select all objects matching the selected fields ")),
+ button_replace(_("_Replace All"), _("Replace all the matching objects")),
+ _action_replace(false),
+ blocked(false),
+ desktop(NULL),
+ deskTrack()
+
{
+ entry_find.getEntry()->set_width_chars(25);
+ entry_replace.getEntry()->set_width_chars(25);
+
+ Gtk::RadioButtonGroup grp_searchin = check_searchin_text.get_group();
+ check_searchin_property.set_group(grp_searchin);
+ vbox_searchin.pack_start(check_searchin_text, true, true);
+ vbox_searchin.pack_start(check_searchin_property, true, true);
+ frame_searchin.add(vbox_searchin);
+
+ Gtk::RadioButtonGroup grp_scope = check_scope_all.get_group();
+ check_scope_layer.set_group(grp_scope);
+ check_scope_selection.set_group(grp_scope);
+ vbox_scope.pack_start(check_scope_all, true, true);
+ vbox_scope.pack_start(check_scope_layer, true, true);
+ vbox_scope.pack_start(check_scope_selection, true, true);
+ frame_scope.add(vbox_scope);
+
+ hbox_searchin.set_spacing(4);
+ hbox_searchin.pack_start(frame_searchin, true, true);
+ hbox_searchin.pack_start(frame_scope, true, true);
+
+ vbox_options1.pack_start(check_case_sensitive, true, true);
+ vbox_options1.pack_start(check_include_hidden, true, true);
+ vbox_options2.pack_start(check_exact_match, true, true);
+ vbox_options2.pack_start(check_include_locked, true, true);
+ hbox_options.pack_start(vbox_options1, true, true, 4);
+ hbox_options.pack_start(vbox_options2, true, true, 4);
+ frame_options.add(hbox_options);
+
+ hbox_properties1.set_homogeneous(false);
+ hbox_properties1.pack_start(check_ids, false, false, 4 );
+ hbox_properties1.pack_start(check_style, false, false, 8);
+ hbox_properties1.pack_start(check_font, false, false, 8);
+ hbox_properties2.set_homogeneous(false);
+ hbox_properties2.pack_start(check_attributevalue, false, false, 4);
+ hbox_properties2.pack_start(check_attributename, false, false, 4);
+ vbox_properties.pack_start(hbox_properties1, true, true, 0);
+ vbox_properties.pack_start(hbox_properties2, true, true, 2);
+ frame_properties.add(vbox_properties);
+
+ vbox_types1.pack_start(check_alltypes, true, true);
+ vbox_types1.pack_start(check_paths, true, true);
+ vbox_types1.pack_start(check_texts, true, true);
+ vbox_types1.pack_start(check_groups, true, true);
+ vbox_types1.pack_start(check_clones, true, true);
+ vbox_types1.pack_start(check_images, true, true);
+ vbox_types2.pack_start(check_offsets, true, true);
+ vbox_types2.pack_start(check_rects, true, true);
+ vbox_types2.pack_start(check_ellipses, true, true);
+ vbox_types2.pack_start(check_stars, true, true);
+ vbox_types2.pack_start(check_spirals, true, true);
+ hbox_types.pack_start(vbox_types1, true, true, 4);
+ hbox_types.pack_start(vbox_types2, true, true, 4);
+ frame_types.add(hbox_types);
+
+ vbox_expander.pack_start(frame_options, true, true, 4);
+ vbox_expander.pack_start(frame_properties, true, true, 4);
+ vbox_expander.pack_start(frame_types, true, true, 4);
+
+ expander_options.set_use_underline();
+ expander_options.add(vbox_expander);
+
+ box_buttons.set_layout(Gtk::BUTTONBOX_END);
+ box_buttons.set_spacing(4);
+ box_buttons.pack_start(button_find, true, true, 6);
+ box_buttons.pack_start(button_replace, true, true, 6);
+ hboxbutton_row.pack_start(status, true, true, 6);
+ hboxbutton_row.pack_end(box_buttons, true, true);
+
Gtk::Box *contents = _getContents();
- contents->set_spacing(4);
-
- contents->pack_start(_entry_text, true, true);
- contents->pack_start(_entry_id, true, true);
- contents->pack_start(_entry_style, true, true);
- contents->pack_start(_entry_attribute, true, true);
-
- contents->pack_start(_check_all, true, true);
- contents->pack_start(_check_all_shapes, true, true);
- contents->pack_start(_check_rects, true, true);
- contents->pack_start(_check_ellipses, true, true);
- contents->pack_start(_check_stars, true, true);
- contents->pack_start(_check_spirals, true, true);
- contents->pack_start(_check_paths, true, true);
- contents->pack_start(_check_texts, true, true);
- contents->pack_start(_check_groups, true, true);
- contents->pack_start(_check_clones, true, true);
- contents->pack_start(_check_images, true, true);
- contents->pack_start(_check_offsets, true, true);
-
- contents->pack_start(_check_search_selection, true, true);
- contents->pack_start(_check_search_layer, true, true);
- contents->pack_start(_check_include_hidden, true, true);
- contents->pack_start(_check_include_locked, true, true);
-
- contents->pack_start(_button_clear, true, true);
- contents->pack_start(_button_find, true, true);
+ contents->set_spacing(6);
+ contents->pack_start(entry_find, false, false);
+ contents->pack_start(entry_replace, false, false);
+ contents->pack_start(hbox_searchin, false, false);
+ contents->pack_start(expander_options, false, false);
+ contents->pack_end(hboxbutton_row, false, false);
+
+ checkProperties.push_back(&check_ids);
+ checkProperties.push_back(&check_style);
+ checkProperties.push_back(&check_font);
+ checkProperties.push_back(&check_attributevalue);
+ checkProperties.push_back(&check_attributename);
+
+ checkTypes.push_back(&check_paths);
+ checkTypes.push_back(&check_texts);
+ checkTypes.push_back(&check_groups);
+ checkTypes.push_back(&check_clones);
+ checkTypes.push_back(&check_images);
+ checkTypes.push_back(&check_offsets);
+ checkTypes.push_back(&check_rects);
+ checkTypes.push_back(&check_ellipses);
+ checkTypes.push_back(&check_stars);
+ checkTypes.push_back(&check_spirals);
+ checkTypes.push_back(&check_offsets);
// set signals to handle clicks
- _check_all.signal_clicked().connect(sigc::mem_fun(*this, &Find::onToggleAlltypes));
- _check_all_shapes.signal_clicked().connect(sigc::mem_fun(*this, &Find::onToggleShapes));
- _button_clear.signal_clicked().connect(sigc::mem_fun(*this, &Find::onClear));
- _button_find.signal_clicked().connect(sigc::mem_fun(*this, &Find::onFind));
+ expander_options.property_expanded().signal_changed().connect(sigc::mem_fun(*this, &Find::onExpander));
+ button_find.signal_clicked().connect(sigc::mem_fun(*this, &Find::onFind));
+ button_replace.signal_clicked().connect(sigc::mem_fun(*this, &Find::onReplace));
+ check_searchin_text.signal_clicked().connect(sigc::mem_fun(*this, &Find::onSearchinText));
+ check_searchin_property.signal_clicked().connect(sigc::mem_fun(*this, &Find::onSearchinProperty));
+ check_alltypes.signal_clicked().connect(sigc::mem_fun(*this, &Find::onToggleAlltypes));
+
+ for(size_t i = 0; i < checkProperties.size(); i++) {
+ checkProperties[i]->signal_clicked().connect(sigc::mem_fun(*this, &Find::onToggleCheck));
+ }
+
+ for(size_t i = 0; i < checkTypes.size(); i++) {
+ checkTypes[i]->signal_clicked().connect(sigc::mem_fun(*this, &Find::onToggleCheck));
+ }
+
+ onSearchinText();
+ onToggleAlltypes();
- _button_find.set_can_default();
- // set_default (_button_find); // activatable by Enter
- _entry_text.getEntry()->grab_focus();
+ desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &Find::setTargetDesktop) );
+ deskTrack.connect(GTK_WIDGET(gobj()));
show_all_children();
- onClear();
+
+ Inkscape::Selection *selection = sp_desktop_selection (SP_ACTIVE_DESKTOP);
+ SPItem *item = selection->singleItem();
+ if (item) {
+ if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) {
+ gchar *str;
+ str = sp_te_get_string_multiline (item);
+ entry_find.getEntry()->set_text(str);
+ }
+ }
+
+ button_find.set_can_default();
+ //button_find.grab_default(); // activatable by Enter
+ entry_find.getEntry()->grab_focus();
}
-Find::~Find()
+Find::~Find()
{
+ desktopChangeConn.disconnect();
+ selectChangedConn.disconnect();
+ deskTrack.disconnect();
}
+void Find::setDesktop(SPDesktop *desktop)
+{
+ Panel::setDesktop(desktop);
+ deskTrack.setBase(desktop);
+}
+
+void Find::setTargetDesktop(SPDesktop *desktop)
+{
+ if (this->desktop != desktop) {
+ if (this->desktop) {
+ selectChangedConn.disconnect();
+ }
+ this->desktop = desktop;
+ if (desktop && desktop->selection) {
+ selectChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(*this, &Find::onSelectionChange)));
+ }
+ }
+}
+
+void Find::onSelectionChange(void)
+{
+ if (!blocked) {
+ status.set_text("");
+ }
+}
/*########################################################################
# FIND helper functions
########################################################################*/
-
-bool
-Find::item_id_match (SPItem *item, const gchar *id, bool exact)
+Glib::ustring Find::find_replace(const gchar *str, const gchar *find, const gchar *replace, bool exact, bool casematch, bool replaceall)
{
- if (item->getRepr() == NULL) {
- return false;
+ Glib::ustring ustr = str;
+ Glib::ustring ufind = find;
+ if (!casematch) {
+ ufind = ufind.lowercase();
}
-
- if (SP_IS_STRING(item)) { // SPStrings have "on demand" ids which are useless for searching
- return false;
+ gsize n = find_strcmp_pos(ustr.c_str(), ufind.c_str(), exact, casematch);
+ while (n != std::string::npos) {
+ ustr.replace(n, ufind.length(), replace);
+ if (!replaceall) {
+ return ustr;
+ }
+ // Start the next search after the last replace character to avoid infinite loops (replace "a" with "aaa" etc)
+ n = find_strcmp_pos(ustr.c_str(), ufind.c_str(), exact, casematch, n + strlen(replace) + 1);
}
+ return ustr;
+}
- const gchar *item_id = item->getRepr()->attribute("id");
- if (item_id == NULL) {
- return false;
+gsize Find::find_strcmp_pos(const gchar *str, const gchar *find, bool exact, bool casematch, gsize start/*=0*/)
+{
+ Glib::ustring ustr = str;
+ Glib::ustring ufind = find;
+
+ if (!casematch) {
+ ustr = ustr.lowercase();
+ ufind = ufind.lowercase();
}
+ gsize pos = std::string::npos;
if (exact) {
- return ((bool) !strcmp(item_id, id));
+ if (ustr == ufind) {
+ pos = 0;
+ }
} else {
-// g_print ("strstr: %s %s: %s\n", item_id, id, strstr(item_id, id) != NULL? "yes":"no");
- return ((bool) (strstr(item_id, id) != NULL));
+ pos = ustr.find(ufind, start);
}
+
+ return pos;
+}
+
+
+bool Find::find_strcmp(const gchar *str, const gchar *find, bool exact, bool casematch)
+{
+ return (std::string::npos != find_strcmp_pos(str, find, exact, casematch));
}
bool
-Find::item_text_match (SPItem *item, const gchar *text, bool exact)
+Find::item_text_match (SPItem *item, const gchar *find, bool exact, bool casematch, bool replace/*=false*/)
{
if (item->getRepr() == NULL) {
return false;
@@ -172,111 +342,317 @@ Find::item_text_match (SPItem *item, const gchar *text, bool exact)
if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) {
const gchar *item_text = sp_te_get_string_multiline (item);
- if (item_text == NULL)
+ if (item_text == NULL) {
return false;
- bool ret;
- if (exact) {
- ret = ((bool) !strcasecmp(item_text, text));
- } else {
- //FIXME: strcasestr
- ret = ((bool) (strstr(item_text, text) != NULL));
}
- g_free ((void*) item_text);
- return ret;
+ bool found = find_strcmp(item_text, find, exact, casematch);
+
+ if (found && replace) {
+ Glib::ustring ufind = find;
+ if (!casematch) {
+ ufind = ufind.lowercase();
+ }
+
+ Inkscape::Text::Layout const *layout = te_get_layout (item);
+ if (!layout) {
+ return found;
+ }
+
+ gchar* replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str());
+ gsize n = find_strcmp_pos(item_text, ufind.c_str(), exact, casematch);
+ static Inkscape::Text::Layout::iterator _begin_w;
+ static Inkscape::Text::Layout::iterator _end_w;
+ while (n != std::string::npos) {
+ _begin_w = layout->charIndexToIterator(n);
+ _end_w = layout->charIndexToIterator(n + strlen(find));
+ sp_te_replace(item, _begin_w, _end_w, replace_text);
+ item_text = sp_te_get_string_multiline (item);
+ n = find_strcmp_pos(item_text, ufind.c_str(), exact, casematch, n + strlen(replace_text) + 1);
+ }
+
+ g_free(replace_text);
+ }
+
+ return found;
}
return false;
}
+
bool
-Find::item_style_match (SPItem *item, const gchar *text, bool exact)
+Find::item_id_match (SPItem *item, const gchar *id, bool exact, bool casematch, bool replace/*=false*/)
{
if (item->getRepr() == NULL) {
return false;
}
- const gchar *item_text = item->getRepr()->attribute("style");
- if (item_text == NULL) {
+ if (SP_IS_STRING(item)) { // SPStrings have "on demand" ids which are useless for searching
+ return false;
+ }
+
+ const gchar *item_id = item->getRepr()->attribute("id");
+ if (item_id == NULL) {
+ return false;
+ }
+
+ bool found = find_strcmp(item_id, id, exact, casematch);
+
+ if (found && replace) {
+ gchar * replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str());
+ Glib::ustring new_item_style = find_replace(item_id, id, replace_text , exact, casematch, true);
+ if (new_item_style != item_id) {
+ item->getRepr()->setAttribute("id", new_item_style.data());
+ }
+ g_free(replace_text);
+ }
+
+ return found;
+}
+
+bool
+Find::item_style_match (SPItem *item, const gchar *text, bool exact, bool casematch, bool replace/*=false*/)
+{
+ if (item->getRepr() == NULL) {
+ return false;
+ }
+
+ gchar *item_style = g_strdup(item->getRepr()->attribute("style"));
+ if (item_style == NULL) {
+ return false;
+ }
+
+ bool found = find_strcmp(item_style, text, exact, casematch);
+
+ if (found && replace) {
+ gchar * replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str());
+ Glib::ustring new_item_style = find_replace(item_style, text, replace_text , exact, casematch, true);
+ if (new_item_style != item_style) {
+ item->getRepr()->setAttribute("style", new_item_style.data());
+ }
+ g_free(replace_text);
+ }
+
+ g_free(item_style);
+ return found;
+}
+
+bool Find::item_attr_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool replace/*=false*/)
+{
+ bool found = false;
+
+ if (item->getRepr() == NULL) {
return false;
}
+ gchar *attr_value = g_strdup(item->getRepr()->attribute(text));
if (exact) {
- return ((bool) !strcmp(item_text, text));
+ found = (attr_value != NULL);
} else {
- return ((bool) (strstr(item_text, text) != NULL));
+ found = item->getRepr()->matchAttributeName(text);
}
+ g_free(attr_value);
+
+ // TODO - Rename attribute name ?
+ if (found && replace) {
+ found = false;
+ }
+
+ return found;
}
-bool Find::item_attr_match(SPItem *item, const gchar *name, bool exact)
+bool Find::item_attrvalue_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool replace/*=false*/)
{
- bool result = false;
- if (item->getRepr()) {
- if (exact) {
- const gchar *attr_value = item->getRepr()->attribute(name);
- result = (attr_value != NULL);
- } else {
- result = item->getRepr()->matchAttributeName(name);
+ bool ret = false;
+
+ if (item->getRepr() == NULL) {
+ return false;
+ }
+
+ Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = item->getRepr()->attributeList();
+ for (; iter; ++iter) {
+ const gchar* key = g_quark_to_string(iter->key);
+ gchar *attr_value = g_strdup(item->getRepr()->attribute(key));
+ bool found = find_strcmp(attr_value, text, exact, casematch);
+ if (found) {
+ ret = true;
+ }
+
+ if (found && replace) {
+ gchar * replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str());
+ Glib::ustring new_item_style = find_replace(attr_value, text, replace_text , exact, casematch, true);
+ if (new_item_style != attr_value) {
+ item->getRepr()->setAttribute(key, new_item_style.data());
+ }
+ }
+
+ g_free(attr_value);
+ }
+
+ return ret;
+}
+
+
+bool Find::item_font_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool replace/*=false*/)
+{
+ bool ret = false;
+
+ if (item->getRepr() == NULL) {
+ return false;
+ }
+
+ const gchar *item_style = item->getRepr()->attribute("style");
+ if (item_style == NULL) {
+ return false;
+ }
+
+ std::vector<Glib::ustring> vFontTokenNames;
+ vFontTokenNames.push_back("font-family:");
+ vFontTokenNames.push_back("-inkscape-font-specification:");
+
+ std::vector<Glib::ustring> vStyleTokens = Glib::Regex::split_simple(";", item_style);
+ for(size_t i=0; i<vStyleTokens.size(); i++) {
+ Glib::ustring token = vStyleTokens[i];
+ for(size_t j=0; j<vFontTokenNames.size(); j++) {
+ if ( token.find(vFontTokenNames[j]) != std::string::npos) {
+ Glib::ustring font1 = Glib::ustring(vFontTokenNames[j]).append(text);
+ bool found = find_strcmp(token.c_str(), font1.c_str(), exact, casematch);
+ if (found) {
+ ret = true;
+ if (_action_replace) {
+ gchar *replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str());
+ gchar *orig_str = g_strdup(token.c_str());
+ // Exact match fails since the "font-family:" is in the token, since the find was exact it still works with false below
+ Glib::ustring new_item_style = find_replace(orig_str, text, replace_text , false /*exact*/, casematch, true);
+ if (new_item_style != orig_str) {
+ vStyleTokens.at(i) = new_item_style;
+ }
+ g_free(orig_str);
+ g_free(replace_text);
+ }
+ }
+ }
}
}
- return result;
+
+ if (ret && _action_replace) {
+ Glib::ustring new_item_style;
+ for(size_t i=0; i<vStyleTokens.size(); i++) {
+ new_item_style.append(vStyleTokens.at(i)).append(";");
+ }
+ new_item_style.erase(new_item_style.size()-1);
+ item->getRepr()->setAttribute("style", new_item_style.data());
+ }
+
+ return ret;
}
GSList *
-Find::filter_fields (GSList *l, bool exact)
+Find::filter_fields (GSList *l, bool exact, bool casematch)
{
- const gchar* text = _entry_text.getEntry()->get_text().c_str();
- const gchar* id = _entry_id.getEntry()->get_text().c_str();
- const gchar* style = _entry_style.getEntry()->get_text().c_str();
- const gchar* attr = _entry_attribute.getEntry()->get_text().c_str();
-
+ Glib::ustring tmp = entry_find.getEntry()->get_text();
+ if (tmp.empty()) {
+ return l;
+ }
+ gchar* text = g_strdup(tmp.c_str());
+
GSList *in = l;
GSList *out = NULL;
- if (strlen (text) != 0) {
+
+ if (check_searchin_text.get_active()) {
for (GSList *i = in; i != NULL; i = i->next) {
- if (item_text_match (SP_ITEM(i->data), text, exact)) {
- out = g_slist_prepend (out, i->data);
+ if (item_text_match (SP_ITEM(i->data), text, exact, casematch)) {
+ if (!g_slist_find(out, i->data)) {
+ out = g_slist_prepend (out, i->data);
+ if (_action_replace) {
+ item_text_match (SP_ITEM(i->data), text, exact, casematch, _action_replace);
+ }
+ }
}
}
- } else {
- out = in;
}
-
- in = out;
- out = NULL;
- if (strlen (id) != 0) {
- for (GSList *i = in; i != NULL; i = i->next) {
- if (item_id_match (SP_ITEM(i->data), id, exact)) {
- out = g_slist_prepend (out, i->data);
+ else if (check_searchin_property.get_active()) {
+
+ bool ids = check_ids.get_active();
+ bool style = check_style.get_active();
+ bool font = check_font.get_active();
+ bool attrname = check_attributename.get_active();
+ bool attrvalue = check_attributevalue.get_active();
+
+ if (ids) {
+ for (GSList *i = in; i != NULL; i = i->next) {
+ if (item_id_match (SP_ITEM(i->data), text, exact, casematch)) {
+ if (!g_slist_find(out, i->data)) {
+ out = g_slist_prepend (out, i->data);
+ if (_action_replace) {
+ item_id_match (SP_ITEM(i->data), text, exact, casematch, _action_replace);
+ }
+ }
+ }
}
}
- } else {
- out = in;
- }
- in = out;
- out = NULL;
- if (strlen (style) != 0) {
- for (GSList *i = in; i != NULL; i = i->next) {
- if (item_style_match (SP_ITEM(i->data), style, exact)) {
- out = g_slist_prepend (out, i->data);
+
+ if (style) {
+ for (GSList *i = in; i != NULL; i = i->next) {
+ if (item_style_match (SP_ITEM(i->data), text, exact, casematch)) {
+ if (!g_slist_find(out, i->data))
+ if (!g_slist_find(out, i->data)) {
+ out = g_slist_prepend (out, i->data);
+ if (_action_replace) {
+ item_style_match (SP_ITEM(i->data), text, exact, casematch, _action_replace);
+ }
+ }
+ }
}
}
- } else {
- out = in;
- }
- in = out;
- out = NULL;
- if (strlen (attr) != 0) {
- for (GSList *i = in; i != NULL; i = i->next) {
- if (item_attr_match (SP_ITEM(i->data), attr, exact)) {
- out = g_slist_prepend (out, i->data);
+
+ if (attrname) {
+ for (GSList *i = in; i != NULL; i = i->next) {
+ if (item_attr_match (SP_ITEM(i->data), text, exact, casematch)) {
+ if (!g_slist_find(out, i->data)) {
+ out = g_slist_prepend (out, i->data);
+ if (_action_replace) {
+ item_attr_match (SP_ITEM(i->data), text, exact, casematch, _action_replace);
+ }
+ }
+ }
}
}
- } else {
- out = in;
+
+
+ if (attrvalue) {
+ for (GSList *i = in; i != NULL; i = i->next) {
+ if (item_attrvalue_match (SP_ITEM(i->data), text, exact, casematch)) {
+ if (!g_slist_find(out, i->data)) {
+ out = g_slist_prepend (out, i->data);
+ if (_action_replace) {
+ item_attrvalue_match (SP_ITEM(i->data), text, exact, casematch, _action_replace);
+ }
+ }
+ }
+ }
+ }
+
+
+ if (font) {
+ for (GSList *i = in; i != NULL; i = i->next) {
+ if (item_font_match (SP_ITEM(i->data), text, exact, casematch)) {
+ if (!g_slist_find(out, i->data)) {
+ out = g_slist_prepend (out, i->data);
+ if (_action_replace) {
+ item_font_match (SP_ITEM(i->data), text, exact, casematch, _action_replace);
+ }
+ }
+ }
+ }
+ }
+
}
+ g_free(text);
+
return out;
}
@@ -284,37 +660,37 @@ Find::filter_fields (GSList *l, bool exact)
bool
Find::item_type_match (SPItem *item)
{
- SPDesktop *desktop = getDesktop();
+ bool all =check_alltypes.get_active();
- if (SP_IS_RECT(item)) {
- return (_check_all_shapes.get_active() || _check_rects.get_active());
+ if ( SP_IS_RECT(item)) {
+ return ( all ||check_rects.get_active());
} else if (SP_IS_GENERICELLIPSE(item) || SP_IS_ELLIPSE(item) || SP_IS_ARC(item) || SP_IS_CIRCLE(item)) {
- return (_check_all_shapes.get_active() || _check_ellipses.get_active());
+ return ( all || check_ellipses.get_active());
} else if (SP_IS_STAR(item) || SP_IS_POLYGON(item)) {
- return (_check_all_shapes.get_active() || _check_stars.get_active());
+ return ( all || check_stars.get_active());
} else if (SP_IS_SPIRAL(item)) {
- return (_check_all_shapes.get_active() || _check_spirals.get_active());
+ return ( all || check_spirals.get_active());
} else if (SP_IS_PATH(item) || SP_IS_LINE(item) || SP_IS_POLYLINE(item)) {
- return (_check_paths.get_active());
+ return (all || check_paths.get_active());
} else if (SP_IS_TEXT(item) || SP_IS_TSPAN(item) || SP_IS_TREF(item) || SP_IS_STRING(item)) {
- return (_check_texts.get_active());
+ return (all || check_texts.get_active());
} else if (SP_IS_GROUP(item) && !desktop->isLayer(item) ) { // never select layers!
- return (_check_groups.get_active());
+ return (all || check_groups.get_active());
} else if (SP_IS_USE(item)) {
- return (_check_clones.get_active());
+ return (all || check_clones.get_active());
} else if (SP_IS_IMAGE(item)) {
- return (_check_images.get_active());
+ return (all || check_images.get_active());
} else if (SP_IS_OFFSET(item)) {
- return (_check_offsets.get_active());
+ return (all || check_offsets.get_active());
}
return false;
@@ -323,8 +699,6 @@ Find::item_type_match (SPItem *item)
GSList *
Find::filter_types (GSList *l)
{
- if (_check_all.get_active()) return l;
-
GSList *n = NULL;
for (GSList *i = l; i != NULL; i = i->next) {
if (item_type_match (SP_ITEM(i->data))) {
@@ -336,18 +710,16 @@ Find::filter_types (GSList *l)
GSList *
-Find::filter_list (GSList *l, bool exact)
+Find::filter_list (GSList *l, bool exact, bool casematch)
{
- l = filter_fields (l, exact);
l = filter_types (l);
+ l = filter_fields (l, exact, casematch);
return l;
}
GSList *
Find::all_items (SPObject *r, GSList *l, bool hidden, bool locked)
{
- SPDesktop *desktop = getDesktop();
-
if (SP_IS_DEFS(r)) {
return l; // we're not interested in items in defs
}
@@ -371,8 +743,6 @@ Find::all_items (SPObject *r, GSList *l, bool hidden, bool locked)
GSList *
Find::all_selection_items (Inkscape::Selection *s, GSList *l, SPObject *ancestor, bool hidden, bool locked)
{
- SPDesktop *desktop = getDesktop();
-
for (GSList *i = (GSList *) s->itemList(); i != NULL; i = i->next) {
if (SP_IS_ITEM (i->data) && !reinterpret_cast<SPItem *>(i->data)->cloned && !desktop->isLayer(SP_ITEM(i->data))) {
SPItem *item = reinterpret_cast<SPItem *>(i->data);
@@ -388,43 +758,56 @@ Find::all_selection_items (Inkscape::Selection *s, GSList *l, SPObject *ancestor
}
return l;
}
-
-
+
+
/*########################################################################
# BUTTON CLICK HANDLERS (callbacks)
########################################################################*/
void
-Find::onClear()
-{
- _entry_text.getEntry()->set_text(Glib::ustring(""));
- _entry_id.getEntry()->set_text(Glib::ustring(""));
- _entry_style.getEntry()->set_text(Glib::ustring(""));
- _entry_attribute.getEntry()->set_text(Glib::ustring(""));
-
- _check_all.set_active();
+Find::onFind()
+{
+ _action_replace = false;
+ onAction();
+
+ // Return focus to the find entry
+ entry_find.getEntry()->grab_focus();
}
-
-
void
-Find::onFind()
-{
- SPDesktop *desktop = getDesktop();
+Find::onReplace()
+{
+ if (entry_find.getEntry()->get_text().length() < 1) {
+ status.set_text(_("Nothing to replace"));
+ return;
+ }
+ _action_replace = true;
+ onAction();
- bool hidden = _check_include_hidden.get_active();
- bool locked = _check_include_locked.get_active();
+ // Return focus to the find entry
+ entry_find.getEntry()->grab_focus();
+}
+
+void
+Find::onAction()
+{
+
+ bool hidden = check_include_hidden.get_active();
+ bool locked = check_include_locked.get_active();
+ bool exact = check_exact_match.get_active();
+ bool casematch = check_case_sensitive.get_active();
+ blocked = true;
GSList *l = NULL;
- if (_check_search_selection.get_active()) {
- if (_check_search_layer.get_active()) {
+ if (check_scope_selection.get_active()) {
+ if (check_scope_layer.get_active()) {
l = all_selection_items (desktop->selection, l, desktop->currentLayer(), hidden, locked);
} else {
l = all_selection_items (desktop->selection, l, NULL, hidden, locked);
}
} else {
- if (_check_search_layer.get_active()) {
+ if (check_scope_layer.get_active()) {
l = all_items (desktop->currentLayer(), l, hidden, locked);
} else {
l = all_items(sp_desktop_document(desktop)->getRoot(), l, hidden, locked);
@@ -432,13 +815,7 @@ Find::onFind()
}
guint all = g_slist_length (l);
- bool exact = true;
- GSList *n = NULL;
- n = filter_list (l, exact);
- if (n == NULL) {
- exact = false;
- n = filter_list (l, exact);
- }
+ GSList *n = filter_list (l, exact, casematch);
if (n != NULL) {
int count = g_slist_length (n);
@@ -448,88 +825,124 @@ Find::onFind()
"<b>%d</b> objects found (out of <b>%d</b>), %s match.",
count),
count, all, exact? _("exact") : _("partial"));
+ status.set_text(Glib::ustring::compose("%1 %2 %3", count, _("objects"), _action_replace? _("replaced") : _("found") ));
Inkscape::Selection *selection = sp_desktop_selection (desktop);
selection->clear();
selection->setList(n);
scroll_to_show_item (desktop, SP_ITEM(n->data));
+
+ if (_action_replace) {
+ DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, _("Text Replace"));
+ }
+
} else {
+ status.set_text(_("Not found"));
+ if (!check_scope_selection.get_active()) {
+ Inkscape::Selection *selection = sp_desktop_selection (desktop);
+ selection->clear();
+ }
desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No objects found"));
}
+ blocked = false;
+
+}
+void
+Find::onToggleCheck ()
+{
+ bool objectok = false;
+ status.set_text("");
+
+ if (check_alltypes.get_active()) {
+ objectok = true;
+ }
+ for(int i = 0; i < 11; i++) {
+ if (checkTypes[i]->get_active()) {
+ objectok = true;
+ }
+ }
+
+ if (!objectok) {
+ status.set_text(_("Select an object"));
+ }
+
+
+ bool propertyok = false;
+
+ if (!check_searchin_property.get_active()) {
+ propertyok = true;
+ } else {
+
+ for(size_t i = 0; i < checkProperties.size(); i++) {
+ if (checkProperties[i]->get_active()) {
+ propertyok = true;
+ }
+ }
+ }
+
+ if (!propertyok) {
+ status.set_text(_("Select a property"));
+ }
+
+ // Can't replace attribute names
+ bool attributenameyok = !check_attributename.get_active();
+
+ button_find.set_sensitive(objectok && propertyok);
+ button_replace.set_sensitive(objectok && propertyok && attributenameyok);
+
}
void
Find::onToggleAlltypes ()
{
- if (_check_all.get_active()) {
- // explicit toggle to make sure its handler gets called, no matter what was the original state
- _check_all_shapes.toggled();
- _check_all_shapes.set_active();
- _check_all_shapes.hide();
- _check_paths.hide();
- _check_texts.hide();
- _check_groups.hide();
- _check_clones.hide();
- _check_images.hide();
- _check_offsets.hide();
- } else {
- // explicit toggle to make sure its handler gets called, no matter what was the original state
- _check_all_shapes.toggled();
- _check_all_shapes.set_active();
- _check_all_shapes.show();
-
- _check_paths.set_active();
- _check_paths.show();
- _check_texts.set_active();
- _check_texts.show();
- _check_groups.set_active();
- _check_groups.show();
- _check_clones.set_active();
- _check_clones.show();
- _check_images.set_active();
- _check_images.show();
- _check_offsets.set_active();
- _check_offsets.show();
- }
- squeeze_window();
+ bool all =check_alltypes.get_active();
+ for(size_t i = 0; i < checkTypes.size(); i++) {
+ checkTypes[i]->set_sensitive(!all);
+ }
+
+ onToggleCheck();
}
void
-Find::onToggleShapes ()
+Find::onSearchinText ()
{
- if (_check_all_shapes.get_active()) {
- _check_rects.hide();
- _check_ellipses.hide();
- _check_stars.hide();
- _check_spirals.hide();
- } else {
- _check_rects.set_active();
- _check_rects.show();
- _check_ellipses.set_active();
- _check_ellipses.show();
- _check_stars.set_active();
- _check_stars.show();
- _check_spirals.set_active();
- _check_spirals.show();
- }
- squeeze_window();
+ searchinToggle(false);
+ onToggleCheck();
}
+void
+Find::onSearchinProperty ()
+{
+ searchinToggle(true);
+ onToggleCheck();
+}
+
+void
+Find::searchinToggle(bool on)
+{
+ for(size_t i = 0; i < checkProperties.size(); i++) {
+ checkProperties[i]->set_sensitive(on);
+ }
+}
+
+void
+Find::onExpander ()
+{
+ if (!expander_options.get_expanded())
+ squeeze_window();
+}
/*########################################################################
# UTILITY
########################################################################*/
-
-
void
Find::squeeze_window()
{
- // TO DO: make window as small as possible
+ // TODO: resize dialog window when the expander is closed
+ // set_size_request(-1, -1);
}
-
-
} // namespace Dialog
} // namespace UI
} // namespace Inkscape
diff --git a/src/ui/dialog/find.h b/src/ui/dialog/find.h
index 15263caac..62ecd1763 100644
--- a/src/ui/dialog/find.h
+++ b/src/ui/dialog/find.h
@@ -16,6 +16,10 @@
#include "ui/widget/button.h"
#include "ui/widget/entry.h"
#include <glib.h>
+#include <gtkmm.h>
+
+#include "desktop.h"
+#include "ui/dialog/desktop-tracker.h"
class SPItem;
class SPObject;
@@ -26,66 +30,268 @@ class Selection;
namespace UI {
namespace Dialog {
+/**
+ * The Find class defines the Find and replace dialog.
+ *
+ * The Find and replace dialog allows you to search within the
+ * current document for specific text or properties of items.
+ * Matches items are highlighted and can be replaced as well.
+ * Scope can be limited to the entire document, current layer or selected items.
+ * Other options allow searching on specific object types and properties.
+ */
+
class Find : public UI::Widget::Panel {
public:
Find();
virtual ~Find();
+ /**
+ * Helper function which returns a new instance of the dialog.
+ * getInstance is needed by the dialog manager (Inkscape::UI::Dialog::DialogManager).
+ */
static Find &getInstance() { return *new Find(); }
protected:
- // Widgets:
- Inkscape::UI::Widget::Entry _entry_text;
- Inkscape::UI::Widget::Entry _entry_id;
- Inkscape::UI::Widget::Entry _entry_style;
- Inkscape::UI::Widget::Entry _entry_attribute;
-
- Inkscape::UI::Widget::CheckButton _check_search_selection;
- Inkscape::UI::Widget::CheckButton _check_search_layer;
- Inkscape::UI::Widget::CheckButton _check_include_hidden;
- Inkscape::UI::Widget::CheckButton _check_include_locked;
-
- // Type checkbutton widgets...
- Inkscape::UI::Widget::CheckButton _check_all;
- Inkscape::UI::Widget::CheckButton _check_all_shapes;
- Inkscape::UI::Widget::CheckButton _check_rects;
- Inkscape::UI::Widget::CheckButton _check_ellipses;
- Inkscape::UI::Widget::CheckButton _check_stars;
- Inkscape::UI::Widget::CheckButton _check_spirals;
- Inkscape::UI::Widget::CheckButton _check_paths;
- Inkscape::UI::Widget::CheckButton _check_texts;
- Inkscape::UI::Widget::CheckButton _check_groups;
- Inkscape::UI::Widget::CheckButton _check_clones;
- Inkscape::UI::Widget::CheckButton _check_images;
- Inkscape::UI::Widget::CheckButton _check_offsets;
-
- // Button-click handlers
- void onClear();
+
+
+ /**
+ * Callbacks for pressing the dialog buttons.
+ */
void onFind();
+ void onReplace();
+ void onExpander();
+ void onAction();
void onToggleAlltypes();
- void onToggleShapes();
+ void onToggleCheck();
+ void onSearchinText();
+ void onSearchinProperty();
+ /**
+ * Toggle all the properties checkboxes
+ */
+ void searchinToggle(bool on);
- // onFind helper functions
- bool item_id_match (SPItem *item, const gchar *id, bool exact);
- bool item_text_match (SPItem *item, const gchar *text, bool exact);
- bool item_style_match (SPItem *item, const gchar *text, bool exact);
- bool item_attr_match (SPItem *item, const gchar *name, bool exact);
- GSList * filter_fields (GSList *l, bool exact);
+ /**
+ * Returns true if the SPItem 'item' has the same id
+ *
+ * @param item the SPItem to check
+ * @param id the value to compare with
+ * @param exact do an exacty match
+ * @param casematch match the text case exactly
+ * @param replace replace the value if found
+ *
+ */
+ bool item_id_match (SPItem *item, const gchar *id, bool exact, bool casematch, bool replace=false);
+ /**
+ * Returns true if the SPItem 'item' has the same text content
+ *
+ * @param item the SPItem to check
+ * @param name the value to compare with
+ * @param exact do an exacty match
+ * @param casematch match the text case exactly
+ * @param replace replace the value if found
+ *
+ *
+ */
+ bool item_text_match (SPItem *item, const gchar *text, bool exact, bool casematch, bool replace=false);
+ /**
+ * Returns true if the SPItem 'item' has the same text in the style attribute
+ *
+ * @param item the SPItem to check
+ * @param name the value to compare with
+ * @param exact do an exacty match
+ * @param casematch match the text case exactly
+ * @param replace replace the value if found
+ *
+ */
+ bool item_style_match (SPItem *item, const gchar *text, bool exact, bool casematch, bool replace=false);
+ /**
+ * Returns true if found the SPItem 'item' has the same attribute name
+ *
+ * @param item the SPItem to check
+ * @param name the value to compare with
+ * @param exact do an exacty match
+ * @param casematch match the text case exactly
+ * @param replace replace the value if found
+ *
+ */
+ bool item_attr_match (SPItem *item, const gchar *name, bool exact, bool casematch, bool replace=false);
+ /**
+ * Returns true if the SPItem 'item' has the same attribute value
+ *
+ * @param item the SPItem to check
+ * @param name the value to compare with
+ * @param exact do an exacty match
+ * @param casematch match the text case exactly
+ * @param replace replace the value if found
+ *
+ */
+ bool item_attrvalue_match (SPItem *item, const gchar *name, bool exact, bool casematch, bool replace=false);
+ /**
+ * Returns true if the SPItem 'item' has the same font values
+ *
+ * @param item the SPItem to check
+ * @param name the value to compare with
+ * @param exact do an exacty match
+ * @param casematch match the text case exactly
+ * @param replace replace the value if found
+ *
+ */
+ bool item_font_match (SPItem *item, const gchar *name, bool exact, bool casematch, bool replace=false);
+ /**
+ * Function to filter a list of items based on the item type by calling each item_XXX_match function
+ */
+ GSList * filter_fields (GSList *l, bool exact, bool casematch);
bool item_type_match (SPItem *item);
GSList * filter_types (GSList *l);
- GSList * filter_list (GSList *l, bool exact);
+ GSList * filter_list (GSList *l, bool exact, bool casematch);
+
+ /**
+ * Find a string within a string and returns true if found with options for exact and casematching
+ */
+ bool find_strcmp(const gchar *str, const gchar *find, bool exact, bool casematch);
+
+ /**
+ * Find a string within a string and return the position with options for exact, casematching and search start location
+ */
+ gsize find_strcmp_pos(const gchar *str, const gchar *find, bool exact, bool casematch, gsize start=0);
+
+ /**
+ * Replace a string with another string with options for exact and casematching and replace once/all
+ */
+ Glib::ustring find_replace(const gchar *str, const gchar *find, const gchar *replace, bool exact, bool casematch, bool replaceall);
+
+ /**
+ * recursive function to return a list of all the items in the SPObject tree
+ *
+ */
GSList * all_items (SPObject *r, GSList *l, bool hidden, bool locked);
+ /**
+ * to return a list of all the selected items
+ *
+ */
GSList * all_selection_items (Inkscape::Selection *s, GSList *l, SPObject *ancestor, bool hidden, bool locked);
+ /**
+ * Shrink the dialog size when the expander widget is closed
+ * Currently not working, no known way to do this
+ */
void squeeze_window();
+ /**
+ * Can be invoked for setting the desktop. Currently not used.
+ */
+ void setDesktop(SPDesktop *desktop);
+ /**
+ * Is invoked by the desktop tracker when the desktop changes.
+ */
+ void setTargetDesktop(SPDesktop *desktop);
+ /**
+ * Called when desktop selection changes
+ */
+ void onSelectionChange(void);
+
private:
Find(Find const &d);
Find& operator=(Find const &d);
-
- Inkscape::UI::Widget::Button _button_clear;
- Inkscape::UI::Widget::Button _button_find;
+
+ /*
+ * Find and replace combo box widgets
+ */
+ Inkscape::UI::Widget::Entry entry_find;
+ Inkscape::UI::Widget::Entry entry_replace;
+
+ /**
+ * Scope and search in widgets
+ */
+ Inkscape::UI::Widget::RadioButton check_scope_all;
+ Inkscape::UI::Widget::RadioButton check_scope_layer;
+ Inkscape::UI::Widget::RadioButton check_scope_selection;
+ Inkscape::UI::Widget::RadioButton check_searchin_text;
+ Inkscape::UI::Widget::RadioButton check_searchin_property;
+ Gtk::HBox hbox_searchin;
+ Gtk::VBox vbox_scope;
+ Gtk::VBox vbox_searchin;
+ Gtk::Frame frame_searchin;
+ Gtk::Frame frame_scope;
+
+ /**
+ * General option widgets
+ */
+ Inkscape::UI::Widget::CheckButton check_case_sensitive;
+ Inkscape::UI::Widget::CheckButton check_exact_match;
+ Inkscape::UI::Widget::CheckButton check_include_hidden;
+ Inkscape::UI::Widget::CheckButton check_include_locked;
+ Gtk::VBox vbox_options1;
+ Gtk::VBox vbox_options2;
+ Gtk::HBox hbox_options;
+ Gtk::VBox vbox_expander;
+ Gtk::Expander expander_options;
+ Gtk::Frame frame_options;
+
+ /**
+ * Property type widgets
+ */
+ Inkscape::UI::Widget::CheckButton check_ids;
+ Inkscape::UI::Widget::CheckButton check_attributename;
+ Inkscape::UI::Widget::CheckButton check_attributevalue;
+ Inkscape::UI::Widget::CheckButton check_style;
+ Inkscape::UI::Widget::CheckButton check_font;
+ Gtk::VBox vbox_properties;
+ Gtk::HBox hbox_properties1;
+ Gtk::HBox hbox_properties2;
+ Gtk::Frame frame_properties;
+
+ /**
+ * A vector of all the properties widgets for easy processing
+ */
+ std::vector<Inkscape::UI::Widget::CheckButton *> checkProperties;
+
+ /**
+ * Object type widgets
+ */
+ Inkscape::UI::Widget::CheckButton check_alltypes;
+ Inkscape::UI::Widget::CheckButton check_rects;
+ Inkscape::UI::Widget::CheckButton check_ellipses;
+ Inkscape::UI::Widget::CheckButton check_stars;
+ Inkscape::UI::Widget::CheckButton check_spirals;
+ Inkscape::UI::Widget::CheckButton check_paths;
+ Inkscape::UI::Widget::CheckButton check_texts;
+ Inkscape::UI::Widget::CheckButton check_groups;
+ Inkscape::UI::Widget::CheckButton check_clones;
+ Inkscape::UI::Widget::CheckButton check_images;
+ Inkscape::UI::Widget::CheckButton check_offsets;
+ Gtk::VBox vbox_types1;
+ Gtk::VBox vbox_types2;
+ Gtk::HBox hbox_types;
+ Gtk::Frame frame_types;
+
+ /**
+ * A vector of all the check option widgets for easy processing
+ */
+ std::vector<Inkscape::UI::Widget::CheckButton *> checkTypes;
+
+ //Gtk::HBox hbox_text;
+
+ /**
+ * Action Buttons and status
+ */
+ Gtk::Label status;
+ Inkscape::UI::Widget::Button button_find;
+ Inkscape::UI::Widget::Button button_replace;
+ Gtk::HButtonBox box_buttons;
+ Gtk::HBox hboxbutton_row;
+
+ /**
+ * Finding or replacing
+ */
+ bool _action_replace;
+ bool blocked;
+
+ SPDesktop *desktop;
+ DesktopTracker deskTrack;
+ sigc::connection desktopChangeConn;
+ sigc::connection selectChangedConn;
};
} // namespace Dialog
diff --git a/src/ui/dialog/guides.cpp b/src/ui/dialog/guides.cpp
index 61b4fe389..088290b6f 100644
--- a/src/ui/dialog/guides.cpp
+++ b/src/ui/dialog/guides.cpp
@@ -54,7 +54,7 @@ GuidelinePropertiesDialog::GuidelinePropertiesDialog(SPGuide *guide, SPDesktop *
}
bool GuidelinePropertiesDialog::_relative_toggle_status = false; // initialize relative checkbox status for when this dialog is opened for first time
-Glib::ustring GuidelinePropertiesDialog::_angle_unit_status = "deg"; // initialize angle unit status
+Glib::ustring GuidelinePropertiesDialog::_angle_unit_status = DEG; // initialize angle unit status
GuidelinePropertiesDialog::~GuidelinePropertiesDialog() {
// save current status
@@ -87,7 +87,7 @@ void GuidelinePropertiesDialog::_modeChanged()
_spin_button_x.setValue(0);
} else {
// absolute
- _spin_angle.setValueKeepUnit(_oldangle, "deg");
+ _spin_angle.setValueKeepUnit(_oldangle, DEG);
_spin_button_x.setValueKeepUnit(_oldpos[Geom::X], "px");
_spin_button_y.setValueKeepUnit(_oldpos[Geom::Y], "px");
@@ -96,7 +96,7 @@ void GuidelinePropertiesDialog::_modeChanged()
void GuidelinePropertiesDialog::_onApply()
{
- double deg_angle = _spin_angle.getValue("deg");
+ double deg_angle = _spin_angle.getValue(DEG);
if (!_mode)
deg_angle += _oldangle;
Geom::Point normal;
diff --git a/src/ui/dialog/transformation.cpp b/src/ui/dialog/transformation.cpp
index 9b65c1f68..1951dc92a 100644
--- a/src/ui/dialog/transformation.cpp
+++ b/src/ui/dialog/transformation.cpp
@@ -724,7 +724,7 @@ void Transformation::applyPageScale(Inkscape::Selection *selection)
void Transformation::applyPageRotate(Inkscape::Selection *selection)
{
- double angle = _scalar_rotate.getValue("deg");
+ double angle = _scalar_rotate.getValue(DEG);
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
if (prefs->getBool("/dialogs/transformation/applyseparately")) {
diff --git a/src/ui/dialog/xml-tree.cpp b/src/ui/dialog/xml-tree.cpp
index cdbb2257c..ae1ebf5ca 100644
--- a/src/ui/dialog/xml-tree.cpp
+++ b/src/ui/dialog/xml-tree.cpp
@@ -233,7 +233,7 @@ XmlTree::XmlTree (void) :
GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(attributes));
g_signal_connect (G_OBJECT(selection), "changed", G_CALLBACK (on_attr_select_row), this);
-
+ g_signal_connect( G_OBJECT(attributes), "row-value-changed", G_CALLBACK(on_attr_row_changed), this);
xml_element_new_button.signal_clicked().connect(sigc::mem_fun(*this, &XmlTree::cmd_new_element_node));
xml_text_new_button.signal_clicked().connect(sigc::mem_fun(*this, &XmlTree::cmd_new_text_node));
@@ -738,11 +738,12 @@ void XmlTree::on_attr_select_row(GtkTreeSelection *selection, gpointer data)
GtkTreeModel *model;
if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
-/* self->selected_attr = 0;
+ // Nothing selected
+ self->selected_attr = 0;
self->attr_reset_context(self->selected_attr);
self->xml_attribute_delete_button.set_sensitive(false);
- self->on_attr_unselect_row_clear_text();*/
+ self->on_attr_unselect_row_clear_text();
return;
}
@@ -763,6 +764,22 @@ void XmlTree::on_attr_select_row(GtkTreeSelection *selection, gpointer data)
}
+void XmlTree::on_attr_row_changed(SPXMLViewAttrList *attributes, const gchar * name, gpointer data)
+{
+ // Reselect the selected row if the data changes to refresh the attribute and value edit boxes.
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(attributes));
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ const gchar *attr_name;
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gtk_tree_model_get (model, &iter, 0, &attr_name, -1);
+ if (!strcmp(name, attr_name)) {
+ gtk_tree_selection_unselect_all(selection);
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+ }
+}
+
void XmlTree::on_attr_unselect_row_clear_text()
{
attr_name.set_text("");
diff --git a/src/ui/dialog/xml-tree.h b/src/ui/dialog/xml-tree.h
index eeb828771..bf43f5ae8 100644
--- a/src/ui/dialog/xml-tree.h
+++ b/src/ui/dialog/xml-tree.h
@@ -126,11 +126,16 @@ private:
static void after_tree_move(GtkCTree *tree, GtkCTreeNode *node, GtkCTreeNode *new_parent, GtkCTreeNode *new_sibling, gpointer data);
/**
- * Callback functions for when attribute selection changes
+ * Callback for when attribute selection changes
*/
static void on_attr_select_row(GtkTreeSelection *selection, gpointer data);
/**
+ * Callback for when attribute list values change
+ */
+ static void on_attr_row_changed(SPXMLViewAttrList *attributes, const gchar * name, gpointer data);
+
+ /**
* Enable widgets based on current selections
*/
void on_tree_select_row_enable(GtkCTreeNode *node);
diff --git a/src/ui/widget/button.cpp b/src/ui/widget/button.cpp
index 1ba531ddf..bac866920 100644
--- a/src/ui/widget/button.cpp
+++ b/src/ui/widget/button.cpp
@@ -31,6 +31,22 @@ CheckButton::CheckButton(Glib::ustring const &label, Glib::ustring const &toolti
set_tooltip_text(tooltip);
}
+CheckButton::CheckButton(Glib::ustring const &label, Glib::ustring const &tooltip, bool active)
+{
+ set_use_underline (true);
+ set_label (label);
+ set_tooltip_text(tooltip);
+ set_active(active);
+}
+
+RadioButton::RadioButton(Glib::ustring const &label, Glib::ustring const &tooltip)
+{
+ set_use_underline (true);
+ set_label (label);
+ set_tooltip_text(tooltip);
+}
+
+
} // namespace Widget
} // namespace UI
} // namespace Inkscape
diff --git a/src/ui/widget/button.h b/src/ui/widget/button.h
index 362cdf8ff..b219fc3db 100644
--- a/src/ui/widget/button.h
+++ b/src/ui/widget/button.h
@@ -11,6 +11,7 @@
#define INKSCAPE_UI_WIDGET_BUTTON_H
#include <gtkmm/checkbutton.h>
+#include <gtkmm/radiobutton.h>
namespace Inkscape {
namespace UI {
@@ -34,6 +35,19 @@ class CheckButton : public Gtk::CheckButton
public:
CheckButton();
CheckButton(Glib::ustring const &label, Glib::ustring const &tooltip);
+ CheckButton(Glib::ustring const &label, Glib::ustring const &tooltip, bool active);
+
+};
+
+/**
+ * RadioButton widget.
+ */
+class RadioButton : public Gtk::RadioButton
+{
+public:
+ RadioButton();
+ RadioButton(Glib::ustring const &label, Glib::ustring const &tooltip);
+
};
} // namespace Widget
diff --git a/src/ui/widget/entry.cpp b/src/ui/widget/entry.cpp
index 7ac8532fb..173e014d9 100644
--- a/src/ui/widget/entry.cpp
+++ b/src/ui/widget/entry.cpp
@@ -24,7 +24,6 @@ Entry::Entry( Glib::ustring const &label, Glib::ustring const &tooltip,
: Labelled(label, tooltip, new Gtk::Entry(), suffix, icon, mnemonic)
{
}
-
} // namespace Widget
} // namespace UI
diff --git a/src/ui/widget/entry.h b/src/ui/widget/entry.h
index d332dde1b..53b848fc9 100644
--- a/src/ui/widget/entry.h
+++ b/src/ui/widget/entry.h
@@ -11,8 +11,10 @@
#define INKSCAPE_UI_WIDGET_ENTRY__H
#include "labelled.h"
+#include <gtkmm.h>
#include <gtkmm/entry.h>
+#include <gtkmm/comboboxtext.h>
namespace Inkscape {
namespace UI {