Code

A few additions to ensure that editing trefs is not allowed (and doesn't cause a...
authorgbanaszk <gbanaszk@users.sourceforge.net>
Tue, 17 Jul 2007 17:39:06 +0000 (17:39 +0000)
committergbanaszk <gbanaszk@users.sourceforge.net>
Tue, 17 Jul 2007 17:39:06 +0000 (17:39 +0000)
src/selection-chemistry.cpp
src/sp-tref.cpp
src/sp-tref.h
src/text-context.cpp
src/text-editing.cpp
src/text-editing.h

index dd6ab6eb80a689d40350e9de84490c09a7ae51e6..188ace06bd49cc91115bec2e962df56b6e68bbb6 100644 (file)
@@ -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;
index d5479b6cda02edc110f33c77bf4a809ab63de8aa..28db57990e25b32c7f43563c08e47395ba608765 100644 (file)
@@ -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;
index eea8207e3c3b1dbdb5aac3a8b1824af8d58519b8..2e79a28c394f78a1e86042d2d2ebafed4a1b06eb 100644 (file)
@@ -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 */
index b45cd6e58090af5155975ba251de874154852655..f3f974c6f96356b8ecdb1b981dae53240b8ad621 100644 (file)
@@ -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 <text>
                                 }
-                                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;
index bed837cefabf2ff65903734a16ad15c3e5bd8e56..2ad894cc6122e2269e4046219711da96a7a81af6 100644 (file)
@@ -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;
         }
index dba763f75e7c0c5d8fe3f294252874f445aa159d..8955d1e30507f0e470d8faacb0048b4e9fc95245 100644 (file)
@@ -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);