index 0ba60a91cac30868e47ed6d025261d3e3b23fd5e..851fc3a3bf9780828aceef86cafe852a00bcd538 100644 (file)
* Frank Felfe <innerspace@iname.com>
* MenTaLguY <mental@rydia.net>
* bulia byak <buliabyak@users.sf.net>
+ * Andrius R. <knutux@gmail.com>
*
- * Copyright (C) 1999-2005 authors
+ * Copyright (C) 1999-2006 authors
* Copyright (C) 2001-2002 Ximian, Inc.
*
* Released under GNU GPL, read the file 'COPYING' for more information
#include "sp-namedview.h"
#include "prefs-utils.h"
#include "sp-offset.h"
+#include "sp-clippath.h"
+#include "sp-mask.h"
#include "file.h"
#include "layer-fns.h"
#include "context-fns.h"
+#include <map>
using NR::X;
using NR::Y;
@@ -129,7 +133,7 @@ void sp_selection_copy_impl (const GSList *items, GSList **clip, GSList **defs_c
/**
Add gradients/patterns/markers referenced by copied objects to defs
*/
-void
+void
paste_defs (GSList **defs_clip, SPDocument *doc)
{
if (!defs_clip)
@@ -299,7 +303,7 @@ get_all_items (GSList *list, SPObject *from, SPDesktop *desktop, bool onlyvisibl
for (SPObject *child = sp_object_first_child(SP_OBJECT(from)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
if (SP_IS_ITEM(child) &&
!desktop->isLayer(SP_ITEM(child)) &&
- (!onlysensitive || !SP_ITEM(child)->isLocked()) &&
+ (!onlysensitive || !SP_ITEM(child)->isLocked()) &&
(!onlyvisible || !desktop->itemIsHidden(SP_ITEM(child))) &&
(!exclude || !g_slist_find ((GSList *) exclude, child))
)
}
void
-sp_undo(SPDesktop *desktop, SPDocument *doc)
+sp_undo(SPDesktop *desktop, SPDocument *)
{
if (!sp_document_undo(SP_DT_DOCUMENT(desktop)))
desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Nothing to undo."));
}
void
-sp_redo(SPDesktop *desktop, SPDocument *doc)
+sp_redo(SPDesktop *desktop, SPDocument *)
{
if (!sp_document_redo(SP_DT_DOCUMENT(desktop)))
desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Nothing to redo."));
{
SPGradient *ref = gradient;
- while (ref) {
+ while (ref) {
// climb up the refs, copying each one in the chain
Inkscape::XML::Node *grad_repr = SP_OBJECT_REPR(ref)->duplicate();
*defs_clip = g_slist_prepend (*defs_clip, grad_repr);
}
}
-void sp_copy_marker (GSList **defs_clip, SPMarker *marker)
+void sp_copy_single (GSList **defs_clip, SPObject *thing)
{
- Inkscape::XML::Node *marker_repr = SP_OBJECT_REPR(marker)->duplicate();
- *defs_clip = g_slist_prepend (*defs_clip, marker_repr);
+ Inkscape::XML::Node *duplicate_repr = SP_OBJECT_REPR(thing)->duplicate();
+ *defs_clip = g_slist_prepend (*defs_clip, duplicate_repr);
}
@@ -858,9 +862,9 @@ void sp_copy_textpath_path (GSList **defs_clip, SPTextPath *tp, const GSList *it
void sp_copy_stuff_used_by_item (GSList **defs_clip, SPItem *item, const GSList *items)
{
- SPStyle *style = SP_OBJECT_STYLE (item);
+ SPStyle *style = SP_OBJECT_STYLE (item);
- if (style && (style->fill.type == SP_PAINT_TYPE_PAINTSERVER)) {
+ if (style && (style->fill.type == SP_PAINT_TYPE_PAINTSERVER)) {
SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item);
if (SP_IS_LINEARGRADIENT (server) || SP_IS_RADIALGRADIENT (server))
sp_copy_gradient (defs_clip, SP_GRADIENT(server));
sp_copy_pattern (defs_clip, SP_PATTERN(server));
}
- if (style && (style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)) {
+ if (style && (style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)) {
SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER(item);
if (SP_IS_LINEARGRADIENT (server) || SP_IS_RADIALGRADIENT (server))
sp_copy_gradient (defs_clip, SP_GRADIENT(server));
@@ -876,11 +880,11 @@ void sp_copy_stuff_used_by_item (GSList **defs_clip, SPItem *item, const GSList
sp_copy_pattern (defs_clip, SP_PATTERN(server));
}
- if (SP_IS_SHAPE (item)) {
+ if (SP_IS_SHAPE (item)) {
SPShape *shape = SP_SHAPE (item);
for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
if (shape->marker[i]) {
- sp_copy_marker (defs_clip, SP_MARKER (shape->marker[i]));
+ sp_copy_single (defs_clip, SP_OBJECT (shape->marker[i]));
}
}
}
@@ -889,6 +893,20 @@ void sp_copy_stuff_used_by_item (GSList **defs_clip, SPItem *item, const GSList
sp_copy_textpath_path (defs_clip, SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item))), items);
}
+ if (item->clip_ref->getObject()) {
+ sp_copy_single (defs_clip, item->clip_ref->getObject());
+ }
+
+ if (item->mask_ref->getObject()) {
+ SPObject *mask = item->mask_ref->getObject();
+ sp_copy_single (defs_clip, mask);
+ // recurse into the mask for its gradients etc.
+ for (SPObject *o = SP_OBJECT(mask)->children; o != NULL; o = o->next) {
+ if (SP_IS_ITEM(o))
+ sp_copy_stuff_used_by_item (defs_clip, SP_ITEM (o), items);
+ }
+ }
+
// recurse
for (SPObject *o = SP_OBJECT(item)->children; o != NULL; o = o->next) {
if (SP_IS_ITEM(o))
const GSList *items = g_slist_copy ((GSList *) selection->itemList());
// 0. Copy text to system clipboard
- // FIXME: for non-texts, put serialized XML as text to the clipboard;
+ // FIXME: for non-texts, put serialized XML as text to the clipboard;
//for this sp_repr_write_stream needs to be rewritten with iostream instead of FILE
Glib::ustring text;
if (tools_isactive (desktop, TOOLS_TEXT)) {
sp_repr_css_attr_unref (style_clipboard);
style_clipboard = NULL;
}
-
- //clear main clipboard
+
+ //clear main clipboard
while (clipboard) {
Inkscape::GC::release((Inkscape::XML::Node *) clipboard->data);
clipboard = g_slist_remove(clipboard, clipboard->data);
void sp_selection_paste(bool in_place)
{
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
-
+
if (desktop == NULL) {
return;
}
sp_document_done(SP_DT_DOCUMENT (desktop));
}
-void sp_selection_to_next_layer ()
+void sp_selection_to_next_layer ()
{
SPDesktop *dt = SP_ACTIVE_DESKTOP;
g_slist_free ((GSList *) items);
}
-void sp_selection_to_prev_layer ()
+void sp_selection_to_prev_layer ()
{
SPDesktop *dt = SP_ACTIVE_DESKTOP;
if (next) {
GSList *temp_clip = NULL;
sp_selection_copy_impl (items, &temp_clip, NULL, NULL); // we're in the same doc, so no need to copy defs
- sp_selection_delete_impl (items);
+ sp_selection_delete_impl (items);
GSList *copied = sp_selection_paste_impl (SP_DT_DOCUMENT (dt), next, &temp_clip, NULL);
selection->setReprList((GSList const *) copied);
g_slist_free (copied);
@@ -1182,7 +1200,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const
SPItem *item = SP_ITEM(l->data);
NR::Point old_center(0,0);
- if (set_i2d && item->isCenterSet())
+ if (set_i2d && item->isCenterSet())
old_center = item->getCenter();
#if 0 /* Re-enable this once persistent guides have a graphical indication.
@@ -1232,7 +1250,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const
// calculate the matrix we need to apply to the clone to cancel its induced transform from its original
NR::Matrix t = matrix_to_desktop (matrix_from_desktop (affine, item), item);
- NR::Matrix t_inv = matrix_to_desktop (matrix_from_desktop (affine.inverse(), item), item);
+ NR::Matrix t_inv = matrix_to_desktop (matrix_from_desktop (affine.inverse(), item), item);
NR::Matrix result = t_inv * item->transform * t;
if ((prefs_parallel || prefs_unmoved) && affine.is_translation()) {
NR::Rect const bbox(selection->bounds());
if (bbox.isEmpty()) {
- return;
+ return;
}
NR::translate const p2o(-bbox.min());
@@ -1432,7 +1450,7 @@ sp_selection_rotate(Inkscape::Selection *selection, gdouble const angle_degrees)
if (selection->isEmpty())
return;
- NR::Point const center(selection->bounds().midpoint());
+ NR::Point center = selection->center();
sp_selection_rotate_relative(selection, center, angle_degrees);
return;
NR::Rect const bbox(selection->bounds());
- NR::Point const center(bbox.midpoint());
+
+ NR::Point center = selection->center();
gdouble const zoom = selection->desktop()->current_zoom();
gdouble const zmove = angle / zoom;
bool only_in_viewport, bool inlayer, bool onlyvisible, bool onlysensitive);
template <typename D>
-SPItem *next_item_from_list(SPDesktop *desktop, GSList const *items, SPObject *root,
+SPItem *next_item_from_list(SPDesktop *desktop, GSList const *items, SPObject *root,
bool only_in_viewport, bool inlayer, bool onlyvisible, bool onlysensitive);
struct Forward {
return;
}
- // Check if more than one object is selected.
- if (g_slist_length((GSList *) selection->itemList()) > 1) {
- desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("If you want to clone several objects, <b>group</b> them and <b>clone the group</b>."));
- return;
- }
-
- Inkscape::XML::Node *sel_repr = SP_OBJECT_REPR(selection->singleItem());
- Inkscape::XML::Node *parent = sp_repr_parent(sel_repr);
+ GSList *reprs = g_slist_copy((GSList *) selection->reprList());
+
+ selection->clear();
+
+ // sorting items from different parents sorts each parent's subset without possibly mixing them, just what we need
+ reprs = g_slist_sort(reprs, (GCompareFunc) sp_repr_compare_position);
- Inkscape::XML::Node *clone = sp_repr_new("svg:use");
- clone->setAttribute("x", "0");
- clone->setAttribute("y", "0");
- clone->setAttribute("xlink:href", g_strdup_printf("#%s", sel_repr->attribute("id")));
+ GSList *newsel = NULL;
+
+ while (reprs) {
+ Inkscape::XML::Node *sel_repr = (Inkscape::XML::Node *) reprs->data;
+ Inkscape::XML::Node *parent = sp_repr_parent(sel_repr);
- // add the new clone to the top of the original's parent
- parent->appendChild(clone);
+ Inkscape::XML::Node *clone = sp_repr_new("svg:use");
+ sp_repr_set_attr(clone, "x", "0");
+ sp_repr_set_attr(clone, "y", "0");
+ sp_repr_set_attr(clone, "xlink:href", g_strdup_printf("#%s", sel_repr->attribute("id")));
+
+ // add the new clone to the top of the original's parent
+ parent->appendChild(clone);
+ newsel = g_slist_prepend(newsel, clone);
+ reprs = g_slist_remove(reprs, sel_repr);
+ Inkscape::GC::release(clone);
+ }
+
sp_document_done(SP_DT_DOCUMENT(desktop));
- selection->set(clone);
- Inkscape::GC::release(clone);
+ selection->setReprList(newsel);
+
+ g_slist_free(newsel);
}
void
SPStyle *style = SP_OBJECT_STYLE (item);
- if (!style || style->fill.type != SP_PAINT_TYPE_PAINTSERVER)
+ if (!style || style->fill.type != SP_PAINT_TYPE_PAINTSERVER)
continue;
SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item);
SPItem *i = SP_ITEM (desktop->currentLayer()->appendChildRepr(copy));
// FIXME: relink clones to the new canvas objects
- // use SPObject::setid when mental finishes it to steal ids of
+ // use SPObject::setid when mental finishes it to steal ids of
// this is needed to make sure the new item has curve (simply requestDisplayUpdate does not work)
sp_document_ensure_up_to_date (document);
// multiple times, and this is done so that they don't clash)
GTimeVal cu;
g_get_current_time (&cu);
- guint current = (int) (cu.tv_sec * 1000000 + cu.tv_usec) % 1024;
+ guint current = (int) (cu.tv_sec * 1000000 + cu.tv_usec) % 1024;
// Create the filename
gchar *filename = g_strdup_printf ("%s-%s-%u.png", document->name, SP_OBJECT_REPR(items->data)->attribute("id"), current);
// Calculate the matrix that will be applied to the image so that it exactly overlaps the source objects
NR::Matrix eek = sp_item_i2d_affine (SP_ITEM(parent_object));
- NR::Matrix t = NR::scale (1/res, -1/res) * NR::translate (bbox.x0, bbox.y1) * eek.inverse();
+ NR::Matrix t = NR::scale (1/res, -1/res) * NR::translate (bbox.x0, bbox.y1) * eek.inverse();
// Do the export
sp_export_png_file(document, filepath,
gchar c[256];
if (sp_svg_transform_write(c, 256, t)) {
repr->setAttribute("transform", c);
- }
+ }
// add the new repr to the parent
parent->appendChild(repr);
- // move to the saved position
+ // move to the saved position
repr->setPosition(pos > 0 ? pos + 1 : 1);
// Set selection to the new image
g_free (filepath);
}
+/**
+ * \brief sp_selection_set_mask
+ *
+ * This function creates a mask or clipPath from selection
+ * Two different modes:
+ * if applyToLayer, all selection is moved to DEFS as mask/clippath
+ * and is applied to current layer
+ * otherwise, topmost object is used as mask for other objects
+ * If \a apply_clip_path parameter is true, clipPath is created, otherwise mask
+ *
+ */
+void
+sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop == NULL)
+ return;
+
+ SPDocument *document = SP_DT_DOCUMENT(desktop);
+
+ Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+
+ // check if something is selected
+ bool is_empty = selection->isEmpty();
+ if ( apply_to_layer && is_empty) {
+ desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to create mask from."));
+ return;
+ } else if (!apply_to_layer && ( is_empty || NULL == selection->itemList()->next )) {
+ desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select mask object and <b>object(s)</b> to apply mask to."));
+ return;
+ }
+
+ sp_document_ensure_up_to_date(document);
+
+ GSList *items = g_slist_copy((GSList *) selection->itemList());
+
+ items = g_slist_sort (items, (GCompareFunc) sp_object_compare_position);
+
+ // create a list of duplicates
+ GSList *mask_items = NULL;
+ GSList *apply_to_items = NULL;
+ bool topmost = prefs_get_int_attribute ("options.maskobject", "topmost", 1);
+ bool remove_original = prefs_get_int_attribute ("options.maskobject", "remove", 1);
+
+ if (apply_to_layer) {
+ // all selected items are used for mask, which is applied to a layer
+ apply_to_items = g_slist_prepend (apply_to_items, desktop->currentLayer());
+
+ for (GSList *i = items; i != NULL; i = i->next) {
+ Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate();
+ mask_items = g_slist_prepend (mask_items, dup);
+
+ if (remove_original) {
+ SPObject *item = SP_OBJECT (i->data);
+ item->deleteObject (false);
+ }
+ }
+ } else if (!topmost) {
+ // topmost item is used as a mask, which is applied to other items in a selection
+ GSList *i = items;
+ Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate();
+ mask_items = g_slist_prepend (mask_items, dup);
+
+ if (remove_original) {
+ SPObject *item = SP_OBJECT (i->data);
+ item->deleteObject (false);
+ }
+
+ for (i = i->next; i != NULL; i = i->next) {
+ apply_to_items = g_slist_prepend (apply_to_items, i->data);
+ }
+ } else {
+ GSList *i = NULL;
+ for (i = items; NULL != i->next; i = i->next) {
+ apply_to_items = g_slist_prepend (apply_to_items, i->data);
+ }
+
+ Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate();
+ mask_items = g_slist_prepend (mask_items, dup);
+
+ if (remove_original) {
+ SPObject *item = SP_OBJECT (i->data);
+ item->deleteObject (false);
+ }
+ }
+
+ g_slist_free (items);
+ items = NULL;
+
+ gchar const* attributeName = apply_clip_path ? "clip-path" : "mask";
+ for (GSList *i = apply_to_items; NULL != i; i = i->next) {
+ SPItem *item = reinterpret_cast<SPItem *>(i->data);
+ // inverted object transform should be applied to a mask object,
+ // as mask is calculated in user space (after applying transform)
+ NR::Matrix maskTransform (item->transform.inverse());
+
+ GSList *mask_items_dup = NULL;
+ for (GSList *mask_item = mask_items; NULL != mask_item; mask_item = mask_item->next) {
+ Inkscape::XML::Node *dup = reinterpret_cast<Inkscape::XML::Node *>(mask_item->data)->duplicate();
+ mask_items_dup = g_slist_prepend (mask_items_dup, dup);
+ }
+
+ const gchar *mask_id = NULL;
+ if (apply_clip_path) {
+ mask_id = sp_clippath_create(mask_items_dup, document, &maskTransform);
+ } else {
+ mask_id = sp_mask_create(mask_items_dup, document, &maskTransform);
+ }
+
+ g_slist_free (mask_items_dup);
+ mask_items_dup = NULL;
+
+ SP_OBJECT_REPR(i->data)->setAttribute(attributeName, g_strdup_printf("url(#%s)", mask_id));
+ }
+
+ g_slist_free (mask_items);
+ g_slist_free (apply_to_items);
+
+ sp_document_done (document);
+}
+
+void sp_selection_unset_mask(bool apply_clip_path) {
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop == NULL)
+ return;
+
+ SPDocument *document = SP_DT_DOCUMENT(desktop);
+ Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+
+ // check if something is selected
+ if (selection->isEmpty()) {
+ desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to remove mask from."));
+ return;
+ }
+
+ bool remove_original = prefs_get_int_attribute ("options.maskobject", "remove", 1);
+ sp_document_ensure_up_to_date(document);
+
+ gchar const* attributeName = apply_clip_path ? "clip-path" : "mask";
+ std::map<SPObject*,SPItem*> referenced_objects;
+ for (GSList const*i = selection->itemList(); NULL != i; i = i->next) {
+ if (remove_original) {
+ // remember referenced mask/clippath, so orphaned masks can be moved back to document
+ SPItem *item = reinterpret_cast<SPItem *>(i->data);
+ Inkscape::URIReference *uri_ref = NULL;
+
+ if (apply_clip_path) {
+ uri_ref = item->clip_ref;
+ } else {
+ uri_ref = item->mask_ref;
+ }
+
+ // collect distinct mask object (and associate with item to apply transform)
+ if (NULL != uri_ref && NULL != uri_ref->getObject()) {
+ referenced_objects[uri_ref->getObject()] = item;
+ }
+ }
+
+ SP_OBJECT_REPR(i->data)->setAttribute(attributeName, "none");
+ }
+
+ // restore mask objects into a document
+ for ( std::map<SPObject*,SPItem*>::iterator it = referenced_objects.begin() ; it != referenced_objects.end() ; ++it) {
+ SPObject *obj = (*it).first;
+ GSList *items_to_move = NULL;
+ for (SPObject *child = sp_object_first_child(obj) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
+ Inkscape::XML::Node *copy = SP_OBJECT_REPR(child)->duplicate();
+ items_to_move = g_slist_prepend (items_to_move, copy);
+ }
+
+ if (!obj->isReferenced()) {
+ // delete from defs if no other object references this mask
+ obj->deleteObject(false);
+ }
+
+ for (GSList *i = items_to_move; NULL != i; i = i->next) {
+ SPItem *item = SP_ITEM(desktop->currentLayer()->appendChildRepr((Inkscape::XML::Node *)i->data));
+ selection->add((Inkscape::XML::Node *)i->data);
+
+ // transform mask, so it is moved the same spot there mask was applied
+ NR::Matrix transform (item->transform);
+ transform *= (*it).second->transform;
+ sp_item_write_transform(item, SP_OBJECT_REPR(item), transform);
+ }
+
+ g_slist_free (items_to_move);
+ }
+
+ sp_document_done (document);
+}
/*
Local Variables: