X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Ftext-context.cpp;h=4b25deb9335358114453153b98365c371dcb66dd;hb=80d34fe4a953d704430b44c6201a4fcbf455dfc6;hp=9645887941f03889edf64ee5187a477040d74d24;hpb=e1c04043fc65d6a82f330792f86a140b3ed324ad;p=inkscape.git diff --git a/src/text-context.cpp b/src/text-context.cpp index 964588794..4b25deb93 100644 --- a/src/text-context.cpp +++ b/src/text-context.cpp @@ -48,6 +48,7 @@ #include "rubberband.h" #include "sp-metrics.h" #include "context-fns.h" +#include "verbs.h" #include "text-editing.h" @@ -184,7 +185,7 @@ sp_text_context_dispose(GObject *obj) Inkscape::Rubberband::get()->stop(); if (ec->shape_knot_holder) { - sp_knot_holder_destroy(ec->shape_knot_holder); + delete ec->shape_knot_holder; ec->shape_knot_holder = NULL; } if (ec->shape_repr) { // remove old listener @@ -289,6 +290,10 @@ sp_text_context_finish(SPEventContext *ec) { SPTextContext *tc = SP_TEXT_CONTEXT(ec); + if (ec->desktop) { + sp_signal_disconnect_by_data(sp_desktop_canvas(ec->desktop), tc); + } + ec->enableGrDrag(false); tc->style_set_connection.disconnect(); @@ -329,18 +334,14 @@ sp_text_context_finish(SPEventContext *ec) gtk_object_destroy(*it); } tc->text_selection_quads.clear(); - - if (ec->desktop) { - sp_signal_disconnect_by_data(sp_desktop_canvas(ec->desktop), tc); - } } static gint -sp_text_context_item_handler(SPEventContext *ec, SPItem *item, GdkEvent *event) +sp_text_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event) { - SPTextContext *tc = SP_TEXT_CONTEXT(ec); - SPDesktop *desktop = ec->desktop; + SPTextContext *tc = SP_TEXT_CONTEXT(event_context); + SPDesktop *desktop = event_context->desktop; SPItem *item_ungrouped; gint ret = FALSE; @@ -349,14 +350,14 @@ sp_text_context_item_handler(SPEventContext *ec, SPItem *item, GdkEvent *event) switch (event->type) { case GDK_BUTTON_PRESS: - if (event->button.button == 1) { + if (event->button.button == 1 && !event_context->space_panning) { // find out clicked item, disregarding groups item_ungrouped = desktop->item_at_point(NR::Point(event->button.x, event->button.y), TRUE); if (SP_IS_TEXT(item_ungrouped) || SP_IS_FLOWTEXT(item_ungrouped)) { - sp_desktop_selection(ec->desktop)->set(item_ungrouped); + sp_desktop_selection(desktop)->set(item_ungrouped); if (tc->text) { // find out click point in document coordinates - NR::Point p = ec->desktop->w2d(NR::Point(event->button.x, event->button.y)); + NR::Point p = desktop->w2d(NR::Point(event->button.x, event->button.y)); // set the cursor closest to that point tc->text_sel_start = tc->text_sel_end = sp_te_get_position_by_coords(tc->text, p); // update display @@ -394,17 +395,17 @@ sp_text_context_item_handler(SPEventContext *ec, SPItem *item, GdkEvent *event) } break; case GDK_BUTTON_RELEASE: - if (event->button.button == 1 && tc->dragging) { + if (event->button.button == 1 && tc->dragging && !event_context->space_panning) { tc->dragging = 0; ret = TRUE; } break; case GDK_MOTION_NOTIFY: - if (event->motion.state & GDK_BUTTON1_MASK && tc->dragging) { + if (event->motion.state & GDK_BUTTON1_MASK && tc->dragging && !event_context->space_panning) { Inkscape::Text::Layout const *layout = te_get_layout(tc->text); if (!layout) break; // find out click point in document coordinates - NR::Point p = ec->desktop->w2d(NR::Point(event->button.x, event->button.y)); + NR::Point p = desktop->w2d(NR::Point(event->button.x, event->button.y)); // set the cursor closest to that point Inkscape::Text::Layout::iterator new_end = sp_te_get_position_by_coords(tc->text, p); if (tc->dragging == 2) { @@ -428,6 +429,7 @@ sp_text_context_item_handler(SPEventContext *ec, SPItem *item, GdkEvent *event) sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); } + gobble_motion_events(GDK_BUTTON1_MASK); ret = TRUE; break; } @@ -435,12 +437,15 @@ sp_text_context_item_handler(SPEventContext *ec, SPItem *item, GdkEvent *event) item_ungrouped = desktop->item_at_point(NR::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); - SP_CTRLRECT(tc->indicator)->setRectangle(sp_item_bbox_desktop(item_ungrouped)); + NR::Maybe ibbox = sp_item_bbox_desktop(item_ungrouped); + if (ibbox) { + SP_CTRLRECT(tc->indicator)->setRectangle(*ibbox); + } - ec->cursor_shape = cursor_text_insert_xpm; - ec->hot_x = 7; - ec->hot_y = 10; - sp_event_context_update_cursor(ec); + event_context->cursor_shape = cursor_text_insert_xpm; + event_context->hot_x = 7; + event_context->hot_y = 10; + sp_event_context_update_cursor(event_context); sp_text_context_update_text_selection(tc); if (SP_IS_TEXT (item_ungrouped)) { @@ -460,7 +465,7 @@ sp_text_context_item_handler(SPEventContext *ec, SPItem *item, GdkEvent *event) if (!ret) { if (((SPEventContextClass *) parent_class)->item_handler) - ret = ((SPEventContextClass *) parent_class)->item_handler(ec, item, event); + ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event); } return ret; @@ -472,7 +477,8 @@ sp_text_context_setup_text(SPTextContext *tc) SPEventContext *ec = SP_EVENT_CONTEXT(tc); /* Create */ - Inkscape::XML::Node *rtext = sp_repr_new("svg:text"); + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_EVENT_CONTEXT_DESKTOP(ec)->doc()); + Inkscape::XML::Node *rtext = xml_doc->createElement("svg:text"); rtext->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create /* Set style */ @@ -482,13 +488,13 @@ sp_text_context_setup_text(SPTextContext *tc) sp_repr_set_svg_double(rtext, "y", tc->pdoc[NR::Y]); /* Create */ - Inkscape::XML::Node *rtspan = sp_repr_new("svg:tspan"); + Inkscape::XML::Node *rtspan = xml_doc->createElement("svg:tspan"); rtspan->setAttribute("sodipodi:role", "line"); // otherwise, why bother creating the tspan? rtext->addChild(rtspan, NULL); Inkscape::GC::release(rtspan); /* Create TEXT */ - Inkscape::XML::Node *rstring = sp_repr_new_text(""); + Inkscape::XML::Node *rstring = xml_doc->createTextNode(""); rtspan->addChild(rstring, NULL); Inkscape::GC::release(rstring); SPItem *text_item = SP_ITEM(ec->desktop->currentLayer()->appendChildRepr(rtext)); @@ -498,7 +504,8 @@ sp_text_context_setup_text(SPTextContext *tc) Inkscape::GC::release(rtext); text_item->transform = SP_ITEM(ec->desktop->currentRoot())->getRelativeTransform(ec->desktop->currentLayer()); text_item->updateRepr(); - sp_document_done(sp_desktop_document(ec->desktop)); + sp_document_done(sp_desktop_document(ec->desktop), SP_VERB_CONTEXT_TEXT, + _("Create text")); } /** @@ -536,7 +543,8 @@ insert_uni_char(SPTextContext *const tc) tc->text_sel_start = tc->text_sel_end = sp_te_replace(tc->text, tc->text_sel_start, tc->text_sel_end, u); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); - sp_document_done(sp_desktop_document(tc->desktop)); + sp_document_done(sp_desktop_document(tc->desktop), SP_VERB_DIALOG_TRANSFORM, + _("Insert Unicode character")); } } @@ -571,39 +579,37 @@ show_curr_uni_char(SPTextContext *const tc) } } tc->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, - _("Unicode: %s: %s"), tc->uni, utf8); + _("Unicode (Enter to finish): %s: %s"), tc->uni, utf8); } else { - tc->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("Unicode: ")); + tc->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("Unicode (Enter to finish): ")); } } static gint -sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) +sp_text_context_root_handler(SPEventContext *const event_context, GdkEvent *const event) { - SPTextContext *const tc = SP_TEXT_CONTEXT(ec); + SPTextContext *const tc = SP_TEXT_CONTEXT(event_context); - SPDesktop *desktop = ec->desktop; + SPDesktop *desktop = event_context->desktop; sp_canvas_item_hide(tc->indicator); sp_text_context_validate_cursor_iterators(tc); - ec->tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); + event_context->tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); switch (event->type) { case GDK_BUTTON_PRESS: - if (event->button.button == 1) { - - SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(ec); + if (event->button.button == 1 && !event_context->space_panning) { if (Inkscape::have_viable_layer(desktop, desktop->messageStack()) == false) { return TRUE; } // save drag origin - ec->xp = (gint) event->button.x; - ec->yp = (gint) event->button.y; - ec->within_tolerance = true; + event_context->xp = (gint) event->button.x; + event_context->yp = (gint) event->button.y; + event_context->within_tolerance = true; NR::Point const button_pt(event->button.x, event->button.y); tc->p0 = desktop->w2d(button_pt); @@ -623,23 +629,23 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) if (tc->over_text) { tc->over_text = 0; // update cursor and statusbar: we are not over a text object now - ec->cursor_shape = cursor_text_xpm; - ec->hot_x = 7; - ec->hot_y = 7; - sp_event_context_update_cursor(ec); + event_context->cursor_shape = cursor_text_xpm; + event_context->hot_x = 7; + event_context->hot_y = 7; + sp_event_context_update_cursor(event_context); desktop->event_context->defaultMessageContext()->clear(); } - if (tc->creating && event->motion.state & GDK_BUTTON1_MASK) { - if ( ec->within_tolerance - && ( abs( (gint) event->motion.x - ec->xp ) < ec->tolerance ) - && ( abs( (gint) event->motion.y - ec->yp ) < ec->tolerance ) ) { + if (tc->creating && event->motion.state & GDK_BUTTON1_MASK && !event_context->space_panning) { + if ( event_context->within_tolerance + && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance ) + && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) { break; // do not drag if we're within tolerance from origin } // Once the user has moved farther than tolerance from the original location // (indicating they intend to draw, not click), then always process the // motion notify coordinates as given (no snapping back to origin) - ec->within_tolerance = false; + event_context->within_tolerance = false; NR::Point const motion_pt(event->motion.x, event->motion.y); NR::Point const p = desktop->w2d(motion_pt); @@ -650,14 +656,14 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) // status text GString *xs = SP_PX_TO_METRIC_STRING(fabs((p - tc->p0)[NR::X]), desktop->namedview->getDefaultMetric()); GString *ys = SP_PX_TO_METRIC_STRING(fabs((p - tc->p0)[NR::Y]), desktop->namedview->getDefaultMetric()); - ec->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Flowed text frame: %s × %s"), xs->str, ys->str); + event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Flowed text frame: %s × %s"), xs->str, ys->str); g_string_free(xs, FALSE); g_string_free(ys, FALSE); } break; case GDK_BUTTON_RELEASE: - if (event->button.button == 1) { + if (event->button.button == 1 && !event_context->space_panning) { if (tc->grabbed) { sp_canvas_item_ungrab(tc->grabbed, GDK_CURRENT_TIME); @@ -666,11 +672,11 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) Inkscape::Rubberband::get()->stop(); - if (tc->creating && ec->within_tolerance) { + if (tc->creating && event_context->within_tolerance) { /* Button 1, set X & Y & new item */ sp_desktop_selection(desktop)->clear(); - NR::Point dtp = ec->desktop->w2d(NR::Point(event->button.x, event->button.y)); - tc->pdoc = sp_desktop_dt2root_xy_point(ec->desktop, dtp); + NR::Point dtp = desktop->w2d(NR::Point(event->button.x, event->button.y)); + tc->pdoc = sp_desktop_dt2root_xy_point(desktop, dtp); tc->show = TRUE; tc->phase = 1; @@ -680,23 +686,26 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) 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: - double cursor_height = sp_desktop_get_font_size_tool(ec->desktop); + double cursor_height = sp_desktop_get_font_size_tool(desktop); sp_ctrlline_set_coords(SP_CTRLLINE(tc->cursor), dtp, dtp + NR::Point(0, cursor_height)); - ec->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Type text; Enter to start new line.")); // FIXME:: this is a copy of a string from _update_cursor below, do not desync + event_context->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Type text; Enter to start new line.")); // FIXME:: this is a copy of a string from _update_cursor below, do not desync - ec->within_tolerance = false; + event_context->within_tolerance = false; } else if (tc->creating) { NR::Point const button_pt(event->button.x, event->button.y); NR::Point p1 = desktop->w2d(button_pt); - double cursor_height = sp_desktop_get_font_size_tool(ec->desktop); + double cursor_height = sp_desktop_get_font_size_tool(desktop); if (fabs(p1[NR::Y] - tc->p0[NR::Y]) > cursor_height) { // otherwise even one line won't fit; most probably a slip of hand (even if bigger than tolerance) SPItem *ft = create_flowtext_with_internal_frame (desktop, tc->p0, p1); + /* Set style */ + sp_desktop_apply_style_tool(desktop, SP_OBJECT_REPR(ft), "tools.text", true); sp_desktop_selection(desktop)->set(ft); - ec->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Flowed text is created.")); - sp_document_done(sp_desktop_document(desktop)); + desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Flowed text is created.")); + sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, + _("Create flowed text")); } else { - ec->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("The frame is too small for the current font size. Flowed text not created.")); + desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("The frame is too small for the current font size. Flowed text not created.")); } } tc->creating = false; @@ -756,7 +765,7 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) } /* Exit unimode. */ tc->unimode = false; - ec->defaultMessageContext()->clear(); + event_context->defaultMessageContext()->clear(); return TRUE; } @@ -764,7 +773,7 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) // Cancel unimode. tc->unimode = false; gtk_im_context_reset(tc->imc); - ec->defaultMessageContext()->clear(); + event_context->defaultMessageContext()->clear(); return TRUE; } @@ -805,6 +814,13 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) /* Neither unimode nor IM consumed key; process text tool shortcuts */ switch (group0_keyval) { + case GDK_x: + case GDK_X: + if (MOD__ALT_ONLY) { + desktop->setToolboxFocusTo ("altx-text"); + return TRUE; + } + break; case GDK_space: if (MOD__CTRL_ONLY) { /* No-break space */ @@ -815,21 +831,22 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) tc->text_sel_start = tc->text_sel_end = sp_te_replace(tc->text, tc->text_sel_start, tc->text_sel_end, "\302\240"); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); - ec->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("No-break space")); - sp_document_done(sp_desktop_document(ec->desktop)); + desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("No-break space")); + sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, + _("Insert no-break space")); return TRUE; } break; case GDK_U: case GDK_u: - if (MOD__CTRL_ONLY) { + if (MOD__CTRL_ONLY || (MOD__CTRL && MOD__SHIFT)) { if (tc->unimode) { tc->unimode = false; - ec->defaultMessageContext()->clear(); + event_context->defaultMessageContext()->clear(); } else { tc->unimode = true; tc->unipos = 0; - ec->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("Unicode: ")); + event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("Unicode (Enter to finish): ")); } if (tc->imc) { gtk_im_context_reset(tc->imc); @@ -852,7 +869,8 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) sp_repr_css_set_property(css, "font-weight", "normal"); 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(ec->desktop)); + sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, + _("Make bold")); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); return TRUE; @@ -869,7 +887,8 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) sp_repr_css_set_property(css, "font-style", "normal"); 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(ec->desktop)); + sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, + _("Make italic")); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); return TRUE; @@ -892,35 +911,88 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) case GDK_Return: case GDK_KP_Enter: + { if (!tc->text) { // printable key; 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 } - tc->text_sel_start = tc->text_sel_end = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end); + + iterator_pair enter_pair; + bool success = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end, enter_pair); + (void)success; // TODO cleanup + tc->text_sel_start = tc->text_sel_end = enter_pair.first; + 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); - sp_document_done(sp_desktop_document(ec->desktop)); + sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, + _("New line")); 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) + + bool noSelection = false; + + 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); + noSelection = true; + } + + iterator_pair bspace_pair; + bool success = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end, bspace_pair); + + if (noSelection) { + if (success) { + tc->text_sel_start = tc->text_sel_end = bspace_pair.first; + } else { // nothing deleted + tc->text_sel_start = tc->text_sel_end = bspace_pair.second; + } + } else { + if (success) { + tc->text_sel_start = tc->text_sel_end = bspace_pair.first; + } else { // nothing deleted + tc->text_sel_start = bspace_pair.first; + tc->text_sel_end = bspace_pair.second; + } + } + sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); - sp_document_done(sp_desktop_document(ec->desktop)); + sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, + _("Backspace")); } return TRUE; case GDK_Delete: case GDK_KP_Delete: if (tc->text) { - if (tc->text_sel_start == tc->text_sel_end) + bool noSelection = false; + + 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); + noSelection = true; + } + + iterator_pair del_pair; + bool success = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end, del_pair); + + if (noSelection) { + tc->text_sel_start = tc->text_sel_end = del_pair.first; + } else { + if (success) { + tc->text_sel_start = tc->text_sel_end = del_pair.first; + } else { // nothing deleted + tc->text_sel_start = del_pair.first; + tc->text_sel_end = del_pair.second; + } + } + + sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); - sp_document_done(sp_desktop_document(ec->desktop)); + sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, + _("Delete")); } return TRUE; case GDK_Left: @@ -928,13 +1000,16 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) case GDK_KP_4: if (tc->text) { if (MOD__ALT) { + gint mul = 1 + gobble_key_events( + get_group0_keyval(&event->key), 0); // with any mask if (MOD__SHIFT) - sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, NR::Point(-10, 0)); + sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, NR::Point(mul*-10, 0)); else - sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, NR::Point(-1, 0)); + sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, NR::Point(mul*-1, 0)); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); - sp_document_maybe_done(sp_desktop_document(ec->desktop), "kern:left"); + sp_document_maybe_done(sp_desktop_document(desktop), "kern:left", SP_VERB_CONTEXT_TEXT, + _("Kern to the left")); } else { cursor_movement_operator = MOD__CTRL ? &Inkscape::Text::Layout::iterator::cursorLeftWithControl : &Inkscape::Text::Layout::iterator::cursorLeft; @@ -947,13 +1022,16 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) case GDK_KP_6: if (tc->text) { if (MOD__ALT) { + gint mul = 1 + gobble_key_events( + get_group0_keyval(&event->key), 0); // with any mask if (MOD__SHIFT) - sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, NR::Point(10, 0)); + sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, NR::Point(mul*10, 0)); else - sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, NR::Point(1, 0)); + sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, NR::Point(mul*1, 0)); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); - sp_document_maybe_done(sp_desktop_document(ec->desktop), "kern:right"); + sp_document_maybe_done(sp_desktop_document(desktop), "kern:right", SP_VERB_CONTEXT_TEXT, + _("Kern to the right")); } else { cursor_movement_operator = MOD__CTRL ? &Inkscape::Text::Layout::iterator::cursorRightWithControl : &Inkscape::Text::Layout::iterator::cursorRight; @@ -966,13 +1044,17 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) case GDK_KP_8: if (tc->text) { if (MOD__ALT) { + gint mul = 1 + gobble_key_events( + get_group0_keyval(&event->key), 0); // with any mask if (MOD__SHIFT) - sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, NR::Point(0, -10)); + sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, NR::Point(0, mul*-10)); else - sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, NR::Point(0, -1)); + sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, NR::Point(0, mul*-1)); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); - sp_document_maybe_done(sp_desktop_document(ec->desktop), "kern:up"); + sp_document_maybe_done(sp_desktop_document(desktop), "kern:up", SP_VERB_CONTEXT_TEXT, + _("Kern up")); + } else { cursor_movement_operator = MOD__CTRL ? &Inkscape::Text::Layout::iterator::cursorUpWithControl : &Inkscape::Text::Layout::iterator::cursorUp; @@ -985,13 +1067,17 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) case GDK_KP_2: if (tc->text) { if (MOD__ALT) { + gint mul = 1 + gobble_key_events( + get_group0_keyval(&event->key), 0); // with any mask if (MOD__SHIFT) - sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, NR::Point(0, 10)); + sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, NR::Point(0, mul*10)); else - sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, NR::Point(0, 1)); + sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, NR::Point(0, mul*1)); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); - sp_document_maybe_done(sp_desktop_document(ec->desktop), "kern:down"); + sp_document_maybe_done(sp_desktop_document(desktop), "kern:down", SP_VERB_CONTEXT_TEXT, + _("Kern down")); + } else { cursor_movement_operator = MOD__CTRL ? &Inkscape::Text::Layout::iterator::cursorDownWithControl : &Inkscape::Text::Layout::iterator::cursorDown; @@ -1028,7 +1114,7 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) } Inkscape::Rubberband::get()->stop(); } else { - sp_desktop_selection(ec->desktop)->clear(); + sp_desktop_selection(desktop)->clear(); } tc->nascent_object = FALSE; return TRUE; @@ -1038,14 +1124,15 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) if (MOD__ALT) { if (MOD__SHIFT) { // FIXME: alt+shift+[] does not work, don't know why - sp_te_adjust_rotation_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, -10); + sp_te_adjust_rotation_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, -10); } else { - sp_te_adjust_rotation_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, -1); + sp_te_adjust_rotation_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, -1); } } else { - sp_te_adjust_rotation(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, -90); + sp_te_adjust_rotation(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, -90); } - sp_document_maybe_done(sp_desktop_document(ec->desktop), "textrot:ccw"); + sp_document_maybe_done(sp_desktop_document(desktop), "textrot:ccw", SP_VERB_CONTEXT_TEXT, + _("Rotate counterclockwise")); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); return TRUE; @@ -1058,14 +1145,15 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) if (MOD__ALT) { if (MOD__SHIFT) { // FIXME: alt+shift+[] does not work, don't know why - sp_te_adjust_rotation_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, 10); + sp_te_adjust_rotation_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, 10); } else { - sp_te_adjust_rotation_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, 1); + sp_te_adjust_rotation_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, 1); } } else { - sp_te_adjust_rotation(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, 90); + sp_te_adjust_rotation(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, 90); } - sp_document_maybe_done(sp_desktop_document(ec->desktop), "textrot:cw"); + sp_document_maybe_done(sp_desktop_document(desktop), "textrot:cw", SP_VERB_CONTEXT_TEXT, + _("Rotate clockwise")); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); return TRUE; @@ -1078,16 +1166,20 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) if (MOD__ALT) { if (MOD__CTRL) { if (MOD__SHIFT) - sp_te_adjust_linespacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, -10); + sp_te_adjust_linespacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, -10); else - sp_te_adjust_linespacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, -1); - sp_document_maybe_done(sp_desktop_document(ec->desktop), "linespacing:dec"); + sp_te_adjust_linespacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, -1); + sp_document_maybe_done(sp_desktop_document(desktop), "linespacing:dec", SP_VERB_CONTEXT_TEXT, + _("Contract line spacing")); + } else { if (MOD__SHIFT) - sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, -10); + sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, -10); else - sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, -1); - sp_document_maybe_done(sp_desktop_document(ec->desktop), "letterspacing:dec"); + sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, -1); + sp_document_maybe_done(sp_desktop_document(desktop), "letterspacing:dec", SP_VERB_CONTEXT_TEXT, + _("Contract letter spacing")); + } sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); @@ -1101,16 +1193,20 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) if (MOD__ALT) { if (MOD__CTRL) { if (MOD__SHIFT) - sp_te_adjust_linespacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, 10); + sp_te_adjust_linespacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, 10); else - sp_te_adjust_linespacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, 1); - sp_document_maybe_done(sp_desktop_document(ec->desktop), "linespacing:inc"); + sp_te_adjust_linespacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, 1); + sp_document_maybe_done(sp_desktop_document(desktop), "linespacing:inc", SP_VERB_CONTEXT_TEXT, + _("Expand line spacing")); + } else { if (MOD__SHIFT) - sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, 10); + sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, 10); else - sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, ec->desktop, 1); - sp_document_maybe_done(sp_desktop_document(ec->desktop), "letterspacing:inc"); + sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, 1); + sp_document_maybe_done(sp_desktop_document(desktop), "letterspacing:inc", SP_VERB_CONTEXT_TEXT, + _("Expand letter spacing")); + } sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); @@ -1169,7 +1265,7 @@ sp_text_context_root_handler(SPEventContext *const ec, GdkEvent *const event) // if nobody consumed it so far if (((SPEventContextClass *) parent_class)->root_handler) { // and there's a handler in parent context, - return ((SPEventContextClass *) parent_class)->root_handler(ec, event); // send event to parent + return ((SPEventContextClass *) parent_class)->root_handler(event_context, event); // send event to parent } else { return FALSE; // return "I did nothing" value so that global shortcuts can be activated } @@ -1212,7 +1308,8 @@ sp_text_paste_inline(SPEventContext *ec) tc->text_sel_start = tc->text_sel_end = sp_te_insert_line(tc->text, tc->text_sel_start); begin = end + 1; } - sp_document_done(sp_desktop_document(ec->desktop)); + sp_document_done(sp_desktop_document(ec->desktop), SP_VERB_CONTEXT_TEXT, + _("Paste text")); return true; } @@ -1251,9 +1348,21 @@ 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); + + iterator_pair pair; + bool success = sp_te_delete(tc->text, tc->text_sel_start, tc->text_sel_end, pair); + + + if (success) { + tc->text_sel_start = tc->text_sel_end = pair.first; + } else { // nothing deleted + tc->text_sel_start = pair.first; + tc->text_sel_end = pair.second; + } + sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); + return true; } @@ -1268,7 +1377,7 @@ sp_text_context_selection_changed(Inkscape::Selection *selection, SPTextContext SPEventContext *ec = SP_EVENT_CONTEXT(tc); if (ec->shape_knot_holder) { // destroy knotholder - sp_knot_holder_destroy(ec->shape_knot_holder); + delete ec->shape_knot_holder; ec->shape_knot_holder = NULL; } @@ -1310,7 +1419,7 @@ sp_text_context_selection_changed(Inkscape::Selection *selection, SPTextContext } static void -sp_text_context_selection_modified(Inkscape::Selection *selection, guint flags, SPTextContext *tc) +sp_text_context_selection_modified(Inkscape::Selection */*selection*/, guint /*flags*/, SPTextContext *tc) { sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); @@ -1325,7 +1434,8 @@ sp_text_context_style_set(SPCSSAttr const *css, SPTextContext *tc) return false; // will get picked up by the parent and applied to the whole text object sp_te_apply_style(tc->text, tc->text_sel_start, tc->text_sel_end, css); - sp_document_done(sp_desktop_document(tc->desktop)); + sp_document_done(sp_desktop_document(tc->desktop), SP_VERB_CONTEXT_TEXT, + _("Set text style")); sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); @@ -1359,10 +1469,12 @@ sp_text_context_style_query(SPStyle *style, int property, SPTextContext *tc) SPObject const *pos_obj = 0; void *rawptr = 0; layout->getSourceOfCharacter(it, &rawptr); + if (!rawptr || !SP_IS_OBJECT(rawptr)) + continue; pos_obj = SP_OBJECT(rawptr); - if (pos_obj == 0) continue; - while (SP_OBJECT_STYLE(pos_obj) == NULL && SP_OBJECT_PARENT(pos_obj)) - pos_obj = SP_OBJECT_PARENT(pos_obj); // SPStrings don't have style + while (SP_IS_STRING(pos_obj) && SP_OBJECT_PARENT(pos_obj)) { + pos_obj = SP_OBJECT_PARENT(pos_obj); // SPStrings don't have style + } styles_list = g_slist_prepend(styles_list, (gpointer)pos_obj); } @@ -1389,11 +1501,15 @@ sp_text_context_update_cursor(SPTextContext *tc, bool scroll_to_see) { GdkRectangle im_cursor = { 0, 0, 1, 1 }; + // due to interruptible display, tc may already be destroyed during a display update before + // the cursor update (can't do both atomically, alas) + if (!tc->desktop) return; + if (tc->text) { NR::Point p0, p1; sp_te_get_cursor_coords(tc->text, tc->text_sel_end, p0, p1); - NR::Point const d0 = p0 * sp_item_i2d_affine(SP_ITEM(tc->text)); - NR::Point const d1 = p1 * sp_item_i2d_affine(SP_ITEM(tc->text)); + NR::Point const d0 = p0 * from_2geom(sp_item_i2d_affine(SP_ITEM(tc->text))); + NR::Point const d1 = p1 * from_2geom(sp_item_i2d_affine(SP_ITEM(tc->text))); // scroll to show cursor if (scroll_to_see) { @@ -1418,7 +1534,10 @@ sp_text_context_update_cursor(SPTextContext *tc, bool scroll_to_see) SPItem *frame = SP_FLOWTEXT(tc->text)->get_frame (NULL); // first frame only if (frame) { sp_canvas_item_show(tc->frame); - SP_CTRLRECT(tc->frame)->setRectangle(sp_item_bbox_desktop(frame)); + NR::Maybe 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; Enter to start new paragraph.")); } else { @@ -1442,6 +1561,10 @@ sp_text_context_update_cursor(SPTextContext *tc, bool scroll_to_see) static void sp_text_context_update_text_selection(SPTextContext *tc) { + // due to interruptible display, tc may already be destroyed during a display update before + // the selection update (can't do both atomically, alas) + if (!tc->desktop) return; + for (std::vector::iterator it = tc->text_selection_quads.begin() ; it != tc->text_selection_quads.end() ; it++) { sp_canvas_item_hide(*it); gtk_object_destroy(*it); @@ -1450,11 +1573,13 @@ static void sp_text_context_update_text_selection(SPTextContext *tc) std::vector quads; if (tc->text != NULL) - quads = sp_te_create_selection_quads(tc->text, tc->text_sel_start, tc->text_sel_end, sp_item_i2d_affine(tc->text)); + quads = sp_te_create_selection_quads(tc->text, tc->text_sel_start, tc->text_sel_end, from_2geom(sp_item_i2d_affine(tc->text))); for (unsigned i = 0 ; i < quads.size() ; i += 4) { SPCanvasItem *quad_canvasitem; quad_canvasitem = sp_canvas_item_new(sp_desktop_controls(tc->desktop), SP_TYPE_CTRLQUADR, NULL); - sp_ctrlquadr_set_rgba32(SP_CTRLQUADR(quad_canvasitem), 0x000000ff); + // FIXME: make the color settable in prefs + // for now, use semitrasparent blue, as cairo cannot do inversion :( + sp_ctrlquadr_set_rgba32(SP_CTRLQUADR(quad_canvasitem), 0x00777777); sp_ctrlquadr_set_coords(SP_CTRLQUADR(quad_canvasitem), quads[i], quads[i+1], quads[i+2], quads[i+3]); sp_canvas_item_show(quad_canvasitem); tc->text_selection_quads.push_back(quad_canvasitem); @@ -1482,9 +1607,15 @@ sp_text_context_forget_text(SPTextContext *tc) { if (! tc->text) return; SPItem *ti = tc->text; + (void)ti; /* We have to set it to zero, * or selection changed signal messes everything up */ tc->text = NULL; + +/* FIXME: this automatic deletion when nothing is inputted crashes the XML edittor and also crashes when duplicating an empty flowtext. + So don't create an empty flowtext in the first place? Create it when first character is typed. + */ +/* if ((SP_IS_TEXT(ti) || SP_IS_FLOWTEXT(ti)) && sp_te_input_is_empty(ti)) { Inkscape::XML::Node *text_repr=SP_OBJECT_REPR(ti); // the repr may already have been unparented @@ -1493,26 +1624,29 @@ sp_text_context_forget_text(SPTextContext *tc) // the XML editor if ( text_repr && sp_repr_parent(text_repr) ) { sp_repr_unparent(text_repr); + sp_document_done(sp_desktop_document(tc->desktop), SP_VERB_CONTEXT_TEXT, + _("Remove empty text")); } } +*/ } gint -sptc_focus_in(GtkWidget *widget, GdkEventFocus *event, SPTextContext *tc) +sptc_focus_in(GtkWidget */*widget*/, GdkEventFocus */*event*/, SPTextContext *tc) { gtk_im_context_focus_in(tc->imc); return FALSE; } gint -sptc_focus_out(GtkWidget *widget, GdkEventFocus *event, SPTextContext *tc) +sptc_focus_out(GtkWidget */*widget*/, GdkEventFocus */*event*/, SPTextContext *tc) { gtk_im_context_focus_out(tc->imc); return FALSE; } static void -sptc_commit(GtkIMContext *imc, gchar *string, SPTextContext *tc) +sptc_commit(GtkIMContext */*imc*/, gchar *string, SPTextContext *tc) { if (!tc->text) { sp_text_context_setup_text(tc); @@ -1523,7 +1657,8 @@ sptc_commit(GtkIMContext *imc, gchar *string, SPTextContext *tc) sp_text_context_update_cursor(tc); sp_text_context_update_text_selection(tc); - sp_document_done(SP_OBJECT_DOCUMENT(tc->text)); + sp_document_done(SP_OBJECT_DOCUMENT(tc->text), SP_VERB_CONTEXT_TEXT, + _("Type text")); }