View Issue Details

IDProjectCategoryView StatusLast Update
0016159ScribusScripterpublic2020-08-11 20:56
Reporterale Assigned Toale  
PrioritynormalSeverityminorReproducibilityhave not tried
Status closedResolutionfixed 
Product Version 
Target VersionFixed in Version1.5.6.svn 
Summary0016159: Add selectFrameText() to scripter
Descriptionif you select the second frame in a chain, selectText's start value will start from the number of characters in the first frame in the chain

scribus.deselectAll()
scribus.selectObject('Text1')
content = scribus.getFrameText()
start_of_2 = len(content)
print(start_of_2)
scribus.deselectAll()
scribus.selectObject('Text2')
scribus.selectText(0, 5) # <- no output
print(scribus.getFrameText())
scribus.selectText(start_of_2, 5) # <- first word in Text2
print(scribus.getFrameText())
TagsNo tags attached.
PatchYes

Activities

ale

2020-07-09 07:35

manager  

select-text-chained-frames.sla (15,786 bytes)

ale

2020-07-09 07:38

manager   ~0047806

i'm working on a patch...

jghali

2020-07-09 09:38

administrator   ~0047807

Last edited: 2020-07-09 09:39

View 2 revisions

Well what you mentioned is expected... If you work on the text of a text chain, the fact is also that you need only to select the first frame of the chain. Given the restrictions on text layout updates while a script is running, that makes working on text easier... and faster. Data related to the text selection is now also shared across the frames of the chain, that will be needed in the future to provide text selection across text frames. Imo what should be changed is the documention related to selectText() so as to make behavior in the case of text chains clearer.

ale

2020-07-09 09:43

manager   ~0047808

hi jean

i could not really follow your explanation but the "simple" patch that fixes this behavior is really simple.

but, of course, if someone has modified the text she will need to update the flow before selecting (this now mentioned in the scripter documentation for (at least) other commands)

there is an issue related to selecting the last character in the chain. i will upload a script with a hack that solves this (but does not seem to affect other cases).
there will also be a simple test file and a test script.

jghali

2020-07-09 10:23

administrator   ~0047809

>> but, of course, if someone has modified the text she will need to update the flow before selecting

That's not the case in fact. As I explained, the text selection data is now shared across the text chain. You can select text as any position of the chain while the same text frame is selected. So you can format a whole text chain according to your desire and update layout only at the end.

ale

2020-07-09 14:53

manager   ~0047810

i attach a patch that adds a selectFrameText() which is a companion to getFrameText().

this patch is necessary to be able to implement a python based table of contents within scribus (and it might enable a few other cool features).

here is a set of tests against the new code:

# get the first 4 chars of a frame
scribus.deselectAll()
scribus.selectObject('Text1')
scribus.selectFrameText(0, 4)
print(scribus.getFrameText() == 'This')
# get all chars starting from 5 in a chained frame
scribus.selectFrameText(5, -1)
print(scribus.getFrameText() == 'is some text with no style in the first frame.')

# select in an empty frame
scribus.deselectAll()
scribus.selectObject('Text4')
scribus.selectFrameText(0, 0)
print(scribus.getFrameText() == '')

# select in a chained empty frame
scribus.deselectAll()
scribus.selectObject('Text6')
scribus.selectFrameText(0, 0)
print(scribus.getFrameText() == '')

# get all chars starting from 5 in an unchained frame
scribus.deselectAll()
scribus.selectObject('Text3')
scribus.selectFrameText(5, -1)
print(scribus.getFrameText() == 'is some text.')
scribus.selectFrameText(0, 0)
# remove the selection
print(len(scribus.getFrameText()) == 18)
scribus.selectFrameText(5, 13)

# push the count to its limit
print(scribus.getFrameText() == 'is some text.')
# set count 1 over the limit
try:
    scribus.selectFrameText(5, 14)
    print('don\'t print this', scribus.getFrameText())
except IndexError:
    print(True)
# select the first 5 chars in the second frame in a chain
scribus.deselectAll()
scribus.selectObject('Text2')
scribus.selectFrameText(0, 5)
print(scribus.getFrameText() == 'which')
# select all chars after the first 6 in the second frame in a chain
scribus.selectFrameText(6, -1)
print(scribus.getFrameText() == 'has the style abc and is in the chained frame.')
# select with. a way too big count
try:
    scribus.selectFrameText(1000, 5)
    print('don\'t print this', scribus.getFrameText())
