text-context.cpp revision 283b476d68aaebafad5c687c34438dd2fe00c1ef
5325N/A#define __SP_TEXT_CONTEXT_C__
5325N/A#ifdef HAVE_CONFIG_H
5325N/A#include "sp-flowtext.h"
5325N/A#include "document.h"
5325N/A#include "sp-namedview.h"
5325N/A#include "selection.h"
5375N/A#include "desktop-style.h"
5325N/A#include "desktop-handles.h"
5325N/A#include "desktop-affine.h"
5325N/A#include "message-stack.h"
5325N/A#include "message-context.h"
5325N/A#include "pixmaps/cursor-text.xpm"
5325N/A#include "pixmaps/cursor-text-insert.xpm"
5325N/A#include "object-edit.h"
5325N/A#include "xml/node-event-vector.h"
5325N/A#include "preferences.h"
5325N/A#include "rubberband.h"
5325N/A#include "sp-metrics.h"
5325N/A#include "context-fns.h"
5325N/A#include "shape-editor.h"
5325N/A#include "selection-chemistry.h"
5325N/A#include "text-editing.h"
5325N/A#include "text-context.h"
5325N/Astatic gint sp_text_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
5325N/Astatic void sp_text_context_selection_changed(Inkscape::Selection *selection, SPTextContext *tc);
5325N/Astatic void sp_text_context_selection_modified(Inkscape::Selection *selection, guint flags, SPTextContext *tc);
5325N/A sizeof(SPTextContextClass),
5325N/A sizeof(SPTextContext),
5325N/A type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPTextContext", &info, (GTypeFlags)0);
if (timeout < 0) {
static gint
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
if (layout) {
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_MOTION_NOTIFY:
if (!layout) break;
if (ibbox) {
desktop->event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> to edit the text, <b>drag</b> to select part of the text."));
desktop->event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> to edit the flowed text, <b>drag</b> to select part of the text."));
if (!ret) {
return ret;
unsigned int uv;
&& !(g_unichar_validate(static_cast<gunichar>(uv)) && (g_unichar_type(static_cast<gunichar>(uv)) == G_UNICODE_PRIVATE_USE) ) ) {
tc->text_sel_start = tc->text_sel_end = sp_te_replace(tc->text, tc->text_sel_start, tc->text_sel_end, u);
unsigned int uv;
switch(utf8[0]) {
tc->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("Unicode (<b>Enter</b> to finish): "));
static gint
case GDK_BUTTON_PRESS:
return TRUE;
GDK_KEY_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK,
return TRUE;
case GDK_MOTION_NOTIFY:
GString *xs = SP_PX_TO_METRIC_STRING(fabs((p - tc->p0)[Geom::X]), desktop->namedview->getDefaultMetric());
GString *ys = SP_PX_TO_METRIC_STRING(fabs((p - tc->p0)[Geom::Y]), desktop->namedview->getDefaultMetric());
event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("<b>Flowed text frame</b>: %s × %s"), xs->str, ys->str);
case GDK_BUTTON_RELEASE:
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
desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("The frame is <b>too small</b> for the current font size. Flowed text not created."));
return TRUE;
case GDK_KEY_PRESS: {
switch (group0_keyval) {
case GDK_space:
case GDK_KP_Space: {
return TRUE;
case GDK_BackSpace: {
return TRUE;
case GDK_Return:
case GDK_KP_Enter: {
return TRUE;
case GDK_Escape: {
return TRUE;
case GDK_Shift_L:
case GDK_Shift_R:
return TRUE;
return TRUE;
bool cursor_moved = false;
if (screenlines <= 0)
switch (group0_keyval) {
case GDK_x:
case GDK_X:
if (MOD__ALT_ONLY) {
return TRUE;
case GDK_space:
if (MOD__CTRL_ONLY) {
tc->text_sel_start = tc->text_sel_end = sp_te_replace(tc->text, tc->text_sel_start, tc->text_sel_end, "\302\240");
return TRUE;
case GDK_U:
case GDK_u:
event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("Unicode (<b>Enter</b> to finish): "));
return TRUE;
case GDK_B:
case GDK_b:
SPStyle const *style = sp_te_style_at_position(tc->text, std::min(tc->text_sel_start, tc->text_sel_end));
return TRUE;
case GDK_I:
case GDK_i:
SPStyle const *style = sp_te_style_at_position(tc->text, std::min(tc->text_sel_start, tc->text_sel_end));
return TRUE;
case GDK_A:
case GDK_a:
if (layout) {
return TRUE;
case GDK_Return:
case GDK_KP_Enter:
return TRUE;
case GDK_BackSpace:
if (tc->text) { // if nascent_object, do nothing, but return TRUE; same for all other delete and move keys
bool noSelection = false;
noSelection = true;
if (noSelection) {
if (success) {
if (success) {
return TRUE;
case GDK_Delete:
case GDK_KP_Delete:
bool noSelection = false;
noSelection = true;
if (noSelection) {
if (success) {
return TRUE;
case GDK_Left:
case GDK_KP_Left:
case GDK_KP_4:
if (MOD__ALT) {
if (MOD__SHIFT)
sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, Geom::Point(mul*-10, 0));
sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, Geom::Point(mul*-1, 0));
if (MOD__CTRL)
cursor_moved = true;
return TRUE;
case GDK_Right:
case GDK_KP_Right:
case GDK_KP_6:
if (MOD__ALT) {
if (MOD__SHIFT)
sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, Geom::Point(mul*10, 0));
sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, Geom::Point(mul*1, 0));
if (MOD__CTRL)
cursor_moved = true;
return TRUE;
case GDK_Up:
case GDK_KP_Up:
case GDK_KP_8:
if (MOD__ALT) {
if (MOD__SHIFT)
sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, Geom::Point(0, mul*-10));
sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, Geom::Point(0, mul*-1));
if (MOD__CTRL)
cursor_moved = true;
return TRUE;
case GDK_Down:
case GDK_KP_Down:
case GDK_KP_2:
if (MOD__ALT) {
if (MOD__SHIFT)
sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, Geom::Point(0, mul*10));
sp_te_adjust_kerning_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, Geom::Point(0, mul*1));
if (MOD__CTRL)
cursor_moved = true;
return TRUE;
case GDK_Home:
case GDK_KP_Home:
if (MOD__CTRL)
cursor_moved = true;
return TRUE;
case GDK_End:
case GDK_KP_End:
if (MOD__CTRL)
cursor_moved = true;
return TRUE;
case GDK_Page_Down:
case GDK_KP_Page_Down:
cursor_moved = true;
return TRUE;
case GDK_Page_Up:
case GDK_KP_Page_Up:
cursor_moved = true;
return TRUE;
case GDK_Escape:
return TRUE;
case GDK_bracketleft:
if (MOD__ALT) {
if (MOD__SHIFT) {
return TRUE;
case GDK_bracketright:
if (MOD__ALT) {
if (MOD__SHIFT) {
return TRUE;
case GDK_less:
case GDK_comma:
if (MOD__ALT) {
if (MOD__CTRL) {
if (MOD__SHIFT)
if (MOD__SHIFT)
sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, -10);
sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, -1);
return TRUE;
case GDK_greater:
case GDK_period:
if (MOD__ALT) {
if (MOD__CTRL) {
if (MOD__SHIFT)
if (MOD__SHIFT)
sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, 10);
sp_te_adjust_tspan_letterspacing_screen(tc->text, tc->text_sel_start, tc->text_sel_end, desktop, 1);
return TRUE;
if (cursor_moved) {
if (!MOD__SHIFT)
return TRUE;
&& !MOD__CTRL_ONLY) {
return TRUE;
case GDK_KEY_RELEASE:
return TRUE;
if (((SPEventContextClass *) parent_class)->root_handler) { // and there's a handler in parent context,
return ((SPEventContextClass *) parent_class)->root_handler(event_context, event); // send event to parent
itr++;
tc->text_sel_start = tc->text_sel_end = sp_te_replace(tc->text, tc->text_sel_start, tc->text_sel_end, text.substr(begin).c_str());
tc->text_sel_start = tc->text_sel_end = sp_te_replace(tc->text, tc->text_sel_start, tc->text_sel_end, text.substr(begin, end - begin).c_str());
return NULL;
return NULL;
if (obj)
return NULL;
if (success) {
if (layout)
sp_text_context_selection_modified(Inkscape::Selection */*selection*/, guint /*flags*/, SPTextContext *tc)
return QUERY_STYLE_NOTHING;
return QUERY_STYLE_NOTHING;
void *rawptr = 0;
return result;
if (scroll_to_see) {
// unlike mouse moves, here we must scroll all the way at first shot, so we override the autoscrollspeed
if (frame) {
if (frame_bbox) {
SP_EVENT_CONTEXT(tc)->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Type or edit flowed text (%d characters); <b>Enter</b> to start new paragraph."), nChars);
SP_EVENT_CONTEXT(tc)->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Type or edit text (%d characters); <b>Enter</b> to start new line."), nChars);
SP_EVENT_CONTEXT(tc)->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> to select or create text, <b>drag</b> to create flowed text; then type.")); // FIXME: this is a copy of string from tools-switch, do not desync
for (std::vector<SPCanvasItem*>::iterator it = tc->text_selection_quads.begin() ; it != tc->text_selection_quads.end() ; it++) {
quads = sp_te_create_selection_quads(tc->text, tc->text_sel_start, tc->text_sel_end, sp_item_i2d_affine(tc->text));
sp_ctrlquadr_set_coords(SP_CTRLQUADR(quad_canvasitem), quads[i], quads[i+1], quads[i+2], quads[i+3]);
static gint
return TRUE;
(void)ti;
/* FIXME: this automatic deletion when nothing is inputted crashes the XML edittor and also crashes when duplicating an empty flowtext.
return FALSE;
return FALSE;
tc->text_sel_start = tc->text_sel_end = sp_te_replace(tc->text, tc->text_sel_start, tc->text_sel_end, string);
sp_text_context_place_cursor (SPTextContext *tc, SPObject *text, Inkscape::Text::Layout::iterator where)
Inkscape::Text::Layout::iterator *sp_text_context_get_cursor_position(SPTextContext *tc, SPObject *text)
return NULL;