diff options
Diffstat (limited to 'src/widgets/stroke-marker-selector.cpp')
| -rw-r--r-- | src/widgets/stroke-marker-selector.cpp | 340 |
1 files changed, 248 insertions, 92 deletions
diff --git a/src/widgets/stroke-marker-selector.cpp b/src/widgets/stroke-marker-selector.cpp index 9b2c3bb42..00b6b5c91 100644 --- a/src/widgets/stroke-marker-selector.cpp +++ b/src/widgets/stroke-marker-selector.cpp @@ -36,35 +36,39 @@ #include "sp-root.h" #include "ui/cache/svg_preview_cache.h" #include "helper/stock-items.h" +#include "gradient-vector.h" +#include <gtkmm/icontheme.h> #include <gtkmm/adjustment.h> #include "ui/widget/spinbutton.h" +#include "stroke-style.h" +#include "gradient-chemistry.h" static Inkscape::UI::Cache::SvgPreview svg_preview_cache; -MarkerComboBox::MarkerComboBox(gchar const *id) : +MarkerComboBox::MarkerComboBox(gchar const *id, int l) : Gtk::ComboBox(), combo_id(id), + loc(l), updating(false), - is_history(false) + markerCount(0) { marker_store = Gtk::ListStore::create(marker_columns); set_model(marker_store); pack_start(image_renderer, false); - pack_end(label_renderer, true); - label_renderer.set_padding(5, 0); - image_renderer.set_padding(5, 0); - set_cell_data_func(label_renderer, sigc::mem_fun(*this, &MarkerComboBox::prepareLabelRenderer)); set_cell_data_func(image_renderer, sigc::mem_fun(*this, &MarkerComboBox::prepareImageRenderer)); gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(gobj()), MarkerComboBox::separator_cb, NULL, NULL); - empty_image = new Gtk::Image(); + empty_image = new Gtk::Image( Glib::wrap( + sp_pixbuf_new( Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_ICON("no-marker") ) ) ); sandbox = ink_markers_preview_doc (); desktop = inkscape_active_desktop(); doc = sp_desktop_document(desktop); + modified_connection = doc->getDefs()->connectModified( sigc::hide(sigc::hide(sigc::bind(sigc::ptr_fun(&MarkerComboBox::handleDefsModified), this))) ); + init_combo(); show(); @@ -73,6 +77,62 @@ MarkerComboBox::MarkerComboBox(gchar const *id) : MarkerComboBox::~MarkerComboBox() { delete combo_id; delete sandbox; + + if (doc) { + modified_connection.disconnect(); + } +} + +void MarkerComboBox::setDesktop(SPDesktop *desktop) +{ + if (this->desktop != desktop) { + + if (doc) { + modified_connection.disconnect(); + } + + this->desktop = desktop; + doc = sp_desktop_document(desktop); + + if (doc) { + modified_connection = doc->getDefs()->connectModified( sigc::hide(sigc::hide(sigc::bind(sigc::ptr_fun(&MarkerComboBox::handleDefsModified), this))) ); + } + + refreshHistory(); + } +} + +void +MarkerComboBox::handleDefsModified(MarkerComboBox *self) +{ + self->refreshHistory(); +} + +void +MarkerComboBox::refreshHistory() +{ + if (updating) + return; + + updating = true; + + GSList *ml = get_marker_list(doc); + + /* + * Seems to be no way to get notified of changes just to markers, + * so listen to changes in all defs and check if the number of markers has changed here + * to avoid unnecessary refreshes when things like gradients change + */ + if (markerCount != g_slist_length(ml)) { + const char *active = get_active()->get_value(marker_columns.marker); + sp_marker_list_from_doc(doc, true); + set_selected(active); + markerCount = g_slist_length(ml); + } + + g_slist_free (ml); + + updating = false; } /** @@ -81,12 +141,22 @@ MarkerComboBox::~MarkerComboBox() { void MarkerComboBox::init_combo() { - updating = false; + if (updating) + return; + + const gchar *active = NULL; + if (get_active()) { + active = get_active()->get_value(marker_columns.marker); + } if (!doc) { Gtk::TreeModel::Row row = *(marker_store->append()); row[marker_columns.label] = _("No document selected"); + row[marker_columns.marker] = g_strdup("None"); row[marker_columns.image] = NULL; + row[marker_columns.stock] = false; + row[marker_columns.history] = false; + row[marker_columns.separator] = false; set_sensitive(false); set_current(NULL); return; @@ -94,11 +164,17 @@ MarkerComboBox::init_combo() static SPDocument *markers_doc = NULL; - // add "None" - Gtk::TreeModel::Row row = *(marker_store->append()); - row[marker_columns.label] = _("None"); - row[marker_columns.marker] = g_strdup("none"); - row[marker_columns.image] = NULL; + // add separator + Gtk::TreeModel::Row row_sep = *(marker_store->append()); + row_sep[marker_columns.label] = "Separator"; + row_sep[marker_columns.marker] = g_strdup("None"); + row_sep[marker_columns.image] = NULL; + row_sep[marker_columns.stock] = false; + row_sep[marker_columns.history] = false; + row_sep[marker_columns.separator] = true; + + // load markers from the current doc + sp_marker_list_from_doc(doc, true); // find and load markers.svg if (markers_doc == NULL) { @@ -109,27 +185,17 @@ MarkerComboBox::init_combo() g_free(markers_source); } - // suck in from current doc - is_history = true; - sp_marker_list_from_doc(doc); - is_history = false; - - // add separator - Gtk::TreeModel::Row row_sep = *(marker_store->append()); - row_sep[marker_columns.label] = "Separator"; - row_sep[marker_columns.isseparator] = true; - row_sep[marker_columns.image] = NULL; - - // suck in from markers.svg + // load markers from markers.svg if (markers_doc) { doc->ensureUpToDate(); - sp_marker_list_from_doc(markers_doc); + sp_marker_list_from_doc(markers_doc, false); } set_sensitive(true); /* Set history */ - set_current(NULL); + set_selected(active); + } /** @@ -140,20 +206,8 @@ void MarkerComboBox::set_current(SPObject *marker) updating = true; if (marker != NULL) { - bool mark_is_stock = false; - if (marker->getRepr()->attribute("inkscape:stockid")) { - mark_is_stock = true; - } - - gchar *markname = 0; - if (mark_is_stock) { - markname = g_strdup(marker->getRepr()->attribute("inkscape:stockid")); - } else { - markname = g_strdup(marker->getRepr()->attribute("id")); - } - + gchar *markname = g_strdup(marker->getRepr()->attribute("id")); set_selected(markname); - g_free (markname); } else { @@ -177,7 +231,7 @@ const gchar * MarkerComboBox::get_active_marker_uri() gchar const *marker = ""; if (strcmp(markid, "none")) { - bool stockid = get_active()->get_value(marker_columns.isstock); + bool stockid = get_active()->get_value(marker_columns.stock); gchar *markurn; if (stockid) @@ -188,7 +242,7 @@ const gchar * MarkerComboBox::get_active_marker_uri() { markurn = g_strdup(markid); } - SPObject *mark = get_stock_item(markurn); + SPObject *mark = get_stock_item(markurn, stockid); g_free(markurn); if (mark) { Inkscape::XML::Node *repr = mark->getRepr(); @@ -201,13 +255,21 @@ const gchar * MarkerComboBox::get_active_marker_uri() return marker; } - void MarkerComboBox::set_active_history() { - set_selected(get_active()->get_value(marker_columns.marker)); + + const gchar *markid = get_active()->get_value(marker_columns.marker); + + // If forked from a stockid, add the stockid + SPObject const *marker = doc->getObjectById(markid); + if (marker && marker->getRepr()->attribute("inkscape:stockid")) { + markid = marker->getRepr()->attribute("inkscape:stockid"); + } + + set_selected(markid); } -void MarkerComboBox::set_selected(const gchar *name) { +void MarkerComboBox::set_selected(const gchar *name, gboolean retry/*=true*/) { if (!name) { set_active(0); @@ -220,42 +282,23 @@ void MarkerComboBox::set_selected(const gchar *name) { if (row[marker_columns.marker] && !strcmp(row[marker_columns.marker], name)) { set_active(iter); - if (strcmp(name, "none")) - set_history(row); return; } } -} -void MarkerComboBox::set_history(Gtk::TreeModel::Row match_row) { - - if (!match_row) { - return; - } - - for(Gtk::TreeIter iter = marker_store->children().begin(); - iter != marker_store->children().end(); ++iter) { - Gtk::TreeModel::Row row = (*iter); - if (row[marker_columns.history] && - !strcmp(row[marker_columns.marker], match_row[marker_columns.marker])) { - return; - } + // Didn't find it in the list, try refreshing from the doc + if (retry) { + sp_marker_list_from_doc(doc, true); + set_selected(name, false); } - - // Add a new row to the history - Gtk::TreeModel::Row row = *(marker_store->insert_after(marker_store->children().begin())); - row[marker_columns.marker] = g_strdup(match_row.get_value(marker_columns.marker)); - row[marker_columns.label] = match_row.get_value(marker_columns.label); - row[marker_columns.isstock] = match_row.get_value(marker_columns.isstock); - row[marker_columns.image] = match_row.get_value(marker_columns.image); - row[marker_columns.history] = true; } + /** * Pick up all markers from source, except those that are in * current_doc (if non-NULL), and add items to the combo. */ -void MarkerComboBox::sp_marker_list_from_doc(SPDocument *source) +void MarkerComboBox::sp_marker_list_from_doc(SPDocument *source, gboolean history) { GSList *ml = get_marker_list(source); GSList *clean_ml = NULL; @@ -267,10 +310,14 @@ void MarkerComboBox::sp_marker_list_from_doc(SPDocument *source) // Add to the list of markers we really do wish to show clean_ml = g_slist_prepend (clean_ml, ml->data); } - add_markers(clean_ml, source); + + remove_markers(history); // Seem to need to remove 2x + remove_markers(history); + add_markers(clean_ml, source, history); g_slist_free (ml); g_slist_free (clean_ml); + } /** @@ -284,6 +331,10 @@ GSList *MarkerComboBox::get_marker_list (SPDocument *source) GSList *ml = NULL; SPDefs *defs = source->getDefs(); + if (!defs) { + return NULL; + } + for ( SPObject *child = defs->firstChild(); child; child = child->getNext() ) { if (SP_IS_MARKER(child)) { @@ -294,37 +345,115 @@ GSList *MarkerComboBox::get_marker_list (SPDocument *source) } /** - * Adds previews of markers in marker_list to the combo + * Remove history or non-history markers from the combo + */ +void MarkerComboBox::remove_markers (gboolean history) +{ + // Having the model set causes assertions when erasing rows, temporarily disconnect + unset_model(); + for(Gtk::TreeIter iter = marker_store->children().begin(); + iter != marker_store->children().end(); ++iter) { + Gtk::TreeModel::Row row = (*iter); + if (row[marker_columns.history] == history && row[marker_columns.separator] == false) { + marker_store->erase(iter); + iter = marker_store->children().begin(); + } + } + + set_model(marker_store); +} + +/** + * Adds markers in marker_list to the combo */ -void MarkerComboBox::add_markers (GSList *marker_list, SPDocument *source) +void MarkerComboBox::add_markers (GSList *marker_list, SPDocument *source, gboolean history) { // Do this here, outside of loop, to speed up preview generation: Inkscape::Drawing drawing; unsigned const visionkey = SPItem::display_key_new(1); drawing.setRoot(sandbox->getRoot()->invoke_show(drawing, visionkey, SP_ITEM_SHOW_DISPLAY)); + // Find the separator, + Gtk::TreeIter sep_iter; + for(Gtk::TreeIter iter = marker_store->children().begin(); + iter != marker_store->children().end(); ++iter) { + Gtk::TreeModel::Row row = (*iter); + if (row[marker_columns.separator]) { + sep_iter = iter; + } + } + + if (history) { + // add "None" + Gtk::TreeModel::Row row = *(marker_store->prepend()); + row[marker_columns.label] = _("None"); + row[marker_columns.stock] = false; + row[marker_columns.marker] = g_strdup("None"); + row[marker_columns.image] = NULL; + row[marker_columns.history] = true; + row[marker_columns.separator] = false; + } for (; marker_list != NULL; marker_list = marker_list->next) { Inkscape::XML::Node *repr = reinterpret_cast<SPItem *>(marker_list->data)->getRepr(); - bool isstock = (repr->attribute("inkscape:stockid")); - gchar const *markid = repr->attribute("id"); + gchar const *markid = repr->attribute("inkscape:stockid") ? repr->attribute("inkscape:stockid") : repr->attribute("id"); // generate preview - Gtk::Image *prv = create_marker_image (22, markid, source, drawing, visionkey); + Gtk::Image *prv = create_marker_image (22, repr->attribute("id"), source, drawing, visionkey); prv->show(); - Gtk::TreeModel::Row row = *(marker_store->append()); - row[marker_columns.label] = g_strdup(markid); - row[marker_columns.marker] = g_strdup(markid); - row[marker_columns.isstock] = isstock; + // Add history before separator, others after + Gtk::TreeModel::Row row; + if (history) + row = *(marker_store->insert(sep_iter)); + else + row = *(marker_store->append()); + + row[marker_columns.label] = gr_ellipsize_text(markid, 20); + // Non "stock" markers can also have "inkscape:stockid" (when using extension ColorMarkers), + // So use !is_history instead to determine is it is "stock" (ie in the markers.svg file) + row[marker_columns.stock] = !history; + row[marker_columns.marker] = repr->attribute("id"); row[marker_columns.image] = prv; - row[marker_columns.history] = is_history; + row[marker_columns.history] = history; + row[marker_columns.separator] = false; } sandbox->getRoot()->invoke_hide(visionkey); } +/* + * Remove from the cache and recreate a marker image + */ +void +MarkerComboBox::update_marker_image(gchar const *mname) +{ + gchar *cache_name = g_strconcat(combo_id, mname, NULL); + Glib::ustring key = svg_preview_cache.cache_key(doc->getURI(), cache_name, 22); + g_free (cache_name); + svg_preview_cache.remove_preview_from_cache(key); + + Inkscape::Drawing drawing; + unsigned const visionkey = SPItem::display_key_new(1); + drawing.setRoot(sandbox->getRoot()->invoke_show(drawing, visionkey, SP_ITEM_SHOW_DISPLAY)); + Gtk::Image *prv = create_marker_image(22, mname, doc, drawing, visionkey); + if (prv) { + prv->show(); + } + sandbox->getRoot()->invoke_hide(visionkey); + + for(Gtk::TreeIter iter = marker_store->children().begin(); + iter != marker_store->children().end(); ++iter) { + Gtk::TreeModel::Row row = (*iter); + if (row[marker_columns.marker] && row[marker_columns.history] && + !strcmp(row[marker_columns.marker], mname)) { + row[marker_columns.image] = prv; + return; + } + } + +} /** * Creates a copy of the marker named mname, determines its visible and renderable * area in the bounding box, and then renders it. This allows us to fill in @@ -357,6 +486,38 @@ MarkerComboBox::create_marker_image(unsigned psize, gchar const *mname, Inkscape::GC::release(mrepr); + // If the marker color is a url link to a pattern or gradient copy that too + SPObject *mk = source->getObjectById(mname); + SPCSSAttr *css_marker = sp_css_attr_from_object(mk->firstChild(), SP_STYLE_FLAG_ALWAYS); + //const char *mfill = sp_repr_css_property(css_marker, "fill", "none"); + const char *mstroke = sp_repr_css_property(css_marker, "fill", "none"); + + if (!strncmp (mstroke, "url(", 4)) { + SPObject *linkObj = getMarkerObj(mstroke, source); + if (linkObj) { + Inkscape::XML::Node *grepr = linkObj->getRepr()->duplicate(xml_doc); + SPObject *oldmarker = sandbox->getObjectById(linkObj->getId()); + if (oldmarker) { + oldmarker->deleteObject(false); + } + defsrepr->appendChild(grepr); + Inkscape::GC::release(grepr); + + if (SP_IS_GRADIENT(linkObj)) { + SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (SP_GRADIENT(linkObj), false); + if (vector) { + Inkscape::XML::Node *grepr = vector->getRepr()->duplicate(xml_doc); + SPObject *oldmarker = sandbox->getObjectById(vector->getId()); + if (oldmarker) { + oldmarker->deleteObject(false); + } + defsrepr->appendChild(grepr); + Inkscape::GC::release(grepr); + } + } + } + } + // Uncomment this to get the sandbox documents saved (useful for debugging) //FILE *fp = fopen (g_strconcat(combo_id, mname, ".svg", NULL), "w"); //sp_repr_save_stream(sandbox->getReprDoc(), fp); @@ -383,24 +544,19 @@ MarkerComboBox::create_marker_image(unsigned psize, gchar const *mname, gchar *cache_name = g_strconcat(combo_id, mname, NULL); Glib::ustring key = svg_preview_cache.cache_key(source->getURI(), cache_name, psize); g_free (cache_name); - Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(svg_preview_cache.get_preview_from_cache(key)); + GdkPixbuf *pixbuf = svg_preview_cache.get_preview_from_cache(key); // no ref created if (!pixbuf) { - pixbuf = Glib::wrap(render_pixbuf(drawing, 0.8, *dbox, psize)); - svg_preview_cache.set_preview_in_cache(key, pixbuf->gobj()); + pixbuf = render_pixbuf(drawing, 0.8, *dbox, psize); + svg_preview_cache.set_preview_in_cache(key, pixbuf); + g_object_unref(pixbuf); // reference is held by svg_preview_cache } // Create widget - Gtk::Image *pb = new Gtk::Image(pixbuf); - + Gtk::Image *pb = Glib::wrap(GTK_IMAGE(gtk_image_new_from_pixbuf(pixbuf))); return pb; } -void MarkerComboBox::prepareLabelRenderer( Gtk::TreeModel::const_iterator const &row ) { - Glib::ustring name=(*row)[marker_columns.label]; - label_renderer.property_markup() = name.c_str(); -} - void MarkerComboBox::prepareImageRenderer( Gtk::TreeModel::const_iterator const &row ) { Gtk::Image *image = (*row)[marker_columns.image]; @@ -413,7 +569,7 @@ void MarkerComboBox::prepareImageRenderer( Gtk::TreeModel::const_iterator const gboolean MarkerComboBox::separator_cb (GtkTreeModel *model, GtkTreeIter *iter, gpointer /*data*/) { gboolean sep = FALSE; - gtk_tree_model_get(model, iter, 5, &sep, -1); + gtk_tree_model_get(model, iter, 4, &sep, -1); return sep; } |