except IndexError:
    print(True)

they test a few corner cases in the attached .sla file.

select-text-chained-frames-2.sla (18,929 bytes)
select-text.diff (5,230 bytes)
diff --git a/scribus/plugins/scriptplugin/cmdtext.cpp b/scribus/plugins/scriptplugin/cmdtext.cpp
index 163f19ae0..22a55d37e 100644
--- a/scribus/plugins/scriptplugin/cmdtext.cpp
+++ b/scribus/plugins/scriptplugin/cmdtext.cpp
@@ -872,6 +872,55 @@ PyObject *scribus_settextverticalalignment(PyObject* /* self */, PyObject* args)
 	Py_RETURN_NONE;
 }
 
+PyObject *scribus_selectframetext(PyObject* /* self */, PyObject* args)
+{
+	char *Name = const_cast<char*>("");
+	int start, selcount;
+	if (!PyArg_ParseTuple(args, "ii|es", &start, &selcount, "utf-8", &Name))
+		return nullptr;
+	if (!checkHaveDocument())
+		return nullptr;
+	PageItem *item = GetUniqueItem(QString::fromUtf8(Name));
+	if (item == nullptr)
+		return nullptr;
+
+	if (!(item->isTextFrame()) && !(item->isPathText()))
+	{
+		PyErr_SetString(WrongFrameTypeError, QObject::tr("Cannot select text in a non-text frame", "python error").toLocal8Bit().constData());
+		return nullptr;
+	}
+	if (selcount < -1)
+	{
+		PyErr_SetString(PyExc_IndexError, QObject::tr("Count must be positive, 0 or -1", "python error").toLocal8Bit().constData());
+		return nullptr;
+	}
+
+	if (start < 0 || (selcount > 0 && (item->lastInFrame() == -1 || selcount > item->lastInFrame() - item->firstInFrame() + 1 - start)))
+	{
+		PyErr_SetString(PyExc_IndexError, QObject::tr("Selection index out of bounds", "python error").toLocal8Bit().constData());
+		return nullptr;
+	}
+	start += item->firstInFrame();
+	if (selcount == -1)
+	{
+		selcount = item->lastInFrame() - start;
+		// in the last in chain, the last char does not get selected -- a.l.e
+		 if (item->nextInChain() == nullptr) {
+			 selcount++;
+		 }
+	}
+	item->itemText.deselectAll();
+	if (selcount == 0)
+	{
+		item->HasSel = false;
+		Py_RETURN_NONE;
+	}
+	item->itemText.select(start, selcount, true);
+	item->HasSel = true;
+
+	Py_RETURN_NONE;
+}
+
 PyObject *scribus_selecttext(PyObject* /* self */, PyObject* args)
 {
 	char *Name = const_cast<char*>("");
@@ -1391,6 +1440,7 @@ void cmdtextdocwarnings()
 	  << scribus_layouttextchain__doc__
 	  << scribus_linktextframes__doc__
 	  << scribus_outlinetext__doc__
+	  << scribus_selectframetext__doc__
 	  << scribus_selecttext__doc__
 	  << scribus_setalign__doc__
 	  << scribus_setcolumngap__doc__
diff --git a/scribus/plugins/scriptplugin/cmdtext.h b/scribus/plugins/scriptplugin/cmdtext.h
index bad04bbad..034aa126f 100644
--- a/scribus/plugins/scriptplugin/cmdtext.h
+++ b/scribus/plugins/scriptplugin/cmdtext.h
@@ -387,11 +387,32 @@ May throw ValueError for an invalid direction constant.\n\
 /*! Set direction */
 PyObject *scribus_setdirection(PyObject * /*self*/, PyObject* args);
 
+/*! docstring */
+PyDoc_STRVAR(scribus_selectframetext__doc__,
+QT_TR_NOOP("selectFrameText(start, count, [\"name\"])\n\
+\n\
+Selects \"count\" characters of text in the text frame \"name\" starting from the\n\
+character \"start\". Character counting starts at 0.\n\
+If \"count\" is zero, any text selection will be cleared.\n\
+If \"count\" is -1, the selection will extend to the end of the frame.\n\
+If \"name\" is not given the currently selected item is used.\n\
+\n\
+This function only acts on the text visible in the specified frame. If you need to \n\
+work on the text contained in a text chain, use selectText() instead.\n\
+As this function depends on text layout being up-to-date, you may need to call \n\
+layoutText() or layoutTextChain() before calling this function in order to get \n\
+expected result.\n\
+\n\
+May throw IndexError if the selection is outside the bounds of the text.\n\
+"));
+/*! Select frame text */
+PyObject *scribus_selectframetext(PyObject * /*self*/, PyObject* args);
+
 /*! docstring */
 PyDoc_STRVAR(scribus_selecttext__doc__,
 QT_TR_NOOP("selectText(start, count, [\"name\"])\n\
 \n\
-Selects \"count\" characters of text in the text frame \"name\" starting from the\n\
+Selects \"count\" characters of text in the story of the text frame \"name\" starting from the\n\
 character \"start\". Character counting starts at 0. If \"count\" is zero, any\n\
 text selection will be cleared.  If \"name\" is not given the currently\n\
 selected item is used.\n\
diff --git a/scribus/plugins/scriptplugin/scriptplugin.cpp b/scribus/plugins/scriptplugin/scriptplugin.cpp
index a9b5497d6..aab82fa3b 100644
--- a/scribus/plugins/scriptplugin/scriptplugin.cpp
+++ b/scribus/plugins/scriptplugin/scriptplugin.cpp
@@ -484,6 +484,7 @@ PyMethodDef scribus_methods[] = {
 	{const_cast<char*>("setImageOffset"), scribus_setimageoffset, METH_VARARGS, tr(scribus_setimageoffset__doc__)},
 	{const_cast<char*>("selectionCount"), (PyCFunction)scribus_selcount, METH_NOARGS, tr(scribus_selcount__doc__)},
 	{const_cast<char*>("selectObject"), scribus_selectobj, METH_VARARGS, tr(scribus_selectobj__doc__)},
+	{const_cast<char*>("selectFrameText"), scribus_selectframetext, METH_VARARGS, tr(scribus_selectframetext__doc__)},
 	{const_cast<char*>("selectText"), scribus_selecttext, METH_VARARGS, tr(scribus_selecttext__doc__)},
 	{const_cast<char*>("sentToLayer"), scribus_senttolayer, METH_VARARGS, tr(scribus_senttolayer__doc__)},
 	{const_cast<char*>("setActiveLayer"), scribus_setactivelayer, METH_VARARGS, tr(scribus_setactivelayer__doc__)},
select-text.diff (5,230 bytes)

ale

2020-07-16 13:57

manager   ~0047840

thanks!

Issue History

Date Modified Username Field Change
2020-07-09 07:35 ale New Issue
2020-07-09 07:35 ale File Added: select-text-chained-frames.sla
2020-07-09 07:38 ale Note Added: 0047806
2020-07-09 09:38 jghali Note Added: 0047807
2020-07-09 09:39 jghali Note Edited: 0047807 View Revisions
2020-07-09 09:43 ale Note Added: 0047808
2020-07-09 10:23 jghali Note Added: 0047809
2020-07-09 14:53 ale File Added: select-text-chained-frames-2.sla
2020-07-09 14:53 ale File Added: select-text.diff
2020-07-09 14:53 ale Note Added: 0047810
2020-07-09 16:12 ale Summary selectText() start value is not 0 for chained text frames => [PATCH] selectText() start value is not 0 for chained text frames
2020-07-09 16:12 ale Patch No => Yes
2020-07-16 09:37 jghali Summary [PATCH] selectText() start value is not 0 for chained text frames => Add selectFrameText() to scripter
2020-07-16 09:42 jghali Assigned To => ale
2020-07-16 09:42 jghali Status new => resolved
2020-07-16 09:42 jghali Resolution open => fixed
2020-07-16 09:42 jghali Fixed in Version => 1.5.6.svn
2020-07-16 13:57 ale Note Added: 0047840
2020-08-11 20:56 cbradney Status resolved => closed