From: gbanaszk Date: Tue, 17 Jul 2007 17:39:06 +0000 (+0000) Subject: A few additions to ensure that editing trefs is not allowed (and doesn't cause a... X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=21717617b65f988f286cc3ad117de9ac88c54a18;p=inkscape.git A few additions to ensure that editing trefs is not allowed (and doesn't cause a crash). Also allows all trefs within a selection to be 'unlinked'. --- diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index dd6ab6eb8..188ace06b 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -2122,6 +2122,18 @@ sp_selection_unlink() { SPItem *item = (SPItem *) items->data; + if (SP_IS_TEXT(item)) { + SPObject *tspan = sp_tref_convert_to_tspan(SP_OBJECT(item)); + + if (tspan) { + SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + + // Set unlink to true, and fall into the next if which + // will include this text item in the new selection + unlinked = true; + } + if (!(SP_IS_USE(item) || SP_IS_TREF(item))) { // keep the non-use item in the new selection new_select = g_slist_prepend(new_select, item); @@ -2132,7 +2144,7 @@ sp_selection_unlink() if (SP_IS_USE(item)) { unlink = sp_use_unlink(SP_USE(item)); } else /*if (SP_IS_TREF(use))*/ { - unlink = SP_ITEM(sp_tref_convert_to_tspan(SP_TREF(item))); + unlink = SP_ITEM(sp_tref_convert_to_tspan(SP_OBJECT(item))); } unlinked = true; diff --git a/src/sp-tref.cpp b/src/sp-tref.cpp index d5479b6cd..28db57990 100644 --- a/src/sp-tref.cpp +++ b/src/sp-tref.cpp @@ -559,55 +559,84 @@ build_string_from_root(Inkscape::XML::Node *root, Glib::ustring *retString) * The code is based partially on sp_use_unlink */ SPObject * -sp_tref_convert_to_tspan(SPTRef *tref) +sp_tref_convert_to_tspan(SPObject *obj) { SPObject * new_tspan = NULL; - if (tref && tref->stringChild) { - Inkscape::XML::Node *tref_repr = SP_OBJECT_REPR(tref); - Inkscape::XML::Node *tref_parent = sp_repr_parent(tref_repr); + //////////////////// + // BASE CASE + //////////////////// + if (SP_IS_TREF(obj)) { - SPDocument *document = SP_OBJECT(tref)->document; - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document); - - Inkscape::XML::Node *new_tspan_repr = xml_doc->createElement("svg:tspan"); - - // Add the new tspan element just after the current tref - tref_parent->addChild(new_tspan_repr, tref_repr); - Inkscape::GC::release(new_tspan_repr); - - new_tspan = document->getObjectByRepr(new_tspan_repr); - - // Create a new string child for the tspan - Inkscape::XML::Node *new_string_repr = SP_OBJECT_REPR(tref->stringChild)->duplicate(xml_doc); - new_tspan_repr->addChild(new_string_repr, NULL); - - SPObject * new_string_child = document->getObjectByRepr(new_string_repr); - - // Merge style from the tref - SPStyle *new_tspan_sty = SP_OBJECT_STYLE(new_tspan); - SPStyle const *tref_sty = SP_OBJECT_STYLE(tref); - sp_style_merge_from_dying_parent(new_tspan_sty, tref_sty); - sp_style_merge_from_parent(new_tspan_sty, new_tspan->parent->style); - - - SP_OBJECT(new_tspan)->updateRepr(); - - // Hold onto our SPObject and repr for now. - sp_object_ref(SP_OBJECT(tref), NULL); - Inkscape::GC::anchor(tref_repr); - - // Remove ourselves, not propagating delete events to avoid a - // chain-reaction with other elements that might reference us. - SP_OBJECT(tref)->deleteObject(false); - - // Give the copy our old id and let go of our old repr. - new_tspan_repr->setAttribute("id", tref_repr->attribute("id")); - Inkscape::GC::release(tref_repr); - - // Establish the succession and let go of our object. - SP_OBJECT(tref)->setSuccessor(new_tspan); - sp_object_unref(SP_OBJECT(tref), NULL); + SPTRef *tref = SP_TREF(obj); + + if (tref && tref->stringChild) { + Inkscape::XML::Node *tref_repr = SP_OBJECT_REPR(tref); + Inkscape::XML::Node *tref_parent = sp_repr_parent(tref_repr); + + SPDocument *document = SP_OBJECT(tref)->document; + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document); + + Inkscape::XML::Node *new_tspan_repr = xml_doc->createElement("svg:tspan"); + + // Add the new tspan element just after the current tref + tref_parent->addChild(new_tspan_repr, tref_repr); + Inkscape::GC::release(new_tspan_repr); + + new_tspan = document->getObjectByRepr(new_tspan_repr); + + // Create a new string child for the tspan + Inkscape::XML::Node *new_string_repr = SP_OBJECT_REPR(tref->stringChild)->duplicate(xml_doc); + new_tspan_repr->addChild(new_string_repr, NULL); + + //SPObject * new_string_child = document->getObjectByRepr(new_string_repr); + + // Merge style from the tref + SPStyle *new_tspan_sty = SP_OBJECT_STYLE(new_tspan); + SPStyle const *tref_sty = SP_OBJECT_STYLE(tref); + sp_style_merge_from_dying_parent(new_tspan_sty, tref_sty); + sp_style_merge_from_parent(new_tspan_sty, new_tspan->parent->style); + + + SP_OBJECT(new_tspan)->updateRepr(); + + // Hold onto our SPObject and repr for now. + sp_object_ref(SP_OBJECT(tref), NULL); + Inkscape::GC::anchor(tref_repr); + + // Remove ourselves, not propagating delete events to avoid a + // chain-reaction with other elements that might reference us. + SP_OBJECT(tref)->deleteObject(false); + + // Give the copy our old id and let go of our old repr. + new_tspan_repr->setAttribute("id", tref_repr->attribute("id")); + Inkscape::GC::release(tref_repr); + + // Establish the succession and let go of our object. + SP_OBJECT(tref)->setSuccessor(new_tspan); + sp_object_unref(SP_OBJECT(tref), NULL); + } + } + //////////////////// + // RECURSIVE CASE + //////////////////// + else { + GSList *l = NULL; + for (SPObject *child = sp_object_first_child(obj) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) { + sp_object_ref (SP_OBJECT (child), obj); + l = g_slist_prepend (l, child); + } + l = g_slist_reverse (l); + while (l) { + SPObject *child = SP_OBJECT (l->data); + l = g_slist_remove (l, child); + + // Note that there may be more than one conversion happening here, so if it's not a + // tref being passed into this function, the returned value can't be specifically known + new_tspan = sp_tref_convert_to_tspan(child); + + sp_object_unref (SP_OBJECT (child), obj); + } } return new_tspan; diff --git a/src/sp-tref.h b/src/sp-tref.h index eea8207e3..2e79a28c3 100644 --- a/src/sp-tref.h +++ b/src/sp-tref.h @@ -63,7 +63,7 @@ void sp_tref_update_text(SPTRef *tref); bool sp_tref_reference_allowed(SPTRef *tref, SPObject *possible_ref); bool sp_tref_fully_contained(SPObject *start_item, Glib::ustring::iterator &start, SPObject *end_item, Glib::ustring::iterator &end); -SPObject * sp_tref_convert_to_tspan(SPTRef *item); +SPObject * sp_tref_convert_to_tspan(SPObject *item); #endif /* !SP_TREF_H */ diff --git a/src/text-context.cpp b/src/text-context.cpp index b45cd6e58..f3f974c6f 100644 --- a/src/text-context.cpp +++ b/src/text-context.cpp @@ -916,7 +916,9 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) sp_text_context_setup_text(tc); tc->nascent_object = 0; // we don't need it anymore, having created a real } - tc->text_sel_start = tc->text_sel_end = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end); + tc->text_sel_start = tc->text_sel_end + = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end, SP_TE_DELETE_OTHER); + tc->text_sel_start = tc->text_sel_end = sp_te_insert_line(tc->text, tc->text_sel_start); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); @@ -925,9 +927,16 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) return TRUE; case GDK_BackSpace: if (tc->text) { // if nascent_object, do nothing, but return TRUE; same for all other delete and move keys - if (tc->text_sel_start == tc->text_sel_end) + sp_te_deletion_type deleteType = SP_TE_DELETE_OTHER; + + if (tc->text_sel_start == tc->text_sel_end) { tc->text_sel_start.prevCursorPosition(); - tc->text_sel_start = tc->text_sel_end = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end); + deleteType = SP_TE_DELETE_SINGLE_BACKSPACE; + } + + tc->text_sel_start = tc->text_sel_end + = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end, deleteType); + sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); sp_document_done(sp_desktop_document(ec->desktop), SP_VERB_CONTEXT_TEXT, @@ -937,9 +946,16 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) case GDK_Delete: case GDK_KP_Delete: if (tc->text) { - if (tc->text_sel_start == tc->text_sel_end) + sp_te_deletion_type deleteType = SP_TE_DELETE_OTHER; + + if (tc->text_sel_start == tc->text_sel_end) { tc->text_sel_end.nextCursorPosition(); - tc->text_sel_start = tc->text_sel_end = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end); + deleteType = SP_TE_SINGLE_DELETE; + } + + tc->text_sel_start = tc->text_sel_end + = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end, deleteType); + sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); sp_document_done(sp_desktop_document(ec->desktop), SP_VERB_CONTEXT_TEXT, @@ -1291,7 +1307,7 @@ bool sp_text_delete_selection(SPEventContext *ec) if (tc->text_sel_start == tc->text_sel_end) return false; - tc->text_sel_start = tc->text_sel_end = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end); + tc->text_sel_start = tc->text_sel_end = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end, SP_TE_DELETE_OTHER); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); return true; diff --git a/src/text-editing.cpp b/src/text-editing.cpp index bed837cef..2ad894cc6 100644 --- a/src/text-editing.cpp +++ b/src/text-editing.cpp @@ -138,7 +138,7 @@ char * dump_hexy(const gchar * utf8) Inkscape::Text::Layout::iterator sp_te_replace(SPItem *item, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, gchar const *utf8) { - Inkscape::Text::Layout::iterator new_start = sp_te_delete(item, start, end); + Inkscape::Text::Layout::iterator new_start = sp_te_delete(item, start, end, SP_TE_DELETE_OTHER); return sp_te_insert(item, new_start, utf8); } @@ -148,13 +148,28 @@ Inkscape::Text::Layout::iterator sp_te_replace(SPItem *item, Inkscape::Text::Lay static bool is_line_break_object(SPObject const *object) { - return SP_IS_TEXT(object) - || (SP_IS_TSPAN(object) && SP_TSPAN(object)->role != SP_TSPAN_ROLE_UNSPECIFIED) - || SP_IS_TEXTPATH(object) - || SP_IS_FLOWDIV(object) - || SP_IS_FLOWPARA(object) - || SP_IS_FLOWLINE(object) - || SP_IS_FLOWREGIONBREAK(object); + bool is_line_break = false; + + if (object) { + if (SP_IS_TEXT(object) + || SP_IS_TEXTPATH(object) + || SP_IS_FLOWDIV(object) + || SP_IS_FLOWPARA(object) + || SP_IS_FLOWLINE(object) + || SP_IS_FLOWREGIONBREAK(object)) { + + is_line_break = true; + } + + if (SP_IS_TSPAN(object) && SP_TSPAN(object)->role != SP_TSPAN_ROLE_UNSPECIFIED) { + SPObject *prev_object = SP_OBJECT_PREV(object); + if (prev_object && SP_IS_TSPAN(prev_object)) { + is_line_break = true; + } + } + } + + return is_line_break; } /** returns the attributes for an object, or NULL if it isn't a text, @@ -334,7 +349,7 @@ Inkscape::Text::Layout::iterator sp_te_insert_line (SPItem *item, Inkscape::Text { // Disable newlines in a textpath; TODO: maybe on Enter in a textpath, separate it into two // texpaths attached to the same path, with a vertical shift - if (SP_IS_TEXT_TEXTPATH (item)) + if (SP_IS_TEXT_TEXTPATH (item) || SP_IS_TREF(item)) return position; SPDesktop *desktop = SP_ACTIVE_DESKTOP; @@ -350,6 +365,12 @@ Inkscape::Text::Layout::iterator sp_te_insert_line (SPItem *item, Inkscape::Text if (split_obj == 0 || is_line_break_object(split_obj)) { if (split_obj == 0) split_obj = item->lastChild(); + + if (SP_IS_TREF(split_obj)) { + desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, tref_edit_message); + return position; + } + if (split_obj) { Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(split_obj)); Inkscape::XML::Node *new_node = duplicate_node_without_children(xml_doc, SP_OBJECT_REPR(split_obj)); @@ -660,7 +681,8 @@ of figuring out what is a line break and how to delete one. Returns the lesser of \a start and \a end, because that is where the cursor should be put after the deletion is done. */ Inkscape::Text::Layout::iterator -sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end) +sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start, + Inkscape::Text::Layout::iterator const &end, sp_te_deletion_type deletionType) { if (start == end) return start; Inkscape::Text::Layout::iterator first, last; @@ -701,10 +723,15 @@ sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inksc // If the parent is a tref, editing on this particular string is disallowed. if (SP_IS_TREF(SP_OBJECT_PARENT(start_item))) { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, tref_edit_message); - return end; + + // Compensate so the cursor doesn't move when hitting backspace + if (deletionType == SP_TE_DELETE_SINGLE_BACKSPACE) { + first = last; + } + + } else { + erase_from_spstring(SP_STRING(start_item), start_text_iter, end_text_iter); } - - erase_from_spstring(SP_STRING(start_item), start_text_iter, end_text_iter); } } else { SPObject *sub_item = start_item; @@ -715,7 +742,11 @@ sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inksc // If the parent is a tref, editing on this particular string is disallowed. if (SP_IS_TREF(SP_OBJECT_PARENT(sub_item))) { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, tref_edit_message); - return end; + // Compensate so the cursor doesn't move when hitting backspace + if (deletionType == SP_TE_DELETE_SINGLE_BACKSPACE) { + //first = last; + } + break; } Glib::ustring *string = &SP_STRING(sub_item)->string; @@ -1686,7 +1717,7 @@ static bool tidy_xml_tree_recursively(SPObject *root) bool changes = false; for (SPObject *child = root->firstChild() ; child != NULL ; ) { - if (SP_IS_FLOWREGION(child) || SP_IS_FLOWREGIONEXCLUDE(child)) { + if (SP_IS_FLOWREGION(child) || SP_IS_FLOWREGIONEXCLUDE(child) || SP_IS_TREF(child)) { child = SP_OBJECT_NEXT(child); continue; } diff --git a/src/text-editing.h b/src/text-editing.h index dba763f75..8955d1e30 100644 --- a/src/text-editing.h +++ b/src/text-editing.h @@ -34,9 +34,11 @@ SPStyle const * sp_te_style_at_position(SPItem const *text, Inkscape::Text::Layo Inkscape::Text::Layout::iterator sp_te_insert(SPItem *item, Inkscape::Text::Layout::iterator const &position, gchar const *utf8); Inkscape::Text::Layout::iterator sp_te_replace(SPItem *item, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, gchar const *utf8); -Inkscape::Text::Layout::iterator sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end); Inkscape::Text::Layout::iterator sp_te_insert_line (SPItem *text, Inkscape::Text::Layout::iterator const &position); +enum sp_te_deletion_type { SP_TE_DELETE_SINGLE_BACKSPACE, SP_TE_SINGLE_DELETE, SP_TE_DELETE_OTHER }; +Inkscape::Text::Layout::iterator sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, sp_te_deletion_type deletionType); + gchar *sp_te_get_string_multiline(SPItem const *text); Glib::ustring sp_te_get_string_multiline(SPItem const *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end); void sp_te_set_repr_text_multiline(SPItem *text, gchar const *str);