Code

Fix self-snapping when dragging the transformation center of a selection containing...
[inkscape.git] / src / text-context.cpp
index ad9211cbac0e363ef79d759c38fd2416288f3317..c10e0d1a0f70846cb68d9f405f0795da0a3b2fa4 100644 (file)
@@ -35,7 +35,6 @@
 #include "desktop.h"
 #include "desktop-style.h"
 #include "desktop-handles.h"
-#include "desktop-affine.h"
 #include "message-stack.h"
 #include "message-context.h"
 #include "pixmaps/cursor-text.xpm"
@@ -50,7 +49,7 @@
 #include "context-fns.h"
 #include "verbs.h"
 #include "shape-editor.h"
-
+#include "selection-chemistry.h"
 #include "text-editing.h"
 
 #include "text-context.h"
@@ -386,6 +385,7 @@ sp_text_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEve
         case GDK_BUTTON_RELEASE:
             if (event->button.button == 1 && tc->dragging && !event_context->space_panning) {
                 tc->dragging = 0;
+                sp_event_context_discard_delayed_snap_event(event_context);
                 ret = TRUE;
             }
             break;
@@ -425,11 +425,18 @@ sp_text_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEve
             // find out item under mouse, disregarding groups
             item_ungrouped = desktop->item_at_point(Geom::Point(event->button.x, event->button.y), TRUE);
             if (SP_IS_TEXT(item_ungrouped) || SP_IS_FLOWTEXT(item_ungrouped)) {
-                sp_canvas_item_show(tc->indicator);
+
+                Inkscape::Text::Layout const *layout = te_get_layout(item_ungrouped);
+                if (layout->inputTruncated()) {
+                    SP_CTRLRECT(tc->indicator)->setColor(0xff0000ff, false, 0);
+                } else {
+                    SP_CTRLRECT(tc->indicator)->setColor(0x0000ff7f, false, 0);
+                }
                 Geom::OptRect ibbox = sp_item_bbox_desktop(item_ungrouped);
                 if (ibbox) {
                     SP_CTRLRECT(tc->indicator)->setRectangle(*ibbox);
                 }
+                sp_canvas_item_show(tc->indicator);
 
                 event_context->cursor_shape = cursor_text_insert_xpm;
                 event_context->hot_x = 7;
@@ -603,7 +610,14 @@ sp_text_context_root_handler(SPEventContext *const event_context, GdkEvent *cons
                 event_context->within_tolerance = true;
 
                 Geom::Point const button_pt(event->button.x, event->button.y);
-                tc->p0 = desktop->w2d(button_pt);
+                Geom::Point button_dt(desktop->w2d(button_pt));
+
+                SnapManager &m = desktop->namedview->snap_manager;
+                m.setup(desktop);
+                m.freeSnapReturnByRef(button_dt, Inkscape::SNAPSOURCE_NODE_HANDLE);
+                m.unSetup();
+
+                tc->p0 = button_dt;
                 Inkscape::Rubberband::get(desktop)->start(desktop, tc->p0);
                 sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
                                     GDK_KEY_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK,
@@ -638,7 +652,12 @@ sp_text_context_root_handler(SPEventContext *const event_context, GdkEvent *cons
                 event_context->within_tolerance = false;
 
                 Geom::Point const motion_pt(event->motion.x, event->motion.y);
-                Geom::Point const p = desktop->w2d(motion_pt);
+                Geom::Point p = desktop->w2d(motion_pt);
+
+                SnapManager &m = desktop->namedview->snap_manager;
+                m.setup(desktop);
+                m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE);
+                m.unSetup();
 
                 Inkscape::Rubberband::get(desktop)->move(p);
                 gobble_motion_events(GDK_BUTTON1_MASK);
@@ -650,10 +669,26 @@ sp_text_context_root_handler(SPEventContext *const event_context, GdkEvent *cons
                 g_string_free(xs, FALSE);
                 g_string_free(ys, FALSE);
 
+            } else if (!sp_event_context_knot_mouseover(event_context)) {
+                SnapManager &m = desktop->namedview->snap_manager;
+                m.setup(desktop);
+
+                Geom::Point const motion_w(event->motion.x, event->motion.y);
+                Geom::Point motion_dt(desktop->w2d(motion_w));
+                m.preSnap(Inkscape::SnapCandidatePoint(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE));
+                m.unSetup();
             }
             break;
         case GDK_BUTTON_RELEASE:
             if (event->button.button == 1 && !event_context->space_panning) {
+                sp_event_context_discard_delayed_snap_event(event_context);
+
+                Geom::Point p1 = desktop->w2d(Geom::Point(event->button.x, event->button.y));
+
+                SnapManager &m = desktop->namedview->snap_manager;
+                m.setup(desktop);
+                m.freeSnapReturnByRef(p1, Inkscape::SNAPSOURCE_NODE_HANDLE);
+                m.unSetup();
 
                 if (tc->grabbed) {
                     sp_canvas_item_ungrab(tc->grabbed, GDK_CURRENT_TIME);
@@ -665,9 +700,7 @@ sp_text_context_root_handler(SPEventContext *const event_context, GdkEvent *cons
                 if (tc->creating && event_context->within_tolerance) {
                     /* Button 1, set X & Y & new item */
                     sp_desktop_selection(desktop)->clear();
-                    Geom::Point dtp = desktop->w2d(Geom::Point(event->button.x, event->button.y));
-                    tc->pdoc = sp_desktop_dt2doc_xy_point(desktop, dtp);
-
+                    tc->pdoc = desktop->dt2doc(p1);
                     tc->show = TRUE;
                     tc->phase = 1;
                     tc->nascent_object = 1; // new object was just created
@@ -675,15 +708,13 @@ sp_text_context_root_handler(SPEventContext *const event_context, GdkEvent *cons
                     /* Cursor */
                     sp_canvas_item_show(tc->cursor);
                     // Cursor height is defined by the new text object's font size; it needs to be set
-                    // articifically here, for the text object does not exist yet:
+                    // artificially here, for the text object does not exist yet:
                     double cursor_height = sp_desktop_get_font_size_tool(desktop);
-                    sp_ctrlline_set_coords(SP_CTRLLINE(tc->cursor), dtp, dtp + Geom::Point(0, cursor_height));
+                    sp_ctrlline_set_coords(SP_CTRLLINE(tc->cursor), p1, p1 + Geom::Point(0, cursor_height));
                     event_context->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Type text; <b>Enter</b> to start new line.")); // FIXME:: this is a copy of a string from _update_cursor below, do not desync
 
                     event_context->within_tolerance = false;
                 } else if (tc->creating) {
-                    Geom::Point const button_pt(event->button.x, event->button.y);
-                    Geom::Point p1 = desktop->w2d(button_pt);
                     double cursor_height = sp_desktop_get_font_size_tool(desktop);
                     if (fabs(p1[Geom::Y] - tc->p0[Geom::Y]) > cursor_height) {
                         // otherwise even one line won't fit; most probably a slip of hand (even if bigger than tolerance)
@@ -881,10 +912,10 @@ sp_text_context_root_handler(SPEventContext *const event_context, GdkEvent *cons
                                 if (MOD__CTRL_ONLY && tc->text) {
                                     SPStyle const *style = sp_te_style_at_position(tc->text, std::min(tc->text_sel_start, tc->text_sel_end));
                                     SPCSSAttr *css = sp_repr_css_attr_new();
-                                    if (style->font_style.computed == SP_CSS_FONT_STYLE_NORMAL)
-                                        sp_repr_css_set_property(css, "font-style", "italic");
-                                    else
+                                    if (style->font_style.computed != SP_CSS_FONT_STYLE_NORMAL)
                                         sp_repr_css_set_property(css, "font-style", "normal");
+                                    else
+                                        sp_repr_css_set_property(css, "font-style", "italic");
                                     sp_te_apply_style(tc->text, tc->text_sel_start, tc->text_sel_end, css);
                                     sp_repr_css_attr_unref(css);
                                     sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT,
@@ -1011,7 +1042,7 @@ sp_text_context_root_handler(SPEventContext *const event_context, GdkEvent *cons
                                         sp_document_maybe_done(sp_desktop_document(desktop), "kern:left", SP_VERB_CONTEXT_TEXT,
                                                                _("Kern to the left"));
                                     } else {
-                                        if (MOD__CTRL) 
+                                        if (MOD__CTRL)
                                             tc->text_sel_end.cursorLeftWithControl();
                                         else
                                             tc->text_sel_end.cursorLeft();
@@ -1276,6 +1307,9 @@ sp_text_context_root_handler(SPEventContext *const event_context, GdkEvent *cons
                         }
                         Inkscape::Rubberband::get(desktop)->stop();
                     }
+                } else if ((group0_keyval == GDK_x || group0_keyval == GDK_X) && MOD__ALT_ONLY) {
+                    desktop->setToolboxFocusTo ("altx-text");
+                    return TRUE;
                 }
             }
             break;
@@ -1314,7 +1348,7 @@ sp_text_paste_inline(SPEventContext *ec)
 
         Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
         Glib::ustring const clip_text = refClipboard->wait_for_text();
-        
+
         if (!clip_text.empty()) {
                // Fix for 244940
                // The XML standard defines the following as valid characters
@@ -1342,7 +1376,7 @@ sp_text_paste_inline(SPEventContext *ec)
                        itr = text.erase(itr);
                    }
                }
-               
+
             if (!tc->text) { // create text if none (i.e. if nascent_object)
                 sp_text_context_setup_text(tc);
                 tc->nascent_object = 0; // we don't need it anymore, having created a real <text>
@@ -1387,6 +1421,21 @@ sp_text_get_selected_text(SPEventContext const *ec)
     return sp_te_get_string_multiline(tc->text, tc->text_sel_start, tc->text_sel_end);
 }
 
+SPCSSAttr *
+sp_text_get_style_at_cursor(SPEventContext const *ec)
+{
+    if (!SP_IS_TEXT_CONTEXT(ec))
+        return NULL;
+    SPTextContext const *tc = SP_TEXT_CONTEXT(ec);
+    if (tc->text == NULL)
+        return NULL;
+
+    SPObject const *obj = sp_te_object_at_position(tc->text, tc->text_sel_end);
+    if (obj)
+        return take_style_from_item((SPItem *) obj);
+    return NULL;
+}
+
 /**
  Deletes the currently selected characters. Returns false if there is no
  text selection currently.
@@ -1430,7 +1479,7 @@ sp_text_context_selection_changed(Inkscape::Selection *selection, SPTextContext
     SPEventContext *ec = SP_EVENT_CONTEXT(tc);
 
     ec->shape_editor->unset_item(SH_KNOTHOLDER);
-    SPItem *item = selection->singleItem(); 
+    SPItem *item = selection->singleItem();
     if (item && SP_IS_FLOWTEXT (item) && SP_FLOWTEXT(item)->has_internal_frame()) {
         ec->shape_editor->set_item(item, SH_KNOTHOLDER);
     }
@@ -1570,18 +1619,32 @@ sp_text_context_update_cursor(SPTextContext *tc,  bool scroll_to_see)
         tc->show = TRUE;
         tc->phase = 1;
 
+        Inkscape::Text::Layout const *layout = te_get_layout(tc->text);
+        int const nChars = layout->iteratorToCharIndex(layout->end());
+        char const *trunc = "";
+        bool truncated = false;
+        if (layout->inputTruncated()) {
+            truncated = true;
+            trunc = _(" [truncated]");
+        }
         if (SP_IS_FLOWTEXT(tc->text)) {
             SPItem *frame = SP_FLOWTEXT(tc->text)->get_frame (NULL); // first frame only
             if (frame) {
+                if (truncated) {
+                    SP_CTRLRECT(tc->frame)->setColor(0xff0000ff, false, 0);
+                } else {
+                    SP_CTRLRECT(tc->frame)->setColor(0x0000ff7f, false, 0);
+                }
                 sp_canvas_item_show(tc->frame);
                 Geom::OptRect frame_bbox = sp_item_bbox_desktop(frame);
                 if (frame_bbox) {
                     SP_CTRLRECT(tc->frame)->setRectangle(*frame_bbox);
                 }
             }
-            SP_EVENT_CONTEXT(tc)->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Type flowed text; <b>Enter</b> to start new paragraph."));
+
+            SP_EVENT_CONTEXT(tc)->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Type or edit flowed text (%d characters%s); <b>Enter</b> to start new paragraph."), nChars, trunc);
         } else {
-            SP_EVENT_CONTEXT(tc)->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Type text; <b>Enter</b> to start new line."));
+            SP_EVENT_CONTEXT(tc)->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Type or edit text (%d characters%s); <b>Enter</b> to start new line."), nChars, trunc);
         }
 
     } else {
@@ -1702,6 +1765,29 @@ sptc_commit(GtkIMContext */*imc*/, gchar *string, SPTextContext *tc)
                      _("Type text"));
 }
 
+void
+sp_text_context_place_cursor (SPTextContext *tc, SPObject *text, Inkscape::Text::Layout::iterator where)
+{
+    SP_EVENT_CONTEXT_DESKTOP (tc)->selection->set (text);
+    tc->text_sel_start = tc->text_sel_end = where;
+    sp_text_context_update_cursor(tc);
+    sp_text_context_update_text_selection(tc);
+}
+
+void
+sp_text_context_place_cursor_at (SPTextContext *tc, SPObject *text, Geom::Point const p)
+{
+    SP_EVENT_CONTEXT_DESKTOP (tc)->selection->set (text);
+    sp_text_context_place_cursor (tc, text, sp_te_get_position_by_coords(tc->text, p));
+}
+
+Inkscape::Text::Layout::iterator *sp_text_context_get_cursor_position(SPTextContext *tc, SPObject *text)
+{
+    if (text != tc->text)
+        return NULL;
+    return &(tc->text_sel_end);
+}
+
 
 /*
   Local Variables: