diff options
| author | su_v <suv-sf@users.sourceforge.net> | 2012-10-12 17:18:08 +0000 |
|---|---|---|
| committer | ~suv <suv-sf@users.sourceforge.net> | 2012-10-12 17:18:08 +0000 |
| commit | d997c6a08a0d1184e8a4aec708033d0f548802f8 (patch) | |
| tree | e644b11bedb210813006dbfe4fb6a703bb642fae /src | |
| parent | merge from trunk (r11761) (diff) | |
| parent | Fix for compiling with pre gtkmm 2.24 libraries. (diff) | |
| download | inkscape-d997c6a08a0d1184e8a4aec708033d0f548802f8.tar.gz inkscape-d997c6a08a0d1184e8a4aec708033d0f548802f8.zip | |
merge from trunk (r11787)
(bzr r11668.1.28)
Diffstat (limited to 'src')
39 files changed, 1359 insertions, 176 deletions
diff --git a/src/desktop.cpp b/src/desktop.cpp index ca981a458..fa0c8647f 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -55,6 +55,7 @@ #include "document.h" #include "event-log.h" #include "helper/units.h" +#include "interface.h" #include "inkscape-private.h" #include "layer-fns.h" #include "layer-manager.h" @@ -496,13 +497,51 @@ void SPDesktop::setCurrentLayer(SPObject *object) { _layer_hierarchy->setBottom(object); } -void SPDesktop::toggleAllLayers(bool hide) { +void SPDesktop::toggleHideAllLayers(bool hide) { - for ( SPObject* obj = currentRoot(); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) { + for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), currentRoot()); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) { SP_ITEM(obj)->setHidden(hide); } } +void SPDesktop::toggleLockAllLayers(bool lock) { + + for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), currentRoot()); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) { + SP_ITEM(obj)->setLocked(lock); + } +} + +void SPDesktop::toggleLockOtherLayers(SPObject *object) { + g_return_if_fail(SP_IS_GROUP(object)); + g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) ); + + bool othersLocked = false; + std::vector<SPObject*> layers; + for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) { + // Dont lock any ancestors, since that would in turn lock the layer as well + if (!obj->isAncestorOf(object)) { + layers.push_back(obj); + othersLocked |= !SP_ITEM(obj)->isLocked(); + } + } + for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) { + if (!obj->isAncestorOf(object)) { + layers.push_back(obj); + othersLocked |= !SP_ITEM(obj)->isLocked(); + } + } + + SPItem *item = SP_ITEM(object); + if ( item->isLocked() ) { + item->setLocked(false); + } + + for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) { + SP_ITEM(*it)->setLocked(othersLocked); + } +} + + void SPDesktop::toggleLayerSolo(SPObject *object) { g_return_if_fail(SP_IS_GROUP(object)); g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) ); @@ -1309,6 +1348,17 @@ SPDesktop::toggleScrollbars() _widget->toggleScrollbars(); } + +void SPDesktop::toggleToolbar(gchar const *toolbar_name) +{ + Glib::ustring pref_path = getLayoutPrefPath(this) + toolbar_name + "/state"; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gboolean visible = prefs->getBool(pref_path, true); + prefs->setBool(pref_path, !visible); + + layoutWidget(); +} + void SPDesktop::layoutWidget() { @@ -1465,7 +1515,6 @@ void SPDesktop::toggleSnapGlobal() namedview->setSnapGlobal(!v); } - //---------------------------------------------------------------------- // Callback implementations. The virtual ones are connected by the view. diff --git a/src/desktop.h b/src/desktop.h index 919cd905e..529199692 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -280,7 +280,9 @@ public: void setCurrentLayer(SPObject *object); void toggleLayerSolo(SPObject *object); - void toggleAllLayers(bool hidden); + void toggleHideAllLayers(bool hide); + void toggleLockAllLayers(bool lock); + void toggleLockOtherLayers(SPObject *object); SPObject *layerForObject(SPObject *object); bool isLayer(SPObject *object) const; bool isWithinViewport(SPItem *item) const; @@ -364,6 +366,7 @@ public: void toggleSnapGlobal(); bool gridsEnabled() const { return grids_visible; }; void showGrids(bool show, bool dirty_document = true); + void toggleToolbar(gchar const *toolbar_name); bool is_iconified(); bool is_maximized(); diff --git a/src/document.cpp b/src/document.cpp index 9d8291db0..172037518 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -7,10 +7,12 @@ * bulia byak <buliabyak@users.sf.net> * Jon A. Cruz <jon@joncruz.org> * Abhishek Sharma + * Tavmjong Bah <tavmjong@free.fr> * * Copyright (C) 2004-2005 MenTaLguY * Copyright (C) 1999-2002 Lauris Kaplinski * Copyright (C) 2000-2001 Ximian, Inc. + * Copyright (C) 2012 Tavmjong Bah * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -59,6 +61,7 @@ #include "sp-item-group.h" #include "sp-namedview.h" #include "sp-object-repr.h" +#include "sp-symbol.h" #include "transf_mat_3x4.h" #include "unit-constants.h" #include "xml/repr.h" @@ -1454,9 +1457,10 @@ void SPDocument::importDefs(SPDocument *source) for (Inkscape::XML::Node *def = defs->firstChild() ; def ; def = def->next()) { - // Prevent duplicates of solid swatches by checking if equivalent swatch already exists gboolean duplicate = false; SPObject *src = source->getObjectByRepr(def); + + // Prevent duplicates of solid swatches by checking if equivalent swatch already exists if (src && SP_IS_GRADIENT(src)) { SPGradient *gr = SP_GRADIENT(src); if (gr->isSolid() || gr->getVector()->isSolid()) { @@ -1473,6 +1477,35 @@ void SPDocument::importDefs(SPDocument *source) } } + // Prevent duplication of symbols... could be more clever. + // The tag "_inkscape_duplicate" is added to "id" by ClipboardManagerImpl::copySymbol(). + // We assume that symbols are in defs section (not required by SVG spec). + if (src && SP_IS_SYMBOL(src)) { + + Glib::ustring id = src->getRepr()->attribute("id"); + size_t pos = id.find( "_inkscape_duplicate" ); + if( pos != Glib::ustring::npos ) { + + // This is our symbol, now get rid of tag + id.erase( pos ); + + // Check that it really is a duplicate + for (SPObject *trg = this->getDefs()->firstChild() ; trg ; trg = trg->getNext()) { + if( trg && SP_IS_SYMBOL(trg) && src != trg ) { + std::string id2 = trg->getRepr()->attribute("id"); + + if( !id.compare( id2 ) ) { + duplicate = true; + break; + } + } + } + if ( !duplicate ) { + src->getRepr()->setAttribute("id", id.c_str() ); + } + } + } + if (!duplicate) { Inkscape::XML::Node * dup = def->duplicate(this->getReprDoc()); target_defs->appendChild(dup); diff --git a/src/extension/implementation/implementation.h b/src/extension/implementation/implementation.h index b0230b7c4..fb323cd78 100644 --- a/src/extension/implementation/implementation.h +++ b/src/extension/implementation/implementation.h @@ -104,6 +104,7 @@ public: virtual bool check(Inkscape::Extension::Extension * /*module*/) { return true; } virtual bool cancelProcessing () { return true; } + virtual bool wasCancelled () { return false; } virtual void commitDocument () {} // ----- Input functions ----- diff --git a/src/extension/input.cpp b/src/extension/input.cpp index 1662ef073..5cef38009 100644 --- a/src/extension/input.cpp +++ b/src/extension/input.cpp @@ -152,6 +152,10 @@ Input::open (const gchar *uri) SPDocument *const doc = imp->open(this, uri); + if (imp->wasCancelled()) { + throw Input::open_cancelled(); + } + return doc; } @@ -227,8 +231,7 @@ Input::prefs (const gchar *uri) delete dialog; - if (response == Gtk::RESPONSE_OK) return true; - return false; + return (response == Gtk::RESPONSE_OK); } } } /* namespace Inkscape, Extension */ diff --git a/src/extension/input.h b/src/extension/input.h index 8b198495e..b01ffeb86 100644 --- a/src/extension/input.h +++ b/src/extension/input.h @@ -39,6 +39,10 @@ public: virtual ~no_extension_found() throw() {} const char *what() const throw() { return "No suitable input extension found"; } }; + struct open_cancelled : public std::exception { + virtual ~open_cancelled() throw() {} + const char *what() const throw() { return "Open was cancelled"; } + }; Input (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp); diff --git a/src/extension/internal/pdfinput/pdf-input.cpp b/src/extension/internal/pdfinput/pdf-input.cpp index 6b6107444..d2d594017 100644 --- a/src/extension/internal/pdfinput/pdf-input.cpp +++ b/src/extension/internal/pdfinput/pdf-input.cpp @@ -588,12 +588,19 @@ void PdfImportDialog::_setPreviewPage(int page) { //////////////////////////////////////////////////////////////////////////////// +bool +PdfInput::wasCancelled () { + return _cancelled; +} + /** * Parses the selected page of the given PDF document using PdfParser. */ SPDocument * PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { + _cancelled = false; + // Initialize the globalParams variable for poppler if (!globalParams) { globalParams = new GlobalParams(); @@ -648,6 +655,7 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { if (inkscape_use_gui()) { dlg = new PdfImportDialog(pdf_doc, uri); if (!dlg->showDialog()) { + _cancelled = true; delete dlg; delete pdf_doc; return NULL; diff --git a/src/extension/internal/pdfinput/pdf-input.h b/src/extension/internal/pdfinput/pdf-input.h index 75fcfa69a..e9da5b27c 100644 --- a/src/extension/internal/pdfinput/pdf-input.h +++ b/src/extension/internal/pdfinput/pdf-input.h @@ -135,7 +135,9 @@ public: SPDocument *open( Inkscape::Extension::Input *mod, const gchar *uri ); static void init( void ); - + virtual bool wasCancelled(); +private: + bool _cancelled; }; } // namespace Implementation diff --git a/src/extension/system.cpp b/src/extension/system.cpp index fc20095c5..7fb6e1591 100644 --- a/src/extension/system.cpp +++ b/src/extension/system.cpp @@ -144,6 +144,7 @@ SPDocument *open(Extension *key, gchar const *filename) } SPDocument *doc = imod->open(filename); + if (!doc) { throw Input::open_failed(); } diff --git a/src/file.cpp b/src/file.cpp index 778306d5d..a03c459da 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -235,12 +235,16 @@ bool sp_file_open(const Glib::ustring &uri, } SPDocument *doc = NULL; + bool cancelled = false; try { doc = Inkscape::Extension::open(key, uri.c_str()); } catch (Inkscape::Extension::Input::no_extension_found &e) { doc = NULL; } catch (Inkscape::Extension::Input::open_failed &e) { doc = NULL; + } catch (Inkscape::Extension::Input::open_cancelled &e) { + doc = NULL; + cancelled = true; } if (desktop) { @@ -287,7 +291,7 @@ bool sp_file_open(const Glib::ustring &uri, } return TRUE; - } else { + } else if (!cancelled) { gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str()); gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), safeUri); sp_ui_error_dialog(text); @@ -295,6 +299,8 @@ bool sp_file_open(const Glib::ustring &uri, g_free(safeUri); return FALSE; } + + return FALSE; } /** diff --git a/src/inkscape.cpp b/src/inkscape.cpp index 0a94d0742..fc9a9783f 100644 --- a/src/inkscape.cpp +++ b/src/inkscape.cpp @@ -312,8 +312,8 @@ static gint inkscape_autosave(gpointer) // Try to create the autosave directory if it doesn't exist if (g_mkdir(autosave_dir.c_str(), 0755)) { // the creation failed - Glib::ustring msg = Glib::ustring::format( - _("Autosave failed! Cannot create directory "), Glib::filename_to_utf8(autosave_dir)); + Glib::ustring msg = Glib::ustring::compose( + _("Autosave failed! Cannot create directory %1."), Glib::filename_to_utf8(autosave_dir)); g_warning("%s", msg.c_str()); SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, msg.c_str()); return TRUE; @@ -321,8 +321,8 @@ static gint inkscape_autosave(gpointer) // Try to read dir again autosave_dir_ptr = g_dir_open(autosave_dir.c_str(), 0, NULL); if( !autosave_dir_ptr ){ - Glib::ustring msg = Glib::ustring::format( - _("Autosave failed! Cannot open directory "), Glib::filename_to_utf8(autosave_dir)); + Glib::ustring msg = Glib::ustring::compose( + _("Autosave failed! Cannot open directory %1."), Glib::filename_to_utf8(autosave_dir)); g_warning("%s", msg.c_str()); SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, msg.c_str()); return TRUE; diff --git a/src/interface.cpp b/src/interface.cpp index 2ee188ef7..bad95adc6 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -527,7 +527,7 @@ static GtkWidget *sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb } // end of sp_ui_menu_append_item_from_verb -static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view ) +Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view ) { Glib::ustring prefPath; @@ -554,7 +554,7 @@ checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data) sp_ui_menu_activate(menuitem, action); } else if (pref) { - // The Show/Hide menu items without actions + // All check menu items should have actions now, but just in case Glib::ustring pref_path = getLayoutPrefPath( view ); pref_path += pref; pref_path += "/state"; @@ -596,19 +596,16 @@ static gboolean checkitem_update(GtkWidget *widget, GdkEventExpose * /*event*/, if (!strcmp(action->id, "ToggleGrid")) { ison = dt->gridsEnabled(); } - if (!strcmp(action->id, "ToggleGuides")) { + else if (!strcmp(action->id, "ToggleGuides")) { ison = dt->namedview->getGuides(); } - if (!strcmp(action->id, "ToggleSnapGlobal")) { + else if (!strcmp(action->id, "ToggleSnapGlobal")) { ison = dt->namedview->getSnapGlobal(); } - if (!strcmp(action->id, "ViewCmsToggle")) { + else if (!strcmp(action->id, "ViewCmsToggle")) { ison = dt->colorProfAdjustEnabled(); } - if (!strcmp(action->id, "ToggleRulers")) { - ison = getViewStateFromPref(view, pref); - } - if (!strcmp(action->id, "ToggleScrollbars")) { + else { ison = getViewStateFromPref(view, pref); } } else if (pref) { @@ -896,22 +893,22 @@ sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view) { //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu", // checkitem_toggled, checkitem_update, 0); - sp_ui_menu_append_check_item_from_verb(m, view, _("_Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands", - checkitem_toggled, checkitem_update, 0); - sp_ui_menu_append_check_item_from_verb(m, view, _("Sn_ap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox", - checkitem_toggled, checkitem_update, 0); - sp_ui_menu_append_check_item_from_verb(m, view, _("T_ool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel", - checkitem_toggled, checkitem_update, 0); - sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox", - checkitem_toggled, checkitem_update, 0); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "commands", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_COMMANDS_TOOLBAR)); + sp_ui_menu_append_check_item_from_verb(m, view,NULL, NULL, "snaptoolbox", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SNAP_TOOLBAR)); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "toppanel", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_TOOL_TOOLBAR)); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "toolbox", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_TOOLBOX)); sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers", checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS)); sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars", checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS)); - sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels", - checkitem_toggled, checkitem_update, 0); - sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar", - checkitem_toggled, checkitem_update, 0); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "panels", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_PALETTE)); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "statusbar", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_STATUSBAR)); } diff --git a/src/interface.h b/src/interface.h index 891e39957..2e9e7fc2e 100644 --- a/src/interface.h +++ b/src/interface.h @@ -81,6 +81,7 @@ GtkWidget *sp_ui_main_menubar (Inkscape::UI::View::View *view); void sp_menu_append_recent_documents (GtkWidget *menu); void sp_ui_dialog_title_string (Inkscape::Verb * verb, gchar* c); +Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view ); /** * diff --git a/src/layer-manager.cpp b/src/layer-manager.cpp index 894758a97..c02d75d16 100644 --- a/src/layer-manager.cpp +++ b/src/layer-manager.cpp @@ -159,53 +159,66 @@ void LayerManager::setCurrentLayer( SPObject* obj ) } } -void LayerManager::renameLayer( SPObject* obj, gchar const *label, bool uniquify ) +/* + * Return a unique layer name similar to param label + * A unique name is made by substituting or appending the label's number suffix with + * the next unique larger number suffix not already used for any layer name + */ +Glib::ustring LayerManager::getNextLayerName( SPObject* obj, gchar const *label) { - Glib::ustring incoming( label ? label : "" ); + Glib::ustring incoming( label ? label : "Layer 1" ); Glib::ustring result(incoming); Glib::ustring base(incoming); + Glib::ustring split(" "); guint startNum = 1; - if (uniquify) { + gint pos = base.length()-1; + while (pos >= 0 && g_ascii_isdigit(base[pos])) { + pos-- ; + } - Glib::ustring::size_type pos = base.rfind('#'); - if ( pos != Glib::ustring::npos ) { - gchar* numpart = g_strdup(base.substr(pos+1).c_str()); - if ( numpart ) { - gchar* endPtr = NULL; - guint64 val = g_ascii_strtoull( numpart, &endPtr, 10); - if ( ((val > 0) || (endPtr != numpart)) && (val < 65536) ) { - base.erase( pos ); - result = base; - startNum = static_cast<int>(val); - } - g_free(numpart); - } + gchar* numpart = g_strdup(base.substr(pos+1).c_str()); + if ( numpart ) { + gchar* endPtr = NULL; + guint64 val = g_ascii_strtoull( numpart, &endPtr, 10); + if ( ((val > 0) || (endPtr != numpart)) && (val < 65536) ) { + base.erase( pos+1); + result = incoming; + startNum = static_cast<int>(val); + split = ""; } + g_free(numpart); + } - std::set<Glib::ustring> currentNames; - GSList const *layers=_document->getResourceList("layer"); - SPObject *root=_desktop->currentRoot(); - if ( root ) { - for ( GSList const *iter=layers ; iter ; iter = iter->next ) { - SPObject *layer=static_cast<SPObject *>(iter->data); - if ( layer != obj ) { - currentNames.insert( layer->label() ? Glib::ustring(layer->label()) : Glib::ustring() ); - } + std::set<Glib::ustring> currentNames; + GSList const *layers=_document->getResourceList("layer"); + SPObject *root=_desktop->currentRoot(); + if ( root ) { + for ( GSList const *iter=layers ; iter ; iter = iter->next ) { + SPObject *layer=static_cast<SPObject *>(iter->data); + if ( layer != obj ) { + currentNames.insert( layer->label() ? Glib::ustring(layer->label()) : Glib::ustring() ); } } + } - // Not sure if we need to cap it, but we'll just be paranoid for the moment - // Intentionally unsigned - guint endNum = startNum + 3000; - for ( guint i = startNum; (i < endNum) && (currentNames.find(result) != currentNames.end()); i++ ) { - gchar* suffix = g_strdup_printf("#%d", i); - result = base; - result += suffix; + // Not sure if we need to cap it, but we'll just be paranoid for the moment + // Intentionally unsigned + guint endNum = startNum + 3000; + for ( guint i = startNum; (i < endNum) && (currentNames.find(result) != currentNames.end()); i++ ) { + result = Glib::ustring::format(base, split, i); + } - g_free(suffix); - } + return result; +} +void LayerManager::renameLayer( SPObject* obj, gchar const *label, bool uniquify ) +{ + Glib::ustring incoming( label ? label : "" ); + Glib::ustring result(incoming); + + if (uniquify) { + result = getNextLayerName(obj, label); } obj->setLabel( result.c_str() ); diff --git a/src/layer-manager.h b/src/layer-manager.h index fbb22d405..1b69324d5 100644 --- a/src/layer-manager.h +++ b/src/layer-manager.h @@ -30,6 +30,7 @@ public: void setCurrentLayer( SPObject* obj ); void renameLayer( SPObject* obj, gchar const *label, bool uniquify ); + Glib::ustring getNextLayerName( SPObject* obj, gchar const *label); sigc::connection connectCurrentLayerChanged(const sigc::slot<void, SPObject *> & slot) { return _layer_changed_signal.connect(slot); diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index df94a761e..13fea76c5 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -153,6 +153,32 @@ static int circle_circle_intersection(Circle const &circle0, Circle const &circl return 2; } +/** + * Find circle that touches inside of the curve, with radius matching the curvature, at time value \c t. + * Because this method internally uses unitTangentAt, t should be smaller than 1.0 (see unitTangentAt). + */ +Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.01 ) +{ + //Piecewise<SBasis> k = curvature(curve, tol); + D2<SBasis> dM=derivative(curve); + if ( are_near(L2sq(dM(t)),0.) ) { + dM=derivative(dM); + } + if ( are_near(L2sq(dM(t)),0.) ) { // try second time + dM=derivative(dM); + } + Piecewise<D2<SBasis> > unitv = unitVector(dM,tol); + Piecewise<SBasis> dMlength = dot(Piecewise<D2<SBasis> >(dM),unitv); + Piecewise<SBasis> k = cross(derivative(unitv),unitv); + k = divide(k,dMlength,tol,3); + double curv = k(t); // note that this value is signed + + Geom::Point normal = unitTangentAt(curve, t).cw(); + double radius = 1/curv; + Geom::Point center = curve(t) + radius*normal; + return Geom::Circle(center, fabs(radius)); +} + } // namespace Geom namespace Inkscape { @@ -209,7 +235,7 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : interpolator_type(_("Interpolator type:"), _("Determines which kind of interpolator will be used to interpolate between stroke width along the path"), "interpolator_type", InterpolatorTypeConverter, &wr, this, Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN), interpolator_beta(_("Smoothness:"), _("Sets the smoothness for the CubicBezierJohan interpolator; 0 = linear interpolation, 1 = smooth"), "interpolator_beta", &wr, this, 0.2), start_linecap_type(_("Start cap:"), _("Determines the shape of the path's start"), "start_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND), - linejoin_type(_("Join:"), _("Specifies the shape of the path's corners"), "linejoin_type", LineJoinTypeConverter, &wr, this, LINEJOIN_ROUND), + linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", LineJoinTypeConverter, &wr, this, LINEJOIN_ROUND), miter_limit(_("Miter limit:"), _("Maximum length of the miter (in units of stroke width)"), "miter_limit", &wr, this, 4.), end_linecap_type(_("End cap:"), _("Determines the shape of the path's end"), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND) { @@ -381,76 +407,26 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom:: break; } case LINEJOIN_EXTRP_MITER_ARC: { - Geom::Circle circle0; - Geom::Circle circle1; - Geom::Point tang0(0.,0.); - { - Geom::Point norm0(0.,0.); - Geom::Coord curv0 = 0; - std::vector<Geom::Point> derivs = reverse(B[prev_i]).valueAndDerivatives(0.,5); - for (unsigned deriv_n = 1, count = 0; deriv_n < derivs.size(); deriv_n++) { - Geom::Coord length = derivs[deriv_n].length(); - if ( ! Geom::are_near(length, 0) ) { - if (count == 0) { - tang0 = derivs[deriv_n] / length; - curv0 = length; // save the length of the tangent - count++; - } else { - // curv0 = need good way to calculate curvature - // calculate direction of normal - double angle = angle_between(tang0, derivs[deriv_n]); - if (angle >= 0 ) { - norm0 = tang0.ccw(); - } else { - norm0 = tang0.cw(); - } - break; // break out of for-loop - } - } - } - double r0 = curv0; - Geom::Point center0 = B[prev_i].at1() - r0*norm0; - circle0 = Geom::Circle(center0, r0); - } - Geom::Point tang1(0.,0.); - { - Geom::Point norm1(0.,0.); - Geom::Coord curv1 = 0; - std::vector<Geom::Point> derivs = B[i].valueAndDerivatives(0.,5); - for (unsigned deriv_n = 1, count = 0; deriv_n < derivs.size(); deriv_n++) { - Geom::Coord length = derivs[deriv_n].length(); - if ( ! Geom::are_near(length, 0) ) { - if (count == 0) { - tang1 = derivs[deriv_n] / length; - curv1 = length; // save the length of the tangent - count++; - } else { - //curv1 = length / curv1; // curvature = tangent' / tangent - // calculate direction of normal - double angle = angle_between(tang1, derivs[deriv_n]); - if (angle >= 0 ) { - norm1 = tang1.ccw(); - } else { - norm1 = tang1.cw(); - } - break; // break out of for-loop - } - } - } - double r1 = curv1; - Geom::Point center1 = B[i].at0() - r1*norm1; - circle1 = Geom::Circle(center1, r1); - } - + Geom::Circle circle1 = Geom::touching_circle(reverse(B[prev_i]),0.); + Geom::Circle circle2 = Geom::touching_circle(B[i],0.); Geom::Point points[2]; - int solutions = circle_circle_intersection(circle0, circle1, points[0], points[1]); + int solutions = circle_circle_intersection(circle1, circle2, points[0], points[1]); if (solutions == 2) { - Geom::Point sol = points[0]; - if ( dot(tang0,sol-B[prev_i].at1()) > 0 ) { + Geom::Point sol(0.,0.); + if ( dot(tang2,points[0]-B[i].at0()) > 0 ) { + // points[0] is bad, choose points[1] sol = points[1]; + } else if ( dot(tang2,points[1]-B[i].at0()) > 0 ) { // points[0] could be good, now check points[1] + // points[1] is bad, choose points[0] + sol = points[0]; + } else { + // both points are good, choose nearest + sol = ( distanceSq(B[i].at0(), points[0]) < distanceSq(B[i].at0(), points[1]) ) ? + points[0] : points[1]; } - Geom::EllipticalArc *arc0 = circle0.arc(B[prev_i].at1(), 0.5*(B[prev_i].at1()+sol), sol, true); - Geom::EllipticalArc *arc1 = circle1.arc(sol, 0.5*(sol+B[i].at0()), B[i].at0(), true); + + Geom::EllipticalArc *arc0 = circle1.arc(B[prev_i].at1(), 0.5*(B[prev_i].at1()+sol), sol, true); + Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+B[i].at0()), B[i].at0(), true); if (arc0) { build_from_sbasis(pb,arc0->toSBasis(), tol, false); @@ -462,13 +438,30 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom:: delete arc1; arc1 = NULL; } - } else if (solutions == 1) { // one circle is inside the other + + break; + } else { + // fall back to miter + boost::optional<Geom::Point> p = intersection_point( B[prev_i].at1(), tang1, + B[i].at0(), tang2 ); + if (p) { + // check size of miter + Geom::Point point_on_path = B[prev_i].at1() - rot90(tang1) * width; + Geom::Coord len = distance(*p, point_on_path); + if (len <= fabs(width) * miter_limit) { + // miter OK + pb.lineTo(*p); + } + } + pb.lineTo(B[i].at0()); + } + /*else if (solutions == 1) { // one circle is inside the other // don't know what to do: default to bevel pb.lineTo(B[i].at0()); } else { // no intersections // don't know what to do: default to bevel pb.lineTo(B[i].at0()); - } + } */ break; } diff --git a/src/main.cpp b/src/main.cpp index 578279929..844aded56 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -390,7 +390,7 @@ struct poptOption options[] = { {"export-text-to-path", 'T', POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH, - N_("Convert text object to paths on export (PS, EPS, PDF)"), + N_("Convert text object to paths on export (PS, EPS, PDF, SVG)"), NULL}, {"export-ignore-filters", 0, diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h index 09366a33f..fa39dbd3a 100644 --- a/src/menus-skeleton.h +++ b/src/menus-skeleton.h @@ -182,6 +182,7 @@ static char const menus_skeleton[] = " <submenu name=\"" N_("_Object") "\">\n" " <verb verb-id=\"DialogFillStroke\" />\n" " <verb verb-id=\"DialogObjectProperties\" />\n" +" <verb verb-id=\"DialogSymbols\" />\n" " <separator/>\n" " <verb verb-id=\"SelectionGroup\" />\n" " <verb verb-id=\"SelectionUnGroup\" />\n" @@ -198,6 +199,10 @@ static char const menus_skeleton[] = " <verb verb-id=\"ObjectsToPattern\" />\n" " <verb verb-id=\"ObjectsFromPattern\" />\n" " </submenu>\n" +" <submenu name=\"" N_("Symbo_l") "\">\n" +" <verb verb-id=\"ObjectsToSymbol\" />\n" +" <verb verb-id=\"ObjectsFromSymbol\" />\n" +" </submenu>\n" " <verb verb-id=\"ObjectsToMarker\" />\n" " <verb verb-id=\"ObjectsToGuides\" />\n" " <separator/>\n" diff --git a/src/path-prefix.h b/src/path-prefix.h index bdb6b35f7..be57ae354 100644 --- a/src/path-prefix.h +++ b/src/path-prefix.h @@ -34,6 +34,7 @@ extern "C" { # define INKSCAPE_PALETTESDIR BR_DATADIR( "/inkscape/palettes" ) # define INKSCAPE_PATTERNSDIR BR_DATADIR( "/inkscape/patterns" ) # define INKSCAPE_SCREENSDIR BR_DATADIR( "/inkscape/screens" ) +# define INKSCAPE_SYMBOLSDIR BR_DATADIR( "/inkscape/symbols" ) # define INKSCAPE_TUTORIALSDIR BR_DATADIR( "/inkscape/tutorials" ) # define INKSCAPE_TEMPLATESDIR BR_DATADIR( "/inkscape/templates" ) # define INKSCAPE_UIDIR BR_DATADIR( "/inkscape/ui" ) @@ -56,6 +57,7 @@ extern "C" { # define INKSCAPE_PALETTESDIR WIN32_DATADIR("share\\palettes") # define INKSCAPE_PATTERNSDIR WIN32_DATADIR("share\\patterns") # define INKSCAPE_SCREENSDIR WIN32_DATADIR("share\\screens") +# define INKSCAPE_SYMBOLSDIR WIN32_DATADIR("share\\symbols") # define INKSCAPE_TUTORIALSDIR WIN32_DATADIR("share\\tutorials") # define INKSCAPE_TEMPLATESDIR WIN32_DATADIR("share\\templates") # define INKSCAPE_UIDIR WIN32_DATADIR("share\\ui") @@ -77,6 +79,7 @@ extern "C" { # define INKSCAPE_PALETTESDIR "Contents/Resources/palettes" # define INKSCAPE_PATTERNSDIR "Contents/Resources/patterns" # define INKSCAPE_SCREENSDIR "Contents/Resources/screens" +# define INKSCAPE_SYMBOLSDIR "Contents/Resources/symbols" # define INKSCAPE_TUTORIALSDIR "Contents/Resources/tutorials" # define INKSCAPE_TEMPLATESDIR "Contents/Resources/templates" # define INKSCAPE_UIDIR "Contents/Resources/ui" @@ -98,6 +101,7 @@ extern "C" { # define INKSCAPE_PALETTESDIR INKSCAPE_DATADIR "/inkscape/palettes" # define INKSCAPE_PATTERNSDIR INKSCAPE_DATADIR "/inkscape/patterns" # define INKSCAPE_SCREENSDIR INKSCAPE_DATADIR "/inkscape/screens" +# define INKSCAPE_SYMBOLSDIR INKSCAPE_DATADIR "/inkscape/symbols" # define INKSCAPE_TUTORIALSDIR INKSCAPE_DATADIR "/inkscape/tutorials" # define INKSCAPE_TEMPLATESDIR INKSCAPE_DATADIR "/inkscape/templates" # define INKSCAPE_UIDIR INKSCAPE_DATADIR "/inkscape/ui" diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 332e9a34e..904e21960 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -11,6 +11,7 @@ * Martin Sucha <martin.sucha-inkscape@jts-sro.sk> * Abhishek Sharma * Kris De Gussem <Kris.DeGussem@gmail.com> + * Tavmjong Bah <tavmjong@free.fr> (Symbol additions) * * Copyright (C) 1999-2010,2012 authors * Copyright (C) 2001-2002 Ximian, Inc. @@ -71,6 +72,7 @@ SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; #include "sp-gradient-reference.h" #include "sp-linear-gradient-fns.h" #include "sp-pattern.h" +#include "sp-symbol.h" #include "sp-radial-gradient-fns.h" #include "gradient-context.h" #include "sp-namedview.h" @@ -2887,6 +2889,162 @@ void sp_selection_to_guides(SPDesktop *desktop) DocumentUndo::done(doc, SP_VERB_EDIT_SELECTION_2_GUIDES, _("Objects to guides")); } +/* + * Convert <g> to <symbol>, leaving all <use> elements referencing group unchanged. + */ +void sp_selection_symbol(SPDesktop *desktop, bool apply ) +{ + + if (desktop == NULL) { + return; + } + + SPDocument *doc = sp_desktop_document(desktop); + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); + + Inkscape::Selection *selection = sp_desktop_selection(desktop); + + // Check if something is selected. + if (selection->isEmpty()) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select one <b>group</b> to convert to symbol.")); + return; + } + + SPObject* group = selection->single(); + + // Make sure we have only one object in selection. + if( group == NULL ) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select only one <b>group</b> to convert to symbol.")); + return; + } + + // Make sure we convert the original. + if( SP_IS_USE( group ) ) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select original (<b>Shift+D</b>) to convert to symbol.")); + return; + } + + // Require that we really have a group. + if( !SP_IS_GROUP( group ) ) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Group selection first to convert to symbol.")); + return; + } + + doc->ensureUpToDate(); + + Inkscape::XML::Node *symbol = xml_doc->createElement("svg:symbol"); + symbol->setAttribute("style", group->getAttribute("style")); + symbol->setAttribute("title", group->getAttribute("title")); + symbol->setAttribute("transform", group->getAttribute("transform")); + + Glib::ustring id = group->getAttribute("id"); + + // Now we need to copy all children of group + GSList* children = group->childList(false); + children = g_slist_reverse(children); + for (GSList* i = children; i != NULL; i = i->next ) { + SPObject* child = SP_OBJECT(i->data); + Inkscape::XML::Node *dup = child->getRepr()->duplicate(xml_doc); + symbol->appendChild(dup); + child->deleteObject(true); + } + + // Need to delete <g>; all <use> elements that referenced <g> should + // auto-magically reference <symbol>. + doc->getDefs()->getRepr()->appendChild(symbol); + symbol->setAttribute("id",id.c_str()); // After we delete group with same id. + // Mysterious, must set symbol ID before deleting group or all <use> + // refering to symbol get turned into groups. (Linked to unlinking clones?) + group->deleteObject(true); + + Inkscape::GC::release(symbol); + selection->clear(); + // Group just disappears, nothing to select. + + // Need to signal Symbol dialog to update + + g_slist_free(children); + + DocumentUndo::done(doc, SP_VERB_EDIT_SYMBOL, _("Group to symbol")); +} + +/* + * Convert <symbol> to <g>. All <use> elements referencing symbol remain unchanged. + */ +void sp_selection_unsymbol(SPDesktop *desktop) +{ + + if (desktop == NULL) { + return; + } + + SPDocument *doc = sp_desktop_document(desktop); + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); + + Inkscape::Selection *selection = sp_desktop_selection(desktop); + + // Check if something is selected. + if (selection->isEmpty()) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select a <b>symbol</b> to extract objects from.")); + return; + } + + SPObject* use = selection->single(); + + // Make sure we have only one object in selection. + if( use == NULL ) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select only one <b>symbol</b> to convert to group.")); + return; + } + + // Require that we really have a <use> that references a <symbol>. + if( !SP_IS_USE( use ) && !SP_IS_SYMBOL( use->firstChild() ) ) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select only one <b>symbol</b> to convert to group.")); + return; + } + + doc->ensureUpToDate(); + + SPObject* symbol = use->firstChild(); + + Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); + group->setAttribute("style", symbol->getAttribute("style")); + group->setAttribute("title", symbol->getAttribute("title")); + group->setAttribute("transform", symbol->getAttribute("transform")); + + Glib::ustring id = symbol->getAttribute("id"); + + // Now we need to copy all children of symbol + GSList* children = symbol->childList(false); + children = g_slist_reverse(children); + for (GSList* i = children; i != NULL; i = i->next ) { + SPObject* child = SP_OBJECT(i->data); + Inkscape::XML::Node *dup = child->getRepr()->duplicate(xml_doc); + group->appendChild(dup); + child->deleteObject(true); + } + + SPObject* parent = use->parent; // So we insert <g> next to <use> (easier to find) + + // Need to delete <symbol>; all other <use> elements that referenced <symbol> should + // auto-magically reference <g>. + symbol->deleteObject(true); + group->setAttribute("id",id.c_str()); // After we delete symbol with same id. + parent->getRepr()->appendChild(group); + //use->deleteObject(true); + + SPItem *group_item = static_cast<SPItem *>(sp_desktop_document(desktop)->getObjectByRepr(group)); + Inkscape::GC::release(group); + selection->clear(); + selection->set(group_item); + + // Need to signal Symbol dialog to update + + g_slist_free(children); + + DocumentUndo::done(doc, SP_VERB_EDIT_UNSYMBOL, _("Group from symbol")); +} + void sp_selection_tile(SPDesktop *desktop, bool apply) { diff --git a/src/selection-chemistry.h b/src/selection-chemistry.h index 3c9c1aea9..5b35ded09 100644 --- a/src/selection-chemistry.h +++ b/src/selection-chemistry.h @@ -11,7 +11,7 @@ * Jon A. Cruz <jon@joncruz.org> * Abhishek Sharma * - * Copyright (C) 1999-2010 authors + * Copyright (C) 1999-2012 authors * Copyright (C) 2001-2002 Ximian, Inc. * * Released under GNU GPL, read the file 'COPYING' for more information @@ -67,6 +67,9 @@ void sp_selection_clone_original_path_lpe(SPDesktop *desktop); void sp_selection_to_marker(SPDesktop *desktop, bool apply = true); void sp_selection_to_guides(SPDesktop *desktop); +void sp_selection_symbol(SPDesktop *desktop, bool apply = true); +void sp_selection_unsymbol(SPDesktop *desktop); + void sp_selection_tile(SPDesktop *desktop, bool apply = true); void sp_selection_untile(SPDesktop *desktop); diff --git a/src/selection-describer.cpp b/src/selection-describer.cpp index c141c3da5..391db8950 100644 --- a/src/selection-describer.cpp +++ b/src/selection-describer.cpp @@ -24,6 +24,7 @@ #include "sp-offset.h" #include "sp-flowtext.h" #include "sp-use.h" +#include "sp-symbol.h" #include "sp-rect.h" #include "box3d.h" #include "sp-ellipse.h" @@ -192,7 +193,11 @@ void SelectionDescriber::_updateMessageFromSelection(Inkscape::Selection *select if (!items->next) { // one item char *item_desc = item->description(); - if (SP_IS_USE(item) || (SP_IS_OFFSET(item) && SP_OFFSET (item)->sourceHref)) { + if (SP_IS_USE(item) && SP_IS_SYMBOL(item->firstChild())) { + _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s. %s. %s.", + item_desc, in_phrase, + _("Convert symbol to group to edit"), _when_selected); + } else if (SP_IS_USE(item) || (SP_IS_OFFSET(item) && SP_OFFSET (item)->sourceHref)) { _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s. %s. %s.", item_desc, in_phrase, _("Use <b>Shift+D</b> to look up original"), _when_selected); diff --git a/src/sp-object.cpp b/src/sp-object.cpp index c16dbaeef..3e18c0835 100644 --- a/src/sp-object.cpp +++ b/src/sp-object.cpp @@ -1409,6 +1409,9 @@ void SPObject::_requireSVGVersion(Inkscape::Version version) { be made. The same applies to 'desc' elements. Therefore, these functions ignore all but the first 'title' child element and first 'desc' child element, except when deleting a title or description. + + This will change in SVG 2, where multiple 'title' and 'desc' elements will + be allowed with different localized strings. */ gchar * SPObject::title() const diff --git a/src/sp-symbol.cpp b/src/sp-symbol.cpp index 87cd210e4..56f862bf3 100644 --- a/src/sp-symbol.cpp +++ b/src/sp-symbol.cpp @@ -405,6 +405,11 @@ static Geom::OptRect sp_symbol_bbox(SPItem const *item, Geom::Affine const &tran Geom::Affine const a( symbol->c2p * transform ); bbox = ((SPItemClass *) (parent_class))->bbox(item, a, type); } + } else { + // Need bounding box for Symbols dialog + + Geom::Affine const a; + bbox = ((SPItemClass *) (parent_class))->bbox(item, a, type); } return bbox; } diff --git a/src/sp-use.cpp b/src/sp-use.cpp index e39f560c3..0f45f5444 100644 --- a/src/sp-use.cpp +++ b/src/sp-use.cpp @@ -321,6 +321,14 @@ sp_use_description(SPItem *item) char *ret; if (use->child) { + + if( SP_IS_SYMBOL( use->child ) ) { + //char *symbol_desc = SP_ITEM(use->child)->description(); + //g_free(symbol_desc); + return g_strdup(_("<b>Clone of Symbol</b>")); + //return g_strdup_printf(_("<b>Clone of Symbol</b>: %s"), symbol_desc ); + } + static unsigned recursion_depth = 0; if (recursion_depth >= 4) { /* TRANSLATORS: Used for statusbar description for long <use> chains: diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index f1f6c4ab4..8cdb37f4f 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -7,9 +7,11 @@ * Jon A. Cruz <jon@joncruz.org> * Incorporates some code from selection-chemistry.cpp, see that file for more credits. * Abhishek Sharma + * Tavmjong Bah * * Copyright (C) 2008 authors * Copyright (C) 2010 Jon A. Cruz + * Copyright (C) 2012 Tavmjong Bah (Symbol additions) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -66,6 +68,8 @@ #include "sp-mask.h" #include "sp-textpath.h" #include "sp-rect.h" +#include "sp-use.h" +#include "sp-symbol.h" #include "live_effects/lpeobject.h" #include "live_effects/lpeobject-reference.h" #include "live_effects/parameter/path.h" @@ -109,6 +113,7 @@ class ClipboardManagerImpl : public ClipboardManager { public: virtual void copy(SPDesktop *desktop); virtual void copyPathParameter(Inkscape::LivePathEffect::PathParam *); + virtual void copySymbol(Inkscape::XML::Node* symbol, gchar const* style); virtual bool paste(SPDesktop *desktop, bool in_place); virtual bool pasteStyle(SPDesktop *desktop); virtual bool pasteSize(SPDesktop *desktop, bool separately, bool apply_x, bool apply_y); @@ -295,6 +300,43 @@ void ClipboardManagerImpl::copyPathParameter(Inkscape::LivePathEffect::PathParam } /** + * Copy a symbol from the symbol dialog. + * @param symbol The Inkscape::XML::Node for the symbol. + */ +void ClipboardManagerImpl::copySymbol(Inkscape::XML::Node* symbol, gchar const* style) +{ + //std::cout << "ClipboardManagerImpl::copySymbol" << std::endl; + if ( symbol == NULL ) { + return; + } + + _discardInternalClipboard(); + _createInternalClipboard(); + + // We add "_duplicate" to have a well defined symbol name that + // bypasses the "prevent_id_classes" routine. We'll get rid of it + // when we paste. + Inkscape::XML::Node *repr = symbol->duplicate(_doc); + Glib::ustring symbol_name = repr->attribute("id"); + + symbol_name += "_inkscape_duplicate"; + repr->setAttribute("id", symbol_name.c_str()); + _defs->appendChild(repr); + + Glib::ustring id("#"); + id += symbol->attribute("id"); + + Inkscape::XML::Node *use = _doc->createElement("svg:use"); + use->setAttribute("xlink:href", id.c_str() ); + // Set a default style in <use> rather than <symbol> so it can be changed. + use->setAttribute("style", style ); + _root->appendChild(use); + + fit_canvas_to_drawing(_clipboardSPDoc); + _setClipboardTargets(); +} + +/** * Paste from the system clipboard into the active desktop. * @param in_place Whether to put the contents where they were when copied. */ @@ -725,6 +767,14 @@ void ClipboardManagerImpl::_copyUsedDefs(SPItem *item) } } + // Copy symbols: We may want to be more clever... + // if (SP_IS_USE(item)) { + // SPObject *symbol = SP_USE(item)->child; + // if( symbol && SP_IS_SYMBOL(symbol) ) { + // _copyNode(symbol->getRepr(), _doc, _defs); + // } + // } + // recurse for (SPObject *o = item->children ; o != NULL ; o = o->next) { if (SP_IS_ITEM(o)) { diff --git a/src/ui/clipboard.h b/src/ui/clipboard.h index fb28bfc14..b565740c3 100644 --- a/src/ui/clipboard.h +++ b/src/ui/clipboard.h @@ -25,6 +25,7 @@ class SPDesktop; namespace Inkscape { class Selection; +namespace XML { class Node; } namespace LivePathEffect { class PathParam; } namespace UI { @@ -43,6 +44,7 @@ class ClipboardManager { public: virtual void copy(SPDesktop *desktop) = 0; virtual void copyPathParameter(Inkscape::LivePathEffect::PathParam *) = 0; + virtual void copySymbol(Inkscape::XML::Node* symbol, gchar const* style) = 0; virtual bool paste(SPDesktop *desktop, bool in_place = false) = 0; virtual bool pasteStyle(SPDesktop *desktop) = 0; virtual bool pasteSize(SPDesktop *desktop, bool separately, bool apply_x, bool apply_y) = 0; diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert index ba2f53a9e..580b47522 100644 --- a/src/ui/dialog/Makefile_insert +++ b/src/ui/dialog/Makefile_insert @@ -89,6 +89,8 @@ ink_common_sources += \ ui/dialog/svg-fonts-dialog.h \ ui/dialog/swatches.cpp \ ui/dialog/swatches.h \ + ui/dialog/symbols.cpp \ + ui/dialog/symbols.h \ ui/dialog/text-edit.cpp \ ui/dialog/text-edit.h \ ui/dialog/tile.cpp \ diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index 60011ca9d..faba47769 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -33,6 +33,7 @@ #include "ui/dialog/memory.h" #include "ui/dialog/messages.h" #include "ui/dialog/scriptdialog.h" +#include "ui/dialog/symbols.h" #include "ui/dialog/tile.h" #include "ui/dialog/tracedialog.h" #include "ui/dialog/transformation.h" @@ -121,6 +122,7 @@ DialogManager::DialogManager() { registerFactory("SvgFontsDialog", &create<SvgFontsDialog, FloatingBehavior>); #endif registerFactory("Swatches", &create<SwatchesPanel, FloatingBehavior>); + registerFactory("Symbols", &create<SymbolsDialog, FloatingBehavior>); registerFactory("TileDialog", &create<TileDialog, FloatingBehavior>); registerFactory("Trace", &create<TraceDialog, FloatingBehavior>); registerFactory("Transformation", &create<Transformation, FloatingBehavior>); @@ -156,6 +158,7 @@ DialogManager::DialogManager() { registerFactory("SvgFontsDialog", &create<SvgFontsDialog, DockBehavior>); #endif registerFactory("Swatches", &create<SwatchesPanel, DockBehavior>); + registerFactory("Symbols", &create<SymbolsDialog, DockBehavior>); registerFactory("TileDialog", &create<TileDialog, DockBehavior>); registerFactory("Trace", &create<TraceDialog, DockBehavior>); registerFactory("Transformation", &create<Transformation, DockBehavior>); diff --git a/src/ui/dialog/layer-properties.cpp b/src/ui/dialog/layer-properties.cpp index 35a235dbc..4f9edf774 100644 --- a/src/ui/dialog/layer-properties.cpp +++ b/src/ui/dialog/layer-properties.cpp @@ -331,8 +331,11 @@ void LayerPropertiesDialog::Rename::perform(LayerPropertiesDialog &dialog) { void LayerPropertiesDialog::Create::setup(LayerPropertiesDialog &dialog) { dialog.set_title(_("Add Layer")); - //TODO: find an unused layer number, forming name from _("Layer ") + "%d" - dialog._layer_name_entry.set_text(_("Layer")); + + // Set the initial name to the "next available" layer name + LayerManager *mgr = dialog._desktop->layer_manager; + Glib::ustring newName = mgr->getNextLayerName(NULL, dialog._desktop->currentLayer()->label()); + dialog._layer_name_entry.set_text(newName.c_str()); dialog._apply_button.set_label(_("_Add")); dialog._setup_position_controls(); } diff --git a/src/ui/dialog/layers.cpp b/src/ui/dialog/layers.cpp index 70cf7075c..55a2f19a5 100644 --- a/src/ui/dialog/layers.cpp +++ b/src/ui/dialog/layers.cpp @@ -69,6 +69,9 @@ enum { BUTTON_SOLO, BUTTON_SHOW_ALL, BUTTON_HIDE_ALL, + BUTTON_LOCK_OTHERS, + BUTTON_LOCK_ALL, + BUTTON_UNLOCK_ALL, DRAGNDROP }; @@ -263,6 +266,21 @@ bool LayersPanel::_executeAction() _fireAction( SP_VERB_LAYER_HIDE_ALL ); } break; + case BUTTON_LOCK_OTHERS: + { + _fireAction( SP_VERB_LAYER_LOCK_OTHERS ); + } + break; + case BUTTON_LOCK_ALL: + { + _fireAction( SP_VERB_LAYER_LOCK_ALL ); + } + break; + case BUTTON_UNLOCK_ALL: + { + _fireAction( SP_VERB_LAYER_UNLOCK_ALL ); + } + break; case DRAGNDROP: { _doTreeMove( ); @@ -544,26 +562,36 @@ void LayersPanel::_handleButtonEvent(GdkEventButton* event) { static unsigned doubleclick = 0; - // TODO - fix to a better is-popup function if ( (event->type == GDK_BUTTON_PRESS) && (event->button == 3) ) { + // TODO - fix to a better is-popup function + Gtk::TreeModel::Path path; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + if ( _tree.get_path_at_pos( x, y, path ) ) { + _checkTreeSelection(); + _popupMenu.popup(event->button, event->time); + } + } - { - Gtk::TreeModel::Path path; - Gtk::TreeViewColumn* col = 0; - int x = static_cast<int>(event->x); - int y = static_cast<int>(event->y); - int x2 = 0; - int y2 = 0; - if ( _tree.get_path_at_pos( x, y, - path, col, - x2, y2 ) ) { - _checkTreeSelection(); - _popupMenu.popup(event->button, event->time); + if ( event->type == GDK_BUTTON_RELEASE && (event->button == 1) + && (event->state & GDK_SHIFT_MASK)) { + // Shift left click on the visible/lock columns toggles "solo" mode + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col = 0; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + int x2 = 0; + int y2 = 0; + if ( _tree.get_path_at_pos( x, y, path, col, x2, y2 ) ) { + if (col == _tree.get_column(COL_VISIBLE-1)) { + _takeAction(BUTTON_SOLO); + } else if (col == _tree.get_column(COL_LOCKED-1)) { + _takeAction(BUTTON_LOCK_OTHERS); } } - } + if ( (event->type == GDK_2BUTTON_PRESS) && (event->button == 1) ) { doubleclick = 1; } @@ -876,6 +904,12 @@ LayersPanel::LayersPanel() : _popupMenu.append(*manage(new Gtk::SeparatorMenuItem())); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOCK_OTHERS, 0, "Lock Others", (int)BUTTON_LOCK_OTHERS ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOCK_ALL, 0, "Lock All", (int)BUTTON_LOCK_ALL ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_UNLOCK_ALL, 0, "Unlock All", (int)BUTTON_UNLOCK_ALL ) ); + + _popupMenu.append(*manage(new Gtk::SeparatorMenuItem())); + _watchingNonTop.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RAISE, GTK_STOCK_GO_UP, "Up", (int)BUTTON_UP ) ); _watchingNonBottom.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOWER, GTK_STOCK_GO_DOWN, "Down", (int)BUTTON_DOWN ) ); diff --git a/src/ui/dialog/symbols.cpp b/src/ui/dialog/symbols.cpp new file mode 100644 index 000000000..21a178b57 --- /dev/null +++ b/src/ui/dialog/symbols.cpp @@ -0,0 +1,591 @@ +/** + * @file + * Symbols dialog. + */ +/* Authors: + * Copyright (C) 2012 Tavmjong Bah + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <iostream> +#include <algorithm> + +#include <gtkmm/buttonbox.h> +#include <gtkmm/label.h> +#include <gtkmm/table.h> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/comboboxtext.h> +#include <gtkmm/iconview.h> +#include <gtkmm/liststore.h> +#include <gtkmm/treemodelcolumn.h> +#include <gtkmm/clipboard.h> + +#include "path-prefix.h" +#include "io/sys.h" + +#include "ui/cache/svg_preview_cache.h" +#include "ui/clipboard.h" + +#include "symbols.h" + +#include "desktop.h" +#include "desktop-handles.h" +#include "document.h" +#include "inkscape.h" +#include "sp-root.h" +#include "sp-use.h" +#include "sp-symbol.h" + +#include "verbs.h" +#include "xml/repr.h" + +namespace Inkscape { +namespace UI { + +static Cache::SvgPreview svg_preview_cache; + +namespace Dialog { + + // See: http://developer.gnome.org/gtkmm/stable/classGtk_1_1TreeModelColumnRecord.html +class SymbolColumns : public Gtk::TreeModel::ColumnRecord +{ +public: + + Gtk::TreeModelColumn<Glib::ustring> symbol_id; + Gtk::TreeModelColumn<Glib::ustring> symbol_title; + Gtk::TreeModelColumn< Glib::RefPtr<Gdk::Pixbuf> > symbol_image; + + SymbolColumns() { + add(symbol_id); + add(symbol_title); + add(symbol_image); + } +}; + +SymbolColumns* SymbolsDialog::getColumns() +{ + SymbolColumns* columns = new SymbolColumns(); + return columns; +} + +/** + * Constructor + */ +SymbolsDialog::SymbolsDialog( gchar const* prefsPath ) : + UI::Widget::Panel("", prefsPath, SP_VERB_DIALOG_SYMBOLS), + store(Gtk::ListStore::create(*getColumns())), + iconView(0), + previewScale(0), + previewSize(0), + currentDesktop(0), + deskTrack(), + currentDocument(0), + previewDocument(0), + instanceConns() +{ + + /******************** Table *************************/ + // Replace by Grid for GTK 3.0 + Gtk::Table *table = new Gtk::Table(2, 4, false); + // panel is a cloked Gtk::VBox + _getContents()->pack_start(*Gtk::manage(table), Gtk::PACK_EXPAND_WIDGET); + guint row = 0; + + /******************** Symbol Sets *************************/ + Gtk::Label* labelSet = new Gtk::Label("Symbol set: "); + table->attach(*Gtk::manage(labelSet),0,1,row,row+1,Gtk::SHRINK,Gtk::SHRINK); + + symbolSet = new Gtk::ComboBoxText(); // Fill in later +#if WITH_GTKMM_2_24 + symbolSet->append("Current Document"); +#else + symbolSet->append_text("Current Document"); +#endif + symbolSet->set_active_text("Current Document"); + table->attach(*Gtk::manage(symbolSet),1,2,row,row+1,Gtk::FILL|Gtk::EXPAND,Gtk::SHRINK); + + sigc::connection connSet = + symbolSet->signal_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::rebuild)); + instanceConns.push_back(connSet); + + ++row; + + /********************* Icon View **************************/ + SymbolColumns* columns = getColumns(); + + iconView = new Gtk::IconView(static_cast<Glib::RefPtr<Gtk::TreeModel> >(store)); + //iconView->set_text_column( columns->symbol_id ); + iconView->set_tooltip_column( 1 ); + iconView->set_pixbuf_column( columns->symbol_image ); + + sigc::connection connIconChanged; + connIconChanged = + iconView->signal_selection_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::iconChanged)); + instanceConns.push_back(connIconChanged); + + Gtk::ScrolledWindow *scroller = new Gtk::ScrolledWindow(); + scroller->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS); + scroller->add(*Gtk::manage(iconView)); + table->attach(*Gtk::manage(scroller),0,2,row,row+1,Gtk::EXPAND|Gtk::FILL,Gtk::EXPAND|Gtk::FILL); + + ++row; + + /******************** Preview Scale ***********************/ + Gtk::Label* labelScale = new Gtk::Label("Preview scale: "); + table->attach(*Gtk::manage(labelScale),0,1,row,row+1,Gtk::SHRINK,Gtk::SHRINK); + + previewScale = new Gtk::ComboBoxText(); + const gchar *scales[] = + {"Fit", "Fit to width", "Fit to height", "0.1", "0.2", "0.5", "1.0", "2.0", "5.0", NULL}; + for( int i = 0; scales[i]; ++i ) { +#if WITH_GTKMM_2_24 + previewScale->append(scales[i]); +#else + previewScale->append_text(scales[i]); +#endif + } + previewScale->set_active_text(scales[0]); + table->attach(*Gtk::manage(previewScale),1,2,row,row+1,Gtk::FILL|Gtk::EXPAND,Gtk::SHRINK); + + sigc::connection connScale = + previewScale->signal_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::rebuild)); + instanceConns.push_back(connScale); + + ++row; + + /******************** Preview Size ************************/ + Gtk::Label* labelSize = new Gtk::Label("Preview size: "); + table->attach(*Gtk::manage(labelSize),0,1,row,row+1,Gtk::SHRINK,Gtk::SHRINK); + + previewSize = new Gtk::ComboBoxText(); + const gchar *sizes[] = {"16", "24", "32", "48", "64", NULL}; + for( int i = 0; sizes[i]; ++i ) { +#if WITH_GTKMM_2_24 + previewSize->append(sizes[i]); +#else + previewSize->append_text(sizes[i]); +#endif + + } + previewSize->set_active_text(sizes[2]); + table->attach(*Gtk::manage(previewSize),1,2,row,row+1,Gtk::FILL|Gtk::EXPAND,Gtk::SHRINK); + + sigc::connection connSize = + previewSize->signal_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::rebuild)); + instanceConns.push_back(connSize); + + ++row; + + /**********************************************************/ + currentDesktop = inkscape_active_desktop(); + currentDocument = sp_desktop_document(currentDesktop); + + previewDocument = symbols_preview_doc(); /* Template to render symbols in */ + previewDocument->ensureUpToDate(); /* Necessary? */ + + key = SPItem::display_key_new(1); + renderDrawing.setRoot(previewDocument->getRoot()->invoke_show(renderDrawing, key, SP_ITEM_SHOW_DISPLAY )); + + get_symbols(); + draw_symbols( currentDocument ); /* Defaults to current document */ + + desktopChangeConn = + deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &SymbolsDialog::setTargetDesktop) ); + instanceConns.push_back( desktopChangeConn ); + deskTrack.connect(GTK_WIDGET(gobj())); +} + +SymbolsDialog::~SymbolsDialog() +{ + for (std::vector<sigc::connection>::iterator it = instanceConns.begin(); it != instanceConns.end(); ++it) { + it->disconnect(); + } + instanceConns.clear(); + deskTrack.disconnect(); +} + +SymbolsDialog& SymbolsDialog::getInstance() +{ + return *new SymbolsDialog(); +} + +void SymbolsDialog::rebuild() { + + store->clear(); + Glib::ustring symbolSetString = symbolSet->get_active_text(); + + SPDocument* symbolDocument = symbolSets[symbolSetString]; + if( !symbolDocument ) { + // Symbol must be from Current Document (this method of + // checking should be language independent). + symbolDocument = currentDocument; + } + draw_symbols( symbolDocument ); +} + +void SymbolsDialog::iconChanged() { +#if WITH_GTKMM_3_0 + std::vector<Gtk::TreePath> iconArray = iconView->get_selected_items(); +#else + Gtk::IconView::ArrayHandle_TreePaths iconArray = iconView->get_selected_items(); +#endif + + if( iconArray.empty() ) { + //std::cout << " iconArray empty: huh? " << std::endl; + } else { + Gtk::TreeModel::Path const & path = *iconArray.begin(); + Gtk::ListStore::iterator row = store->get_iter(path); + Glib::ustring symbol_id = (*row)[getColumns()->symbol_id]; + + /* OK, we know symbol name... now we need to copy it to clipboard, bon chance! */ + Glib::ustring symbolSetString = symbolSet->get_active_text(); + + SPDocument* symbolDocument = symbolSets[symbolSetString]; + if( !symbolDocument ) { + // Symbol must be from Current Document (this method of + // checking should be language independent). + symbolDocument = currentDocument; + } + + SPObject* symbol = symbolDocument->getObjectById(symbol_id); + if( symbol ) { + + // Find style for use in <use> + // First look for default style stored in <symbol> + gchar const* style = symbol->getAttribute("inkscape:symbol-style"); + if( !style ) { + // If no default style in <symbol>, look in documents. + if( symbolDocument == currentDocument ) { + style = style_from_use( symbol_id.c_str(), currentDocument ); + } else { + style = symbolDocument->getReprRoot()->attribute("style"); + } + } + + ClipboardManager *cm = ClipboardManager::get(); + cm->copySymbol(symbol->getRepr(), style); + } + } +} + +/* Hunts preference directories for symbol files */ +void SymbolsDialog::get_symbols() { + + std::list<Glib::ustring> directories; + + if( Inkscape::IO::file_test( INKSCAPE_SYMBOLSDIR, G_FILE_TEST_EXISTS ) && + Inkscape::IO::file_test( INKSCAPE_SYMBOLSDIR, G_FILE_TEST_IS_DIR ) ) { + directories.push_back( INKSCAPE_SYMBOLSDIR ); + } + if( Inkscape::IO::file_test( profile_path("symbols"), G_FILE_TEST_EXISTS ) && + Inkscape::IO::file_test( profile_path("symbols"), G_FILE_TEST_IS_DIR ) ) { + directories.push_back( profile_path("symbols") ); + } + + std::list<Glib::ustring>::iterator it; + for( it = directories.begin(); it != directories.end(); ++it ) { + + GError *err = 0; + GDir *dir = g_dir_open( (*it).c_str(), 0, &err ); + if( dir ) { + + gchar *filename = 0; + while( (filename = (gchar *)g_dir_read_name( dir ) ) != NULL) { + + gchar *fullname = g_build_filename((*it).c_str(), filename, NULL); + + if ( !Inkscape::IO::file_test( fullname, G_FILE_TEST_IS_DIR ) ) { + + SPDocument* symbol_doc = SPDocument::createNewDoc( fullname, FALSE ); + if( symbol_doc ) { + symbolSets[Glib::ustring(filename)]= symbol_doc; +#if WITH_GTKMM_2_24 + symbolSet->append(filename); +#else + symbolSet->append_text(filename); +#endif + } + } + g_free( fullname ); + } + g_dir_close( dir ); + } + } +} + +GSList* SymbolsDialog::symbols_in_doc_recursive (SPObject *r, GSList *l) +{ + + // Stop multiple counting of same symbol + if( SP_IS_USE(r) ) { + return l; + } + + if( SP_IS_SYMBOL(r) ) { + l = g_slist_prepend (l, r); + } + + for (SPObject *child = r->firstChild(); child; child = child->getNext()) { + l = symbols_in_doc_recursive( child, l ); + } + + return l; +} + +GSList* SymbolsDialog::symbols_in_doc( SPDocument* symbolDocument ) { + + GSList *l = NULL; + l = symbols_in_doc_recursive (symbolDocument->getRoot(), l ); + return l; +} + +GSList* SymbolsDialog::use_in_doc_recursive (SPObject *r, GSList *l) +{ + + if( SP_IS_USE(r) ) { + l = g_slist_prepend (l, r); + } + + for (SPObject *child = r->firstChild(); child; child = child->getNext()) { + l = use_in_doc_recursive( child, l ); + } + + return l; +} + +GSList* SymbolsDialog::use_in_doc( SPDocument* useDocument ) { + + GSList *l = NULL; + l = use_in_doc_recursive (useDocument->getRoot(), l ); + return l; +} + +// Returns style from first <use> element found that references id. +// This is a last ditch effort to find a style. +gchar const* SymbolsDialog::style_from_use( gchar const* id, SPDocument* document) { + + gchar const* style = 0; + GSList* l = use_in_doc( document ); + for( ; l != NULL; l = l->next ) { + SPObject* use = SP_OBJECT(l->data); + if( SP_IS_USE( use ) ) { + gchar const *href = use->getRepr()->attribute("xlink:href"); + if( href ) { + Glib::ustring href2(href); + Glib::ustring id2(id); + id2 = "#" + id2; + if( !href2.compare(id2) ) { + style = use->getRepr()->attribute("style"); + break; + } + } + } + } + return style; +} + +void SymbolsDialog::draw_symbols( SPDocument* symbolDocument ) { + + + SymbolColumns* columns = getColumns(); + + GSList* l = symbols_in_doc( symbolDocument ); + for( ; l != NULL; l = l->next ) { + + SPObject* symbol = SP_OBJECT(l->data); + if (!SP_IS_SYMBOL(symbol)) { + //std::cout << " Error: not symbol" << std::endl; + continue; + } + + gchar const *id = symbol->getRepr()->attribute("id"); + gchar const *title = symbol->title(); // From title element + if( !title ) { + title = id; + } + + Glib::RefPtr<Gdk::Pixbuf> pixbuf = create_symbol_image(id, symbolDocument, &renderDrawing, key ); + if( pixbuf ) { + + Gtk::ListStore::iterator row = store->append(); + (*row)[columns->symbol_id] = Glib::ustring( id ); + (*row)[columns->symbol_title] = Glib::ustring( title ); + (*row)[columns->symbol_image] = pixbuf; + } + } +} + +/* + * Returns image of symbol. + * + * Symbols normally are not visible. They must be referenced by a + * <use> element. A temporary document is created with a dummy + * <symbol> element and a <use> element that references the symbol + * element. Each real symbol is swapped in for the dummy symbol and + * the temporary document is rendered. + */ +Glib::RefPtr<Gdk::Pixbuf> +SymbolsDialog::create_symbol_image(gchar const *symbol_id, + SPDocument *source, + Inkscape::Drawing* drawing, + unsigned /*visionkey*/) +{ + + // Retrieve the symbol named 'symbol_id' from the source SVG document + SPObject const* symbol = source->getObjectById(symbol_id); + if (symbol == NULL) { + //std::cout << " Failed to find symbol: " << symbol_id << std::endl; + //return 0; + } + + // Create a copy repr of the symbol with id="the_symbol" + Inkscape::XML::Document *xml_doc = previewDocument->getReprDoc(); + Inkscape::XML::Node *repr = symbol->getRepr()->duplicate(xml_doc); + repr->setAttribute("id", "the_symbol"); + + // Replace old "the_symbol" in previewDocument by new. + Inkscape::XML::Node *root = previewDocument->getReprRoot(); + SPObject *symbol_old = previewDocument->getObjectById("the_symbol"); + if (symbol_old) { + symbol_old->deleteObject(false); + } + + // First look for default style stored in <symbol> + gchar const* style = repr->attribute("inkscape:symbol-style"); + if( !style ) { + // If no default style in <symbol>, look in documents. + if( source == currentDocument ) { + style = style_from_use( symbol_id, source ); + } else { + style = source->getReprRoot()->attribute("style"); + } + } + + // This is for display in Symbols dialog only + if( style ) { + repr->setAttribute( "style", style ); + } + + // BUG: Symbols don't work if defined outside of <defs>. Causes Inkscape + // crash when trying to read in such a file. + root->appendChild(repr); + //defsrepr->appendChild(repr); + Inkscape::GC::release(repr); + + // Uncomment this to get the previewDocument documents saved (useful for debugging) + // FILE *fp = fopen (g_strconcat(symbol_id, ".svg", NULL), "w"); + // sp_repr_save_stream(previewDocument->getReprDoc(), fp); + // fclose (fp); + + // Make sure previewDocument is up-to-date. + previewDocument->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + previewDocument->ensureUpToDate(); + + // Make sure we have symbol in previewDocument + SPObject *object_temp = previewDocument->getObjectById( "the_use" ); + previewDocument->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + previewDocument->ensureUpToDate(); + + // if( object_temp == NULL || !SP_IS_ITEM(object_temp) ) { + // //std::cout << " previewDocument broken?" << std::endl; + // //return 0; + // } + + SPItem *item = SP_ITEM(object_temp); + + // Find object's bbox in document. + // Note symbols can have own viewport... ignore for now. + //Geom::OptRect dbox = item->geometricBounds(); + Geom::OptRect dbox = item->documentVisualBounds(); + // if (!dbox) { + // //std::cout << " No dbox" << std::endl; + // //return NULL; + // } + + Glib::ustring previewSizeString = previewSize->get_active_text(); + unsigned psize = atol( previewSizeString.c_str() ); + + Glib::ustring previewScaleString = previewScale->get_active_text(); + int previewScaleRow = previewScale->get_active_row_number(); + + /* Update to renderable state */ + Glib::ustring key = svg_preview_cache.cache_key(previewDocument->getURI(), symbol_id, psize); + //std::cout << " Key: " << key << std::endl; + // FIX ME + //Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(svg_preview_cache.get_preview_from_cache(key)); + Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::RefPtr<Gdk::Pixbuf>(0); + + if (!pixbuf) { + + /* Scale symbols to fit */ + double scale = 1.0; + switch (previewScaleRow) { + case 0: + /* Fit */ + scale = psize/std::max(dbox->width(),dbox->height()); + break; + case 1: + /* Fit width */ + scale = psize/dbox->width(); + break; + case 2: + /* Fit height */ + scale = psize/dbox->height(); + break; + default: + scale = atof( previewScaleString.c_str() ); + } + + pixbuf = Glib::wrap(render_pixbuf(*drawing, scale, *dbox, psize)); + svg_preview_cache.set_preview_in_cache(key, pixbuf->gobj()); + } + + return pixbuf; +} + +/* + * Return empty doc to render symbols in. + * Symbols are by default not rendered so a <use> element is + * provided. + */ +SPDocument* SymbolsDialog::symbols_preview_doc() +{ + // BUG: <symbol> must be inside <defs> + gchar const *buffer = +"<svg xmlns=\"http://www.w3.org/2000/svg\"" +" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"" +" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"" +" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" +" style=\"fill:none;stroke:black;stroke-width:2\">" +" <defs id=\"defs\">" +" <symbol id=\"the_symbol\"/>" +" </defs>" +" <use id=\"the_use\" xlink:href=\"#the_symbol\"/>" +"</svg>"; + + return SPDocument::createNewDocFromMem( buffer, strlen(buffer), FALSE ); +} + +void SymbolsDialog::setTargetDesktop(SPDesktop *desktop) +{ + if (this->currentDesktop != desktop) { + this->currentDesktop = desktop; + if( !symbolSets[symbolSet->get_active_text()] ) { + // Symbol set is from Current document, update + rebuild(); + } + } +} + + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + + diff --git a/src/ui/dialog/symbols.h b/src/ui/dialog/symbols.h new file mode 100644 index 000000000..c2bb4448e --- /dev/null +++ b/src/ui/dialog/symbols.h @@ -0,0 +1,114 @@ +/** @file + * @brief Symbols dialog + */ +/* Authors: + * Tavmjong Bah + * + * Copyright (C) 2012 Tavmjong Bah + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_UI_DIALOG_SYMBOLS_H +#define INKSCAPE_UI_DIALOG_SYMBOLS_H + +#include "ui/widget/panel.h" +#include "ui/widget/button.h" + +#include "ui/dialog/desktop-tracker.h" + +#include "display/drawing.h" + +#include <glib.h> +#include <gtkmm/treemodel.h> + +#include <vector> + +class SPObject; + +namespace Inkscape { +namespace UI { +namespace Dialog { + +class SymbolColumns; // For Gtk::ListStore + +/** + * A dialog that displays selectable symbols. + */ +class SymbolsDialog : public UI::Widget::Panel { + +public: + SymbolsDialog( gchar const* prefsPath = "/dialogs/symbols" ); + virtual ~SymbolsDialog(); + + static SymbolsDialog& getInstance(); + +protected: + + +private: + SymbolsDialog(SymbolsDialog const &); // no copy + SymbolsDialog &operator=(SymbolsDialog const &); // no assign + + static SymbolColumns *getColumns(); + + void rebuild(); + void iconChanged(); + + void get_symbols(); + void draw_symbols( SPDocument* symbol_document ); + SPDocument* symbols_preview_doc(); + + GSList* symbols_in_doc_recursive(SPObject *r, GSList *l); + GSList* symbols_in_doc( SPDocument* document ); + GSList* use_in_doc_recursive(SPObject *r, GSList *l); + GSList* use_in_doc( SPDocument* document ); + gchar const* style_from_use( gchar const* id, SPDocument* document); + + Glib::RefPtr<Gdk::Pixbuf> + create_symbol_image(gchar const *symbol_name, + SPDocument *source, Inkscape::Drawing* drawing, + unsigned /*visionkey*/); + + /* Keep track of all symbol template documents */ + std::map<Glib::ustring, SPDocument*> symbolSets; + + + Glib::RefPtr<Gtk::ListStore> store; + Gtk::ComboBoxText* symbolSet; + Gtk::IconView* iconView; + Gtk::ComboBoxText* previewScale; + Gtk::ComboBoxText* previewSize; + + void setTargetDesktop(SPDesktop *desktop); + SPDesktop* currentDesktop; + DesktopTracker deskTrack; + SPDocument* currentDocument; + SPDocument* previewDocument; /* Document to render single symbol */ + + /* For rendering the template drawing */ + unsigned key; + Inkscape::Drawing renderDrawing; + + std::vector<sigc::connection> instanceConns; + sigc::connection desktopChangeConn; + +}; + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + +#endif // INKSCAPE_UI_DIALOG_SYMBOLS_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/style-swatch.cpp b/src/ui/widget/style-swatch.cpp index 857ae7019..60d5f6ecc 100644 --- a/src/ui/widget/style-swatch.cpp +++ b/src/ui/widget/style-swatch.cpp @@ -340,15 +340,12 @@ void StyleSwatch::setStyle(SPStyle *query) if (op != 1) { { gchar *str; - if (op == 0) - str = g_strdup_printf(_("O:%.3g"), op); - else - str = g_strdup_printf(_("O:.%d"), (int) (op*10)); + str = g_strdup_printf(_("O: %2.0f"), (op*100.0)); _opacity_value.set_markup (str); g_free (str); } { - gchar *str = g_strdup_printf(_("Opacity: %.3g"), op); + gchar *str = g_strdup_printf(_("Opacity: %2.1f %%"), (op*100.0)); _opacity_place.set_tooltip_text(str); g_free (str); } diff --git a/src/verbs.cpp b/src/verbs.cpp index edfc45f8e..8c45ce665 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -78,6 +78,7 @@ #include "ui/dialog/layers.h" #include "ui/dialog/object-properties.h" #include "ui/dialog/swatches.h" +#include "ui/dialog/symbols.h" #include "ui/dialog/spellcheck.h" #include "ui/icon-names.h" #include "ui/tool/node-tool.h" @@ -957,6 +958,12 @@ void EditVerb::perform(SPAction *action, void *data) case SP_VERB_EDIT_UNTILE: sp_selection_untile(dt); break; + case SP_VERB_EDIT_SYMBOL: + sp_selection_symbol(dt); + break; + case SP_VERB_EDIT_UNSYMBOL: + sp_selection_unsymbol(dt); + break; case SP_VERB_EDIT_CLEAR_ALL: sp_edit_clear_all(dt); break; @@ -1342,17 +1349,34 @@ void LayerVerb::perform(SPAction *action, void *data) break; } case SP_VERB_LAYER_SHOW_ALL: { - dt->toggleAllLayers( false ); + dt->toggleHideAllLayers( false ); DocumentUndo::maybeDone(sp_desktop_document(dt), "layer:showall", SP_VERB_LAYER_SHOW_ALL, _("Show all layers")); break; } - case SP_VERB_LAYER_HIDE_ALL: { - dt->toggleAllLayers( true ); + dt->toggleHideAllLayers( true ); DocumentUndo::maybeDone(sp_desktop_document(dt), "layer:hideall", SP_VERB_LAYER_HIDE_ALL, _("Hide all layers")); break; } - + case SP_VERB_LAYER_LOCK_ALL: { + dt->toggleLockAllLayers( true ); + DocumentUndo::maybeDone(sp_desktop_document(dt), "layer:lockall", SP_VERB_LAYER_LOCK_ALL, _("Lock all layers")); + break; + } + case SP_VERB_LAYER_LOCK_OTHERS: { + if ( dt->currentLayer() == dt->currentRoot() ) { + dt->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No current layer.")); + } else { + dt->toggleLockOtherLayers( dt->currentLayer() ); + DocumentUndo::maybeDone(sp_desktop_document(dt), "layer:lockothers", SP_VERB_LAYER_LOCK_OTHERS, _("Lock other layers")); + } + break; + } + case SP_VERB_LAYER_UNLOCK_ALL: { + dt->toggleLockAllLayers( false ); + DocumentUndo::maybeDone(sp_desktop_document(dt), "layer:unlockall", SP_VERB_LAYER_UNLOCK_ALL, _("Unlock all layers")); + break; + } case SP_VERB_LAYER_TOGGLE_LOCK: case SP_VERB_LAYER_TOGGLE_HIDE: { if ( dt->currentLayer() == dt->currentRoot() ) { @@ -1775,6 +1799,24 @@ void ZoomVerb::perform(SPAction *action, void *data) case SP_VERB_TOGGLE_SCROLLBARS: dt->toggleScrollbars(); break; + case SP_VERB_TOGGLE_COMMANDS_TOOLBAR: + dt->toggleToolbar("commands"); + break; + case SP_VERB_TOGGLE_SNAP_TOOLBAR: + dt->toggleToolbar("snaptoolbox"); + break; + case SP_VERB_TOGGLE_TOOL_TOOLBAR: + dt->toggleToolbar("toppanel"); + break; + case SP_VERB_TOGGLE_TOOLBOX: + dt->toggleToolbar("toolbox"); + break; + case SP_VERB_TOGGLE_PALETTE: + dt->toggleToolbar("panels"); + break; + case SP_VERB_TOGGLE_STATUSBAR: + dt->toggleToolbar("statusbar"); + break; case SP_VERB_TOGGLE_GUIDES: sp_namedview_toggle_guides(doc, repr); break; @@ -1833,6 +1875,7 @@ void ZoomVerb::perform(SPAction *action, void *data) inkscape_dialogs_unhide(); dt->_dlg_mgr->showDialog("IconPreviewPanel"); break; + default: break; } @@ -1876,6 +1919,9 @@ void DialogVerb::perform(SPAction *action, void *data) case SP_VERB_DIALOG_SWATCHES: dt->_dlg_mgr->showDialog("Swatches"); break; + case SP_VERB_DIALOG_SYMBOLS: + dt->_dlg_mgr->showDialog("Symbols"); + break; case SP_VERB_DIALOG_TRANSFORM: dt->_dlg_mgr->showDialog("Transformation"); break; @@ -2339,6 +2385,10 @@ Verb *Verb::_base_verbs[] = { N_("Convert selection to a rectangle with tiled pattern fill"), NULL), new EditVerb(SP_VERB_EDIT_UNTILE, "ObjectsFromPattern", N_("Pattern to _Objects"), N_("Extract objects from a tiled pattern fill"), NULL), + new EditVerb(SP_VERB_EDIT_SYMBOL, "ObjectsToSymbol", N_("Group to Symbol"), + N_("Convert group to a symbol"), NULL), + new EditVerb(SP_VERB_EDIT_UNSYMBOL, "ObjectsFromSymbol", N_("Symbol to Group"), + N_("Extract group from a symbol"), NULL), new EditVerb(SP_VERB_EDIT_CLEAR_ALL, "EditClearAll", N_("Clea_r All"), N_("Delete all objects from document"), NULL), new EditVerb(SP_VERB_EDIT_SELECT_ALL, "EditSelectAll", N_("Select Al_l"), @@ -2489,6 +2539,12 @@ Verb *Verb::_base_verbs[] = { N_("Show all the layers"), NULL), new LayerVerb(SP_VERB_LAYER_HIDE_ALL, "LayerHideAll", N_("_Hide all layers"), N_("Hide all the layers"), NULL), + new LayerVerb(SP_VERB_LAYER_LOCK_ALL, "LayerLockAll", N_("_Lock all layers"), + N_("Lock all the layers"), NULL), + new LayerVerb(SP_VERB_LAYER_LOCK_OTHERS, "LayerLockOthers", N_("Lock/Unlock _other layers"), + N_("Lock all the other layers"), NULL), + new LayerVerb(SP_VERB_LAYER_UNLOCK_ALL, "LayerUnlockAll", N_("_Unlock all layers"), + N_("Unlock all the layers"), NULL), new LayerVerb(SP_VERB_LAYER_TOGGLE_LOCK, "LayerToggleLock", N_("_Lock/Unlock Current Layer"), N_("Toggle lock on current layer"), NULL), new LayerVerb(SP_VERB_LAYER_TOGGLE_HIDE, "LayerToggleHide", N_("_Show/hide Current Layer"), @@ -2632,6 +2688,12 @@ Verb *Verb::_base_verbs[] = { new ZoomVerb(SP_VERB_TOGGLE_GRID, "ToggleGrid", N_("_Grid"), N_("Show or hide the grid"), INKSCAPE_ICON("show-grid")), new ZoomVerb(SP_VERB_TOGGLE_GUIDES, "ToggleGuides", N_("G_uides"), N_("Show or hide guides (drag from a ruler to create a guide)"), INKSCAPE_ICON("show-guides")), new ZoomVerb(SP_VERB_TOGGLE_SNAPPING, "ToggleSnapGlobal", N_("Snap"), N_("Enable snapping"), INKSCAPE_ICON("snap")), + new ZoomVerb(SP_VERB_TOGGLE_COMMANDS_TOOLBAR, "ToggleCommandsToolbar", N_("_Commands Bar"), N_("Show or hide the Commands bar (under the menu)"), NULL), + new ZoomVerb(SP_VERB_TOGGLE_SNAP_TOOLBAR, "ToggleSnapToolbar", N_("Sn_ap Controls Bar"), N_("Show or hide the snapping controls"), NULL), + new ZoomVerb(SP_VERB_TOGGLE_TOOL_TOOLBAR, "ToggleToolToolbar", N_("T_ool Controls Bar"), N_("Show or hide the Tool Controls bar"), NULL), + new ZoomVerb(SP_VERB_TOGGLE_TOOLBOX, "ToggleToolbox", N_("_Toolbox"), N_("Show or hide the main toolbox (on the left)"), NULL), + new ZoomVerb(SP_VERB_TOGGLE_PALETTE, "TogglePalette", N_("_Palette"), N_("Show or hide the color palette"), NULL), + new ZoomVerb(SP_VERB_TOGGLE_STATUSBAR, "ToggleStatusbar", N_("_Statusbar"), N_("Show or hide the statusbar (at the bottom of the window)"), NULL), new ZoomVerb(SP_VERB_ZOOM_NEXT, "ZoomNext", N_("Nex_t Zoom"), N_("Next zoom (from the history of zooms)"), INKSCAPE_ICON("zoom-next")), new ZoomVerb(SP_VERB_ZOOM_PREV, "ZoomPrev", N_("Pre_vious Zoom"), N_("Previous zoom (from the history of zooms)"), @@ -2700,6 +2762,8 @@ Verb *Verb::_base_verbs[] = { // TRANSLATORS: "Swatches" means: color samples new DialogVerb(SP_VERB_DIALOG_SWATCHES, "DialogSwatches", N_("S_watches..."), N_("Select colors from a swatches palette"), GTK_STOCK_SELECT_COLOR), + new DialogVerb(SP_VERB_DIALOG_SYMBOLS, "DialogSymbols", N_("S_ymbols..."), + N_("Select symbol from a symbols palette"), GTK_STOCK_SELECT_COLOR), new DialogVerb(SP_VERB_DIALOG_TRANSFORM, "DialogTransform", N_("Transfor_m..."), N_("Precisely control objects' transformations"), INKSCAPE_ICON("dialog-transform")), new DialogVerb(SP_VERB_DIALOG_ALIGN_DISTRIBUTE, "DialogAlignDistribute", N_("_Align and Distribute..."), diff --git a/src/verbs.h b/src/verbs.h index aa801803c..c47d3ae16 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -84,6 +84,8 @@ enum { SP_VERB_EDIT_SELECTION_2_GUIDES, SP_VERB_EDIT_TILE, SP_VERB_EDIT_UNTILE, + SP_VERB_EDIT_SYMBOL, + SP_VERB_EDIT_UNSYMBOL, SP_VERB_EDIT_CLEAR_ALL, SP_VERB_EDIT_SELECT_ALL, SP_VERB_EDIT_SELECT_ALL_IN_ALL_LAYERS, @@ -149,6 +151,9 @@ enum { SP_VERB_LAYER_SOLO, SP_VERB_LAYER_SHOW_ALL, SP_VERB_LAYER_HIDE_ALL, + SP_VERB_LAYER_LOCK_ALL, + SP_VERB_LAYER_LOCK_OTHERS, + SP_VERB_LAYER_UNLOCK_ALL, SP_VERB_LAYER_TOGGLE_LOCK, SP_VERB_LAYER_TOGGLE_HIDE, /* Object */ @@ -222,6 +227,12 @@ enum { SP_VERB_TOGGLE_GRID, SP_VERB_TOGGLE_GUIDES, SP_VERB_TOGGLE_SNAPPING, + SP_VERB_TOGGLE_COMMANDS_TOOLBAR, + SP_VERB_TOGGLE_SNAP_TOOLBAR, + SP_VERB_TOGGLE_TOOL_TOOLBAR, + SP_VERB_TOGGLE_TOOLBOX, + SP_VERB_TOGGLE_PALETTE, + SP_VERB_TOGGLE_STATUSBAR, SP_VERB_ZOOM_NEXT, SP_VERB_ZOOM_PREV, SP_VERB_ZOOM_1_1, @@ -255,6 +266,7 @@ enum { SP_VERB_DIALOG_FILL_STROKE, SP_VERB_DIALOG_GLYPHS, SP_VERB_DIALOG_SWATCHES, + SP_VERB_DIALOG_SYMBOLS, SP_VERB_DIALOG_TRANSFORM, SP_VERB_DIALOG_ALIGN_DISTRIBUTE, SP_VERB_DIALOG_SPRAY_OPTION, @@ -328,6 +340,7 @@ enum { SP_VERB_ALIGN_VERTICAL_BOTTOM, SP_VERB_ALIGN_VERTICAL_TOP_TO_ANCHOR, SP_VERB_ALIGN_VERTICAL_HORIZONTAL_CENTER, + /* Footer */ SP_VERB_LAST }; diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 49eb82de7..6e03c2606 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -1446,7 +1446,7 @@ void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop) swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id ); swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true ); GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() ); - gtk_table_attach( GTK_TABLE(holder), swatch_, 1, 2, 0, 1, (GtkAttachOptions)(GTK_SHRINK | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), AUX_BETWEEN_BUTTON_GROUPS, 0 ); + gtk_table_attach( GTK_TABLE(holder), swatch_, 1, 2, 0, 1, (GtkAttachOptions)(GTK_SHRINK | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), AUX_BETWEEN_BUTTON_GROUPS, AUX_SPACING ); } gtk_widget_show_all( holder ); diff --git a/src/widgets/widget-sizes.h b/src/widgets/widget-sizes.h index c3f9e1119..fe89d4574 100644 --- a/src/widgets/widget-sizes.h +++ b/src/widgets/widget-sizes.h @@ -31,7 +31,7 @@ #define SELECTED_STYLE_SB_WIDTH 48 #define SELECTED_STYLE_WIDTH 190 -#define STYLE_SWATCH_WIDTH 100 +#define STYLE_SWATCH_WIDTH 135 #define STATUS_LAYER_FONT_SIZE 7700 diff --git a/src/xml/repr-css.cpp b/src/xml/repr-css.cpp index 7fba4d7c6..f554d314d 100644 --- a/src/xml/repr-css.cpp +++ b/src/xml/repr-css.cpp @@ -351,7 +351,8 @@ static void sp_repr_css_merge_from_decl(SPCSSAttr *css, CRDeclaration const *con guchar *const str_value_unsigned = cr_term_to_string(decl->value); gchar *const str_value = reinterpret_cast<gchar *>(str_value_unsigned); gchar *value_unquoted = attribute_unquote (str_value); // libcroco returns strings quoted in "" - gchar *units = NULL; + Glib::ustring value_unquoted2 = value_unquoted ? value_unquoted : Glib::ustring(); + Glib::ustring units; /* * Problem with parsing of units em and ex, like font-size "1.2em" and "3.4ex" @@ -360,18 +361,22 @@ static void sp_repr_css_merge_from_decl(SPCSSAttr *css, CRDeclaration const *con * * HACK for now is to strip off em and ex units and add them back at the end */ - int l = strlen(value_unquoted); - if (!strncmp(&value_unquoted[l-2], "em", 2) || - !strncmp(&value_unquoted[l-2], "ex", 2)) { - units = g_strndup(&value_unquoted[l-2], 2); - value_unquoted = g_strndup(value_unquoted, l-2); + int le = value_unquoted2.length(); + if (le > 2) { + units = value_unquoted2.substr(le-2, 2); + if ((units == "em") || (units == "ex")) { + value_unquoted2 = value_unquoted2.substr(0, le-2); + } + else { + units.clear(); + } } // libcroco uses %.17f for formatting... leading to trailing zeros or small rounding errors. // CSSOStringStream is used here to write valid CSS (as in sp_style_write_string). This has // the additional benefit of respecting the numerical precission set in the SVG Output // preferences. We assume any numerical part comes first (if not, the whole string is copied). - std::stringstream ss( value_unquoted ); + std::stringstream ss( value_unquoted2 ); double number = 0; std::string characters; std::string temp; @@ -385,10 +390,9 @@ static void sp_repr_css_merge_from_decl(SPCSSAttr *css, CRDeclaration const *con Inkscape::CSSOStringStream os; if( number_valid ) os << number; os << characters; - if (units) { + if (!units.empty()) { os << units; //g_message("sp_repr_css_merge_from_decl looks like em or ex units %s --> %s", str_value, os.str().c_str()); - g_free(units); } ((Node *) css)->setAttribute(decl->property->stryng->str, os.str().c_str(), false); g_free(value_unquoted); |
