View Issue Details

IDProjectCategoryView StatusLast Update
0009568ScribusUndo/Redopublic2011-03-07 19:07
Reportercezaryece Assigned Toplinnell 
PriorityhighSeverityfeatureReproducibilityN/A
Status closedResolutionfixed 
Product Version1.4.0svn 
Target Version1.4.0svnFixed in Version1.4.0svn 
Summary0009568: [FEATURE SOLVED] undow for text frames
DescriptionUnfortunately I have no time for long describe my patch for text undo.
Maybe in few days I will write more here.
patch gives undo for almost all operations on text - editing, changing styles etc.
I think inline frames and inserting special characters is problem.

Apply, test and review.
TagsNo tags attached.
Patch

Relationships

has duplicate 0009522 closedcbradney [PATCH] for disabling Undo/Redo while typing text in text frames 
has duplicate 0009665 closedplinnell Undo in Text Editing 
has duplicate 0003301 closedplinnell Undo does not work on text object 
has duplicate 0008744 closedcbradney undo disabled by default in 1.4? 
has duplicate 0003445 closedcbradney Type fill does not undo. 

Activities

subeditor

2010-12-10 22:41

reporter   ~0025058

I'd like to test, but I'm not very familiar with applying patches. If someone will give instruction, I will try.

cezaryece

2010-12-13 08:57

updater   ~0025099

to apply this patch
1. download 1.3.9 code from SVN and install all needed libs to build Scribus from source (more you can find here: http://wiki.scribus.net/index.php/Building_SVN_versions_with_CMake)
2. copy patch file into Version135 dir in your SVN work copy
3. in terminal as user run command "patch -p0 -l < textUndo.patch"

cezaryece

2010-12-13 09:26

updater   ~0025100

What this patch do?

For pageitem_textframe is definied new method "updateUndo" where main parameter is "action" with type enum EditAct.
EditAct can be:
- PARAMFULL for changing styles (font, color, typographic etc) for whole text story
- PARAMSEL for changing styles for selection
- INSSAX and DELSAX - inserting and deleting portions of text storing it with styles
- INS and DEL - inserting and deleting portions of text without styles (in fact only for pasting clear "non-Scribus" text)
During all actions on text before apply any changes edited portion (selection on whole text) is stored in itemTextSaxed field. Method getItemTextSaxed returns QString with text with styles in string format (like in Copy/Paste actions) from selection or whole text storie.
After applying changes updateUndo is invoked for create undo state, where old text, new text, action type and action place (CPos) is stored.

subeditor

2010-12-23 09:39

reporter   ~0025183

Last edited: 2010-12-23 10:48

I downloaded SVN, ran patch, the, cmake, make and got error:

/home/anton/pageitem_textframe.cpp: In member function ‘virtual void PageItem_TextFrame::handleModeEditKey(QKeyEvent*, bool&)’:
/home/anton/scribus_svn/scribus/pageitem_textframe.cpp:2749: error: ‘currItem’ was not declared in this scope
make[2]: *** [scribus/CMakeFiles/scribus.dir/pageitem_textframe.cpp.o] Error 1
make[1]: *** [scribus/CMakeFiles/scribus.dir/all] Error 2
make: *** [all] Error 2

ale

2010-12-23 21:49

manager   ~0025188

hi subeditor, afaik there will be a new patch around christmas...

subeditor

2010-12-24 09:21

reporter   ~0025191

hi ale!
so there is no way to test this patch for now?

ale

2010-12-24 13:12

manager   ~0025192

there's no need to test until cezary uploads a new version of it... the new patch will be very different (at least this is what he has announced on irc).

i'm eager to test it, too (now that i have 1.4beta installed...)

cezaryece

2010-12-25 00:26

updater   ~0025196

This time I check if path is compiling after applying on current SVN snapshot.
So I hope you can test it.

I try to make sequence of same actions in one undo step.
So now if you insert text chars it all will be stored in one undo/redo action until you insert SPACE or new line (Enter key), which made new sequence. If you change text parameters without changing target for this action it is also stored in one sequence. So if are changing font, size of text or kerning parameters continuously in undo state first state of text will be saved.
Changing cursor position breaks sequence and new undo state will created after this.
Deleting or replacing text is not sequenced (as we deleting styles in this case, not only chars). Also cutting/pasting text.

This patch is for undo for text frames, not for text on path. Somebody should expand it for text on path. I am not interesting in this issue as I am not using it at all.

I hope you will have big joy using this patch.

subeditor

2010-12-25 18:03

reporter   ~0025197

I got error again.

[ 24%] Building CXX object scribus/CMakeFiles/scribus.dir/pageitem_textframe.cpp.o
/home/anton/scribus_svn/scribus/pageitem_textframe.cpp: In member function ‘virtual void PageItem_TextFrame::handleModeEditKey(QKeyEvent*, bool&)’:
/home/anton/scribus_svn/scribus/pageitem_textframe.cpp:2749: error: ‘currItem’ was not declared in this scope
make[2]: *** [scribus/CMakeFiles/scribus.dir/pageitem_textframe.cpp.o] Error 1
make[1]: *** [scribus/CMakeFiles/scribus.dir/all] Error 2
make: *** [all] Error 2

cezaryece

2010-12-25 20:37

updater   ~0025198

subeditor: are you sure you apply ONLY undoText2.patch?
The first one (textUndo.patch) is outdated and should not be applied, but I cant delete it.

subeditor

2010-12-25 21:04

reporter   ~0025199

Well, my steps today:
1. svn co
2. applying patch
3. cmake
4. make
Though I didn't wipe folders from previous try but files should be overwritten, shouldn't they?
Ok, I'll try delete everything and start again.

cezaryece

2010-12-25 22:11

updater   ~0025200

better make clean SVN snaphot
and apply ONLY second patch!!!
you can see in this there is any currItem in pageitem_textframe.cpp

subeditor

2010-12-25 23:41

reporter   ~0025201

Yes, clean install succeed.
Great work! Text undo is what we missed a lot!
Go for testing now, though there is seems to be no real work until New Year.

ale

2010-12-29 08:52

manager   ~0025215

installed!

i had to use the "-l" switch when patching but now i finally have an undo for text in scribus!

i will let scribus run in the debugger to catch possible bugs...

cezaryece

2010-12-30 18:06

updater   ~0025222

Last edited: 2010-12-30 18:29

New version of patch with small fix is ready for download - undoText2.1.patch
Apply it for clean snapshot, not for patched with previous patch.

For you with already previous patch aplied it is one small change:
in pageitem_textframe.cpp in line 3682 change "else" into "if (newState)".

Hope this fixes crushes.

--- 8< ---
edit by a.l.e:

in my patched pageitem_textframe.cpp it's line 3664:

        }
- else
+ if (newState)
        {
            ss = new SimpleState(UndoManager::EditText);
            ss->set("STEXT_CPOS",oldCPos);
        }

cbradney

2011-01-07 20:13

administrator   ~0025305

avox..ping

cezaryece

2011-01-12 07:50

updater   ~0025348

Please... let someone delete first two patch files - they are buggy!
Only last undoText2.1.patch should be applied and tested!

jghali

2011-01-12 08:20

administrator   ~0025352

Done!

subeditor

2011-01-20 12:45

reporter   ~0025446

Scribus crashes with signal 11 randomly on different changes. I don't know for sure if it connected with with this patch. How can I get more specific information about these crashes?

ale

2011-01-20 21:55

manager   ~0025453

if your computer is fast enough and you're using linux, you can run scribus through the gnu debugger

$ gdb scribus
run

und we scribus crashes, you issue the "bt" command to get the full backtrace which tells exactly where the problem happened.

don't hesitate to ask, if you want more information

subeditor

2011-01-27 09:41

reporter   ~0025484

Here is error report:
[Thread 0x7fffd53c2700 (LWP 4652) exited]
ExpandParSel 2650 10
ExpandParSel 2650 10
storytext::charstyle: access at end of text %i 2660

Program received signal SIGSEGV, Segmentation fault.
0x0000000000e6ddc4 in StoryText::text(int) const ()

Beside it while editing text in layout mode cursor jumps few paragraphs backward on pressing "backspace" or ctrl-z.

subeditor

2011-01-27 10:15

reporter   ~0025485

And again:
setNewFont true

Program received signal SIGSEGV, Segmentation fault.
0x0000000d0000000b in ?? ()

ale

2011-01-28 09:11

manager   ~0025490

hi subeditor,
you should probably post the full backtrace...

subeditor

2011-01-28 09:35

reporter   ~0025491

There was nothing special (except some issues with font metrics), but next time I'll will do what you say.

cezaryece

2011-01-30 12:45

updater   ~0025496

Last edited: 2011-01-30 15:31

I have found one bug in 2.1 patch, maybe it is reason for some instability.
In line 1418 of patch file there should be:
currItem->asTextFrame()->lastUndoAction = PageItem::NOACTION;

Now there is:
currItem->asTextFrame()->lastUndoAction == PageItem::NOACTION;
where error is in comparison instead setting value.

New correct version of patch is uploaded. Please delete previous file.

cezaryece

2011-01-30 12:45

updater  

undoText2.2.patch (57,515 bytes)   
Index: Scribus/scribus/canvasmode_legacy.cpp
===================================================================
--- Scribus/scribus/canvasmode_legacy.cpp	(wersja 16088)
+++ Scribus/scribus/canvasmode_legacy.cpp	(kopia robocza)
@@ -1800,7 +1800,9 @@
                                                        if (!cc.isNull())
                                                        {
                                                                // K.I.S.S.:
+								currItem->oldCPos = 0;
                                                                currItem->itemText.insertChars(0, cc, true);
+								currItem->asTextFrame()->updateUndo(PageItem::INS,cc);
                                                                if (m_doc->docHyphenator->AutoCheck)
                                                                        m_doc->docHyphenator->slotHyphenate(currItem);
                                                                m_ScMW->BookMarkTxT(currItem);
Index: Scribus/scribus/pageitem.cpp
===================================================================
--- Scribus/scribus/pageitem.cpp	(wersja 16088)
+++ Scribus/scribus/pageitem.cpp	(kopia robocza)
@@ -34,6 +34,9 @@
 #include <QPolygon>
 #include <cassert>
 #include <QDebug>
+#include <iostream>
+#include <sstream>
+#include <string>

 #include "canvas.h"
 #include "scpaths.h"
@@ -44,6 +47,7 @@
 #include "guidemanager.h"
 #include "page.h"
 #include "pageitem_latexframe.h"
+#include "pageitem_textframe.h"
 #include "prefsmanager.h"
 #include "propertiespalette.h"
 #include "resourcecollection.h"
@@ -60,9 +64,11 @@
 #include "scribusstructs.h"
 #include "scribuswin.h"
 #include "sctextstream.h"
+#include "serializer.h"
 #include "selection.h"
 #include "sclimits.h"
 #include "text/nlsconfig.h"
+#include "desaxe/saxXML.h"
 #include "undomanager.h"
 #include "undostate.h"
 #include "util.h"
@@ -327,6 +333,7 @@
        CurX = 0;
        CurY = 0;
        CPos = 0;
+	oldCPos = 0;
        Extra = 0;
        TExtra = 0;
        BExtra = 0;
@@ -3209,6 +3216,9 @@
                        restoreShapeContour(ss, isUndo);
                else if (ss->contains("APPLY_IMAGE_EFFECTS"))
                        restoreImageEffects(ss, isUndo);
+		else if (ss->contains("STEXT"))
+			restoreEditText(ss, isUndo);
+
        }
        if (!OnMasterPage.isEmpty())
                m_Doc->setCurrentPage(oldCurrentPage);
@@ -3707,8 +3717,190 @@
        }
 }

+void PageItem::restoreEditText(SimpleState *state, bool isUndo)
+{
+	if (!isTextFrame())
+		return;
+	EditAct action = (EditAct) state->getInt("STEXT");
+	if (action == PARAMFULL || action == PARAMSEL)
+	{
+		QString buffer;
+		if (isUndo)
+			buffer.append(state->get("STEXT_OLD"));
+		else
+			buffer.append(state->get("STEXT_NEW"));
+		if (!buffer.isEmpty())
+		{
+			Serializer dig(*m_Doc);
+			dig.store<ScribusDoc>("<scribusdoc>", m_Doc);
+			StoryText::desaxeRules("/", dig, "SCRIBUSTEXT");
+			dig.addRule("/SCRIBUSTEXT", desaxe::Result<StoryText>());
+			dig.parseMemory(buffer.toStdString().c_str(), buffer.length());
+			StoryText* story = dig.result<StoryText>();
+			if (action == PARAMFULL)
+			{
+				itemText.selectAll();
+				itemText.clear();
+				itemText.append(*story);
+			}
+			else if (action == PARAMSEL)
+			{
+				itemText.deselectAll();
+				itemText.select(state->getInt("STEXT_SELSTART"),state->getInt("STEXT_SELLEN"));
+				asTextFrame()->deleteSelectedTextFromFrame();
+				itemText.insert(state->getInt("STEXT_SELSTART"), *story);
+				itemText.select(state->getInt("STEXT_SELSTART"), story->length());
+				HasSel = true;
+			}
+			CPos = itemText.endOfSelection();
+			delete story;
+		}
+		else { qDebug() << "UNDO buffer EMPTY";}
+	}
+	else if (action == REPSAX)
+	{
+		QString buffout, buffin;  //buffout is deleted, buffin is inserted
+		int pos = state->getInt("STEXT_CPOS");
+		if (isUndo)
+		{
+			buffin.append(state->get("STEXT_OLD"));
+			buffout.append(state->get("STEXT_NEW"));
+		}
+		else
+		{
+			buffin.append(state->get("STEXT_NEW"));
+			buffout.append(state->get("STEXT_OLD"));
+		}
+
+		Serializer dig(*m_Doc);
+		dig.store<ScribusDoc>("<scribusdoc>", m_Doc);
+		StoryText::desaxeRules("/", dig, "SCRIBUSTEXT");
+		dig.addRule("/SCRIBUSTEXT", desaxe::Result<StoryText>());
+		dig.parseMemory(buffout.toStdString().c_str(), buffout.length());
+		StoryText* story = dig.result<StoryText>();
+		itemText.select(pos,story->length(), true);
+		asTextFrame()->deleteSelectedTextFromFrame();
+
+		Serializer dig2(*m_Doc);
+		dig2.store<ScribusDoc>("<scribusdoc>", m_Doc);
+		StoryText::desaxeRules("/", dig2, "SCRIBUSTEXT");
+		dig2.addRule("/SCRIBUSTEXT", desaxe::Result<StoryText>());
+		dig2.parseMemory(buffin.toStdString().c_str(), buffin.length());
+		story = dig2.result<StoryText>();
+		itemText.insert(pos,*story);
+		itemText.select(pos, story->length());
+		HasSel = true;
+		CPos = pos + story->length();
+		delete story;
+	}
+	else if (action == INSSAX || action == DELSAX)
+	{
+		QString str = state->get("STEXT_STR");
+		if (str.isEmpty())
+			str = itemTextSaxed;
+		int pos = state->getInt("STEXT_CPOS");
+		Serializer dig(*m_Doc);
+		dig.store<ScribusDoc>("<scribusdoc>", m_Doc);
+		StoryText::desaxeRules("/", dig, "SCRIBUSTEXT");
+		dig.addRule("/SCRIBUSTEXT", desaxe::Result<StoryText>());
+		dig.parseMemory(str.toStdString().c_str(), str.length());
+		StoryText* story = dig.result<StoryText>();
+		if ((action == INSSAX && isUndo) || (action == DELSAX && !isUndo))
+		{
+			//undo for INSSAX, redo for DELSAX
+			itemText.select(pos, story->length());
+			asTextFrame()->deleteSelectedTextFromFrame();
+		}
+		else
+		{
+			//undo for DELSAX, redo for INSSAX
+			itemText.insert(pos, *story);
+			CPos = pos + story->length();
+		}
+		delete story;
+	}
+	else if (action == INS)
+	{
+		QString str = state->get("STEXT_STR");
+		int pos = state->getInt("STEXT_CPOS");
+		if (isUndo)
+		{
+			itemText.select(pos, str.length());
+			asTextFrame()->deleteSelectedTextFromFrame();
+		}
+		else
+		{
+			itemText.insertChars(pos, str, true);
+			CPos = pos + str.length();
+		}
+	}
+	// after Undo or Redo new actions should create new undoStates
+	asTextFrame()->lastUndoAction = NOACTION;
+
+	m_Doc->scMW()->setTBvals(this);
+	update();
+}
+
+QString PageItem::getItemTextSaxed(EditActPlace undoItem)
+{
+	if (!isTextFrame()) return "";
+	StoryText iT(m_Doc);
+	iT.setDefaultStyle(itemText.defaultStyle());
+	if (undoItem == FRAME)
+		iT.insert(0, itemText);
+	else
+	{
+		int StartOldSel = 0, LenOldSel = 0;
+		if (undoItem == PARAGRAPH)
+		{
+			if (HasSel)
+			{
+				StartOldSel = itemText.startOfSelection();
+				LenOldSel = itemText.lengthOfSelection();
+			}
+			asTextFrame()->ExpandParSel();
+		}
+		else if (undoItem == CHAR || (undoItem == SELECTION && !HasSel))
+		{
+			itemText.select(CPos,1);
+			HasSel = true;
+		}
+		//is SELECTION
+		iT.insert(0, itemText, true);
+		if (LenOldSel > 0) //restoring old selection if undoItem was PARAPGRAPH
+		{
+			itemText.select(StartOldSel, LenOldSel);
+			HasSel = true;
+		}
+	}
+	//saxing text
+	std::ostringstream xmlString;
+	SaxXML xmlStream(xmlString);
+	xmlStream.beginDoc();
+	iT.saxx(xmlStream, "SCRIBUSTEXT");
+	xmlStream.endDoc();
+	std::string xml(xmlString.str());
+	return QString(xml.c_str());
+}
+
+QString PageItem::getTextSaxed(QString str)
+{
+	StoryText iT(m_Doc);
+	iT.setDefaultStyle(itemText.defaultStyle());
+	iT.insertChars(0,str,true);
+	std::ostringstream xmlString;
+	SaxXML xmlStream(xmlString);
+	xmlStream.beginDoc();
+	iT.saxx(xmlStream, "SCRIBUSTEXT");
+	xmlStream.endDoc();
+	std::string xml(xmlString.str());
+	return QString(xml.c_str());
+}
+
 void PageItem::select()
 {
+	if (m_Doc->m_Selection->count() == 1 && m_Doc->m_Selection->itemAt()->isTextFrame())
+		m_Doc->m_Selection->itemAt()->asTextFrame()->lastUndoAction = PageItem::NOACTION;
        m_Doc->view()->Deselect(false);
        //CB #2969 add this true parm to addItem so we dont connectToGUI, the rest of view->SelectItem isnt needed anyway
        m_Doc->m_Selection->addItem(this, true);
Index: Scribus/scribus/pageitem_textframe.cpp
===================================================================
--- Scribus/scribus/pageitem_textframe.cpp	(wersja 16160)
+++ Scribus/scribus/pageitem_textframe.cpp	(kopia robocza)
@@ -46,6 +46,8 @@
 #include "scribus.h"
 #include "scribusdoc.h"
 #include "scribusstructs.h"
+#include "scribusXml.h"
+#include "serializer.h"
 #include "selection.h"
 #include "text/nlsconfig.h"
 #include "undomanager.h"
@@ -67,6 +69,8 @@
        unicodeTextEditMode = false;
        unicodeInputCount = 0;
        unicodeInputString = "";
+	lastUndoAction = NOACTION;
+//	lastAction4Paragraph = false;

        connect(&itemText,SIGNAL(changed()), this, SLOT(slotInvalidateLayout()));
 }
@@ -78,7 +82,9 @@
        unicodeTextEditMode = false;
        unicodeInputCount = 0;
        unicodeInputString = "";
-
+	lastUndoAction = NOACTION;
+//	lastAction4Paragraph = false;
+
        connect(&itemText,SIGNAL(changed()), this, SLOT(slotInvalidateLayout()));
 }

@@ -2653,11 +2676,11 @@
        PageItem *nextItem = this;
        while (nextItem->prevInChain() != 0)
                nextItem = nextItem->prevInChain();
-
+	nextItem->itemTextSaxed = nextItem->getItemTextSaxed(PageItem::FRAME); //for undo
        ParagraphStyle defaultStyle = nextItem->itemText.defaultStyle();
        nextItem->itemText.clear();
        nextItem->itemText.setDefaultStyle(defaultStyle);
-
+	nextItem->asTextFrame()->updateUndo(); //for undo
        while (nextItem != 0)
        {
                nextItem->CPos = 0;
@@ -2710,6 +2733,7 @@
        case Qt::Key_Down:
                if ( (buttonModifiers & Qt::ShiftModifier) == 0 )
                        deselectAll();
+		lastUndoAction = PageItem::NOACTION;
        }

        if (unicodeTextEditMode)
@@ -2735,11 +2759,25 @@
                        if (ok)
                        {
                                if (itemText.lengthOfSelection() > 0)
+				{
+					itemTextSaxed = getItemTextSaxed(PageItem::SELECTION);
                                        deleteSelectedTextFromFrame();
+					lastUndoAction = PageItem::NOACTION;
+				}
                                if (conv < 31)
                                        conv = 32;
+				if (conv == 32)
+				{
+					qDebug() << "SPACE conv?";
+					lastUndoAction = PageItem::NOACTION;
+				}
+				oldCPos = CPos;
                                itemText.insertChars(CPos, QString(QChar(conv)), true);
                                CPos += 1;
+				if (itemTextSaxed.isEmpty())
+					updateUndo(INS, QString(QChar(conv)));
+				else
+					updateUndo(REPSAX, getTextSaxed(QString(QChar(conv))));
 //				Tinput = true;
                                m_Doc->scMW()->setTBvals(this);
                                update();
@@ -3016,7 +3054,10 @@
                {
                        if (itemText.lengthOfSelection() > 0)
                        {
+				oldCPos = itemText.startOfSelection();
+				itemTextSaxed = getItemTextSaxed(PageItem::SELECTION);
                                deleteSelectedTextFromFrame();
+				updateUndo(DELSAX);
                                m_Doc->scMW()->setTBvals(this);
                                update();
 //				view->RefreshItem(this);
@@ -3031,8 +3072,14 @@
                }
                cr = itemText.text(CPos,1);
                if (itemText.lengthOfSelection() == 0)
+		{
                        itemText.select(CPos, 1, true);
+			HasSel = true;
+		}
+		oldCPos = itemText.startOfSelection();
+		itemTextSaxed = getItemTextSaxed(PageItem::SELECTION);
                deleteSelectedTextFromFrame();
+		updateUndo(DELSAX);
                update();
 //		Tinput = false;
                if ((cr == QChar(13)) && (itemText.length() != 0))
@@ -3048,7 +3095,10 @@
                {
                        if (itemText.lengthOfSelection() > 0)
                        {
+				oldCPos = itemText.startOfSelection();
+				itemTextSaxed = getItemTextSaxed(PageItem::SELECTION);
                                deleteSelectedTextFromFrame();
+				updateUndo(DELSAX);
                                m_Doc->scMW()->setTBvals(this);
                                update();
                        }
@@ -3061,8 +3111,12 @@
                {
                        --CPos;
                        itemText.select(CPos, 1, true);
+			HasSel = true;
                }
+		oldCPos = itemText.startOfSelection();
+		itemTextSaxed = getItemTextSaxed(PageItem::SELECTION);
                deleteSelectedTextFromFrame();
+		updateUndo(DELSAX);
 //		Tinput = false;
                if ((cr == QChar(13)) && (itemText.length() != 0))
                {
@@ -3090,7 +3144,10 @@
 #endif
                        if (!controlCharHack && !x11Hack && !k->text().isEmpty())
                        {
+				oldCPos = itemText.startOfSelection();
+				itemTextSaxed = getItemTextSaxed(PageItem::SELECTION);
                                deleteSelectedTextFromFrame();
+				lastUndoAction = PageItem::NOACTION;
                                doUpdate = true;
                        }
                        /*
@@ -3106,16 +3163,30 @@
                //if ((kk == Qt::Key_Tab) || ((kk == Qt::Key_Return) && (buttonState & Qt::ShiftButton)))
                if (kk == Qt::Key_Tab)
                {
+			oldCPos = CPos;
                        itemText.insertChars(CPos, QString(SpecialChars::TAB), true);
                        CPos += 1;
+			lastUndoAction = PageItem::NOACTION;
+			if (itemTextSaxed.isEmpty())
+				updateUndo(INS, QString(SpecialChars::TAB));
+			else
+				updateUndo(REPSAX, getTextSaxed(QString(SpecialChars::TAB)));
 //			Tinput = true;
 //			view->RefreshItem(this);
                        doUpdate = true;
                }
                else if ((uc[0] > QChar(31) && m_Doc->currentStyle.charStyle().font().canRender(uc[0])) || (as == 13) || (as == 30))
                {
+			if (uc[0] == QChar(32) || uc[0] == QChar(13))
+				lastUndoAction = PageItem::NOACTION;
+			if (lastUndoAction != PageItem::INS)
+				oldCPos = CPos;
                        itemText.insertChars(CPos, uc, true);
                        CPos += 1;
+			if (itemTextSaxed.isEmpty())
+				updateUndo(INS, uc);
+			else
+				updateUndo(REPSAX, getTextSaxed(uc));
                        if ((m_Doc->docHyphenator->AutoCheck) && (CPos > 1))
                        {
                                Twort = "";
@@ -3278,8 +3348,30 @@
 // 	layoutWeakLock = true;
 // 	update();
        m_Doc->regionsChanged()->update(getBoundingRect());
+	lastUndoAction = PageItem::NOACTION;
 }

+void PageItem_TextFrame::ExpandParSel() //expand selection to whole paragrpah(s)
+{
+	if (m_Doc->appMode != modeEdit)
+		return;
+	int StartSel = 0, LenSel = 0;
+	if (HasSel)
+	{
+		//extend selection to whole paragraphs
+		StartSel = itemText.startOfParagraph(itemText.nrOfParagraph(itemText.startOfSelection()));
+		LenSel = itemText.endOfParagraph(itemText.nrOfParagraph(itemText.endOfSelection())) - StartSel;
+	}
+	else
+	{
+		//extend selection to whole paragraph
+		StartSel = itemText.startOfParagraph(itemText.nrOfParagraph(CPos));
+		LenSel = itemText.endOfParagraph(itemText.nrOfParagraph(CPos)) - StartSel;
+	}
+	itemText.select(StartSel, LenSel);
+	qDebug() << "ExpandParSel" << StartSel << LenSel;
+}
+
 void PageItem_TextFrame::deselectAll()
 {
        if ( itemText.lengthOfSelection() > 0 )
@@ -3299,6 +3391,7 @@
        }
        //CB Replace with direct call for now //emit HasNoTextSel();
        m_Doc->scMW()->DisableTxEdit();
+	lastUndoAction = PageItem::NOACTION;
 }

 double PageItem_TextFrame::columnWidth()
@@ -3558,3 +3651,75 @@
 {
 	invalidateLayout();
 }
+
+void PageItem_TextFrame::updateUndo(EditAct action, QString str)
+{
+	if (UndoManager::undoEnabled() && undoManager->undoEnabled())
+	{
+		SimpleState* ss;
+		bool newState = true;  // indicate when new undoState should be created
+		if (lastUndoAction == action && action != REPSAX && action != DELSAX)
+		{
+			ss = (SimpleState*) undoManager->getLastUndoState();
+			if (ss->undoObject() == this)
+			{
+				if (action == PARAMFULL || action == PARAMSEL)
+				{
+					itemTextSaxed = ss->get("STEXT_OLD");
+					newState = false;
+				}
+				else if (action == INSSAX || action == INS)
+				{
+					QString tmpstr(ss->get("STEXT_STR"));
+					tmpstr.append(str);
+					str = tmpstr;
+					newState = false;
+				}
+			}
+		}
+		if (newState)
+		{
+			ss = new SimpleState(UndoManager::EditText);
+			ss->set("STEXT_CPOS",oldCPos);
+		}
+		if (action == INSSAX || action == INS)
+			ss->set("STEXT_STR",str);
+		else if (action == DELSAX)
+			ss->set("STEXT_STR",itemTextSaxed);
+		else if (action == REPSAX)
+		{
+			ss->set("STEXT_NEW",str);
+			ss->set("STEXT_OLD",itemTextSaxed);
+		}
+		else
+		{
+			if (action == PARAMFULL && m_Doc->appMode == modeEdit)
+			{
+				//action is for paragraph where cursor is
+				ExpandParSel();
+				action = PARAMSEL;
+			}
+			if (action == PARAMSEL)
+			{
+				ss->set("STEXT_CPOS",CPos);
+				ss->set("STEXT_SELSTART", itemText.startOfSelection());
+				ss->set("STEXT_SELLEN", itemText.endOfSelection() - itemText.startOfSelection());
+			}
+			ss->set("STEXT_OLD", itemTextSaxed);
+			itemTextSaxed = getItemTextSaxed((action == PARAMSEL) ? SELECTION : FRAME);
+			ss->set("STEXT_NEW", itemTextSaxed);
+			if (QString::compare(ss->get("STEXT_OLD"),ss->get("STEXT_NEW")) == 0)
+			{
+				//nothing change - quit without set new Undo step
+				itemTextSaxed.clear();
+				delete ss;
+				return;
+			}
+		}
+		itemTextSaxed.clear();
+		lastUndoAction = action;
+		ss->set("STEXT",(int) action);
+		if (newState)
+			undoManager->action(this, ss);
+	}
+}
Index: Scribus/scribus/scribus.cpp
===================================================================
--- Scribus/scribus/scribus.cpp	(wersja 16088)
+++ Scribus/scribus/scribus.cpp	(kopia robocza)
@@ -137,6 +137,7 @@
 #include "nodeeditpalette.h"
 #include "outlinepalette.h"
 #include "page.h"
+#include "pageitem.h"
 #include "pageitem_imageframe.h"
 #include "pageitem_latexframe.h"
 #include "pageitem_textframe.h"
@@ -1112,9 +1120,18 @@
                                        if (unicodevalue!=-1)
                                        {
                                                if (currItem->HasSel && currItem->itemType()==PageItem::TextFrame)
+						{
+							currItem->oldCPos = currItem->itemText.startOfSelection();
+							currItem->itemTextSaxed = currItem->getItemTextSaxed(PageItem::SELECTION);
                                                        currItem->asTextFrame()->deleteSelectedTextFromFrame();
-
+						}
+						else
+							currItem->oldCPos = currItem->CPos;
                                                currItem->itemText.insertChars(currItem->CPos, QString(QChar(unicodevalue)), true);
+						if (currItem->itemTextSaxed.isEmpty())
+							currItem->asTextFrame()->updateUndo(PageItem::INS, QString(QChar(unicodevalue)));
+						else
+							currItem->asTextFrame()->updateUndo(PageItem::REPSAX, currItem->getTextSaxed(QString(QChar(unicodevalue))));
                                                currItem->CPos += 1;
 //						currItem->Tinput = true;
                                                currItem->update();
@@ -1131,7 +1148,9 @@
                                                        currItem->itemText.item(qMax(currItem->CPos-1,0))->setEffects(fl);
 #else
                                                        currItem->itemText.insertChars(currItem->CPos, QString(SpecialChars::SHYPHEN), true);
+							currItem->oldCPos = currItem->CPos;
                                                        currItem->CPos += 1;
+							currItem->asTextFrame()->updateUndo(PageItem::INS, QString(SpecialChars::SHYPHEN));
 #endif
 //							currItem->Tinput = true;
                                                        currItem->update();
@@ -3318,7 +3337,6 @@
                scrActions["itemLockSize"]->setChecked(currItem->sizeLocked());
                scrActions["itemPrintingEnabled"]->setChecked(currItem->printEnabled());
        }
-
        //propertiesPalette->NewSel(SelectedType);
        if (SelectedType != -1)
        {
@@ -4289,6 +4307,7 @@
                        ImportSetup impsetup=gt->run();
                        if (impsetup.runDialog)
                        {
+				currItem->itemTextSaxed = currItem->getItemTextSaxed(PageItem::FRAME);
                                if (currItem->itemText.length() != 0)
                                {
                                        int t = QMessageBox::warning(this, CommonStrings::trWarning, tr("Do you really want to clear all your text?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
@@ -4296,6 +4315,7 @@
                                                return;
                                }
                                gt->launchImporter(impsetup.importer, impsetup.filename, impsetup.textOnly, impsetup.encoding, false);
+				currItem->asTextFrame()->updateUndo();
                        }
                        delete gt;
                        if (doc->docHyphenator->AutoCheck)
@@ -4321,7 +4341,8 @@

        if (!currItem->asTextFrame())
                return; // not a text frame
-
+	// I dont know what is doing here, but Undo dont hurts
+	currItem->itemTextSaxed = currItem->getItemTextSaxed(PageItem::FRAME);
        ScGTPluginManager::instance()->run();
        if (doc->docHyphenator->AutoCheck)
                doc->docHyphenator->slotHyphenate(currItem);
@@ -4330,6 +4351,7 @@
                if (doc->Items->at(a)->isBookmark)
                        bookmarkPalette->BView->ChangeText(doc->Items->at(a));
        }
+	currItem->asTextFrame()->updateUndo();
        view->DrawNew();
        slotDocCh();
 }
@@ -4385,7 +4407,10 @@
                ImportSetup impsetup=gt->run();
                if (impsetup.runDialog)
                {
+			PageItem *currItem = doc->m_Selection->itemAt(0);
+			currItem->itemTextSaxed = currItem->getItemTextSaxed(PageItem::FRAME);
                        gt->launchImporter(impsetup.importer, impsetup.filename, impsetup.textOnly, impsetup.encoding, true);
+			currItem->asTextFrame()->updateUndo();
                }
                delete gt;
                //CB Hyphenating now emits doc changed, plus we change lang as appropriate
@@ -4964,6 +4989,10 @@
                {
                        if ((currItem->itemText.length() == 0) || (!currItem->HasSel))
                                return;
+			currItem->oldCPos = currItem->itemText.startOfSelection();
+			//for undo
+			currItem->itemTextSaxed = currItem->getItemTextSaxed(PageItem::SELECTION);
+
                        StoryText itemText(doc);
                        itemText.setDefaultStyle(currItem->itemText.defaultStyle());
                        itemText.insert(0, currItem->itemText, true);
@@ -4981,6 +5010,8 @@
                        QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);

                        dynamic_cast<PageItem_TextFrame*>(currItem)->deleteSelectedTextFromFrame();
+			//for undo
+			currItem->asTextFrame()->updateUndo(PageItem::DELSAX);
                        currItem->update();
                }
                else
@@ -5116,14 +5147,19 @@
                {
                        PageItem_TextFrame *currItem = dynamic_cast<PageItem_TextFrame*>(doc->m_Selection->itemAt(0));
                        assert(currItem != NULL);
+			currItem->lastUndoAction = PageItem::NOACTION;
                        if (currItem->HasSel)
+			{
+				//for text undo, storing current selection
+				currItem->itemTextSaxed = currItem->getItemTextSaxed(PageItem::SELECTION);
                                currItem->deleteSelectedTextFromFrame();
+			}

                        if (currItem->CPos < 0)
                                currItem->CPos = 0;
                        if (currItem->CPos > currItem->itemText.length())
                                currItem->CPos = currItem->itemText.length();
-
+			currItem->oldCPos = currItem->CPos;
                        if (ScMimeData::clipboardHasScribusText())
                        {
                                Serializer dig(*doc);
@@ -5137,6 +5173,13 @@
                                StoryText* story = dig.result<StoryText>();

                                currItem->itemText.insert(currItem->CPos, *story);
+
+				//for text undo, select inserted text for saxing it
+				currItem->itemText.select(currItem->CPos,story->length());
+				currItem->HasSel = true;
+				//if itemTextSaxed is not empty there was selection
+				currItem->updateUndo(currItem->itemTextSaxed.isEmpty() ? PageItem::INSSAX : PageItem::REPSAX,currItem->getItemTextSaxed(PageItem::SELECTION));
+
                                currItem->CPos += story->length();

                                delete story;
@@ -5222,6 +5265,11 @@
                                currItem->itemText.insertObject(currItem->CPos, currItem3);
                                currItem->CPos += 1;
                                undoManager->setUndoEnabled(true);
+				//for text undo, select inserted text for saxing it
+				currItem->itemText.select(currItem->oldCPos,currItem->CPos - currItem->oldCPos);
+				currItem->HasSel = true;
+				//if itemTextSaxed is not empty there was selection
+				currItem->updateUndo(currItem->itemTextSaxed.isEmpty() ? PageItem::INSSAX : PageItem::REPSAX,currItem->getItemTextSaxed(PageItem::SELECTION));
                        }
                        else
                        {
@@ -5230,6 +5278,15 @@
                                text = text.replace("\r\n", SpecialChars::PARSEP);
                                text = text.replace('\n', SpecialChars::PARSEP);
                                currItem->itemText.insertChars(currItem->CPos, text, true);
+				//for text undo, select inserted text for saxing it
+				currItem->itemText.select(currItem->CPos,text.length());
+				currItem->HasSel = true;
+				//if itemTextSaxed is not empty there was selection
+				if (currItem->itemTextSaxed.isEmpty())
+					currItem->updateUndo(PageItem::INS, text);
+				else
+					currItem->updateUndo(PageItem::REPSAX, currItem->getTextSaxed(text));
+
                        }
                        currItem->update();
                }
@@ -6927,7 +6984,15 @@
                        }
                }
        }
+	//for undo
+	PageItem *currItem = doc->m_Selection->itemAt(0);
+	if (currItem->asTextFrame())
+		currItem->itemTextSaxed = currItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
+	qDebug() << "setNewFont" << currItem->HasSel;
        doc->itemSelection_SetFont(nf2);
+	if (currItem->asTextFrame())
+		currItem->asTextFrame()->updateUndo(currItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
+
 //	doc->currentStyle.charStyle().setFont((*doc->AllFonts)[nf2]);
        view->DrawNew();
 // 	slotDocCh();
@@ -6935,6 +7000,10 @@

 void ScribusMainWindow::setItemFSize(int id)
 {
+	PageItem *currItem = doc->m_Selection->itemAt(0);
+	if (currItem->asTextFrame())
+		currItem->itemTextSaxed = currItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
+
        int c = id;
        if (c != -1)
                doc->itemSelection_SetFontSize(c*10);
@@ -6950,6 +7019,9 @@
                }
                delete dia;
        }
+	if (currItem->asTextFrame())
+		currItem->asTextFrame()->updateUndo(currItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
+
        propertiesPalette->setSize(c*10);
 // 	slotDocCh();
 }
@@ -7330,9 +7402,9 @@
        if (HaveDoc)
        {
 //		doc->currentStyle.setAlignment(static_cast<ParagraphStyle::AlignmentType>(a));
+		PageItem *currItem = doc->m_Selection->itemAt(0);
                doc->itemSelection_SetAlignment(a);
                propertiesPalette->setAli(a);
-		PageItem *currItem = doc->m_Selection->itemAt(0);
                setTBvals(currItem);
        }
 }
@@ -7347,8 +7419,16 @@
                        doc->itemSelection_EraseParagraphStyle();
                }
                else */
-			doc->itemSelection_SetNamedParagraphStyle(name);
                PageItem *currItem = doc->m_Selection->itemAt(0);
+		if (currItem->asTextFrame())
+		{
+//			currItem->asTextFrame()->ExpandParSel();
+//			currItem->asTextFrame()->lastAction4Paragraph = true;
+			currItem->itemTextSaxed = currItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::PARAGRAPH : PageItem::FRAME);
+		}
+		doc->itemSelection_SetNamedParagraphStyle(name);
+		if (currItem->asTextFrame())
+			currItem->asTextFrame()->updateUndo();
                setTBvals(currItem);
        }
 }
@@ -7363,8 +7443,12 @@
                        doc->itemSelection_EraseCharStyle();
                }
                else */
-			doc->itemSelection_SetNamedCharStyle(name);
                PageItem *currItem = doc->m_Selection->itemAt(0);
+		if (currItem->asTextFrame())
+			currItem->itemTextSaxed = currItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
+		doc->itemSelection_SetNamedCharStyle(name);
+		if (currItem->asTextFrame())
+			currItem->asTextFrame()->updateUndo(currItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
                setTBvals(currItem);
        }
 }
Index: Scribus/scribus/propertiespalette.cpp
===================================================================
--- Scribus/scribus/propertiespalette.cpp	(wersja 16088)
+++ Scribus/scribus/propertiespalette.cpp	(kopia robocza)
@@ -52,7 +52,9 @@
 #include "colorlistbox.h"
 #include "sccolorengine.h"
 #include "cpalette.h"
+#include "pageitem.h"
 #include "pageitem_textframe.h"
+#include "styles/paragraphstyle.h"
 #include "sccombobox.h"
 #include "scfonts.h"
 #include "scribus.h"
@@ -2923,8 +2936,16 @@
 {
        if ((HaveDoc) && (HaveItem))
        {
+		if (CurItem->asTextFrame())
+		{
+//			CurItem->asTextFrame()->ExpandParSel();
+			CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::PARAGRAPH : PageItem::FRAME);
+//			CurItem->asTextFrame()->lastAction4Paragraph = true;
+		}
                doc->itemSelection_SetLineSpacingMode(id);
                updateStyle(doc->appMode == modeEdit? CurItem->currentStyle() : CurItem->itemText.defaultStyle());
+		if (CurItem->asTextFrame())
+			CurItem->asTextFrame()->updateUndo();
        }
 }

@@ -3266,6 +3285,8 @@
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(PageItem::FRAME);
        int omt(ParagraphStyle::OM_None);
 //	if (optMarginCheckLeftProtruding->isChecked()) omt+=ParagraphStyle::OM_LeftProtruding;
        if (optMarginRadioBoth->isChecked())
@@ -3276,6 +3297,8 @@
                omt = ParagraphStyle::OM_RightHangingPunct;

        doc->itemSelection_SetOpticalMargins(omt);
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo();
 }

 void PropertiesPalette::resetOpticalMargins()
@@ -3303,9 +3326,13 @@
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        ParagraphStyle newStyle;
        newStyle.setMinWordTracking(minWordTrackingSpinBox->value() / 100.0);
        doc->itemSelection_ApplyParagraphStyle(newStyle);
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }


@@ -3313,28 +3340,40 @@
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        ParagraphStyle newStyle;
 //	newStyle.setNormWordTracking(percent / 100.0);
        newStyle.charStyle().setWordTracking(normWordTrackingSpinBox->value() / 100.0);
        doc->itemSelection_ApplyParagraphStyle(newStyle);
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }

 void PropertiesPalette::setMinGlyphExtension()
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        ParagraphStyle newStyle;
        newStyle.setMinGlyphExtension(minGlyphExtSpinBox->value() / 100.0);
        doc->itemSelection_ApplyParagraphStyle(newStyle);
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }

 void PropertiesPalette::setMaxGlyphExtension()
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        ParagraphStyle newStyle;
        newStyle.setMaxGlyphExtension(maxGlyphExtSpinBox->value() / 100.0);
        doc->itemSelection_ApplyParagraphStyle(newStyle);
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }


@@ -3352,14 +3391,22 @@
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        doc->itemSelection_SetScaleV(qRound(ChScaleV->value() * 10));
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }

 void PropertiesPalette::NewTBase()
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        doc->itemSelection_SetBaselineOffset(qRound(ChBase->value() * 10));
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }

 void PropertiesPalette::setTScale(double e)
@@ -3386,7 +3433,11 @@
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        doc->itemSelection_SetScaleH(qRound(ChScale->value() * 10));
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }

 void PropertiesPalette::NewX()
@@ -3810,7 +3861,15 @@
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+	{
+//		CurItem->asTextFrame()->ExpandParSel();
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::PARAGRAPH : PageItem::FRAME);
+//		CurItem->asTextFrame()->lastAction4Paragraph = true;
+	}
        doc->itemSelection_SetLineSpacing(LineSp->value());
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo();
 }

 void PropertiesPalette::HandleGapSwitch()
@@ -3872,14 +3931,22 @@
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        doc->itemSelection_SetFontSize(qRound(Size->value()*10.0));
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }

 void PropertiesPalette::NewTracking()
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        doc->itemSelection_SetTracking(qRound(Extra->value() * 10.0));
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }

 void PropertiesPalette::NewLocalXY()
@@ -4197,25 +4264,72 @@

 void PropertiesPalette::NewAlignement(int a)
 {
+	int StartSel = 0, EndSel = 0, LenSel = 0;
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+	{
+		// hack for apply left align for text with no align at all
+		//so during Undo/Redo some align will be applied
+		if (doc->appMode == modeEdit)
+		{
+			//selected parapgraph(s) only
+			CurItem->asTextFrame()->ExpandParSel();
+			StartSel = CurItem->itemText.startOfSelection();
+			EndSel = CurItem->itemText.endOfSelection();
+			for (uint i = CurItem->itemText.nrOfParagraph(StartSel); i <= CurItem->itemText.nrOfParagraph(EndSel); i++)
+			{
+				if (CurItem->itemText.paragraphStyle(CurItem->itemText.startOfParagraph(i)).alignment() == 0)
+				{
+					CurItem->itemText.select(CurItem->itemText.startOfParagraph(i), CurItem->itemText.endOfParagraph(i) - CurItem->itemText.startOfParagraph(i));
+					CurItem->HasSel = true;
+					doc->itemSelection_SetAlignment(0);
+				}
+			}
+		}
+		else
+		{
+			//for whole frame
+			for (uint i = 0; i <= CurItem->itemText.nrOfParagraph(CurItem->itemText.lastInFrame()); i++)
+			{
+				if (CurItem->itemText.paragraphStyle(CurItem->itemText.startOfParagraph(i)).alignment() == 0)
+				{
+					CurItem->CPos = CurItem->itemText.startOfParagraph(i);
+					doc->itemSelection_SetAlignment(0);
+				}
+
+			}
+		}
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::PARAGRAPH : PageItem::FRAME);
+//		CurItem->asTextFrame()->lastAction4Paragraph = true;
+	}
        doc->itemSelection_SetAlignment(a);
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo();
 }

 void PropertiesPalette::setTypeStyle(int s)
 {
        if (!m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        emit NewEffects(s);
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }

 void PropertiesPalette::newShadowOffs()
 {
        if ((HaveDoc) && (HaveItem))
        {
+		if (CurItem->asTextFrame())
+			CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
                int x = qRound(SeStyle->ShadowVal->Xoffset->value() * 10.0);
                int y = qRound(SeStyle->ShadowVal->Yoffset->value() * 10.0);
                doc->itemSelection_SetShadowOffsets(x, y);
+		if (CurItem->asTextFrame())
+			CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
        }
 }

@@ -4235,9 +4349,13 @@
 {
        if ((HaveDoc) && (HaveItem))
        {
+		if (CurItem->asTextFrame())
+			CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
                int x = qRound(SeStyle->UnderlineVal->LPos->value() * 10.0);
                int y = qRound(SeStyle->UnderlineVal->LWidth->value() * 10.0);
                doc->itemSelection_SetUnderline(x, y);
+		if (CurItem->asTextFrame())
+			CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
        }
 }

@@ -4257,9 +4375,13 @@
 {
        if ((HaveDoc) && (HaveItem))
        {
+		if (CurItem->asTextFrame())
+			CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
                int x = qRound(SeStyle->StrikeVal->LPos->value() * 10.0);
                int y = qRound(SeStyle->StrikeVal->LWidth->value() * 10.0);
                doc->itemSelection_SetStrikethru(x, y);
+		if (CurItem->asTextFrame())
+			CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
        }
 }

@@ -4288,7 +4410,13 @@
 {
        int x = qRound(SeStyle->OutlineVal->LWidth->value() * 10.0);
        if ((HaveDoc) && (HaveItem))
+	{
+		if (CurItem->asTextFrame())
+			CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
                doc->itemSelection_SetOutlineWidth(x);
+		if (CurItem->asTextFrame())
+			CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
+	}
 }

 void PropertiesPalette::DoLower()
@@ -4614,7 +4742,11 @@
                return;
        if (HaveDoc)
        {
+		if (CurItem->asTextFrame())
+			CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
                doc->itemSelection_EraseCharStyle();
+		if (CurItem->asTextFrame())
+			CurItem->asTextFrame()->updateUndo(doc->appMode == modeEdit? PageItem::PARAMSEL : PageItem::PARAMFULL);
        }
 }

@@ -4625,9 +4757,17 @@
                return;
        if (HaveDoc)
        {
+		if (CurItem->asTextFrame())
+		{
+//			CurItem->asTextFrame()->ExpandParSel();
+			CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::PARAGRAPH : PageItem::FRAME);
+//			CurItem->asTextFrame()->lastAction4Paragraph = true;
+		}
                doc->itemSelection_EraseParagraphStyle();
                CharStyle emptyCStyle;
                doc->itemSelection_SetCharStyle(emptyCStyle);
+		if (CurItem->asTextFrame())
+			CurItem->asTextFrame()->updateUndo(doc->appMode == modeEdit ? PageItem::PARAMSEL : PageItem::PARAMFULL);
        }
 }

@@ -4773,14 +4913,23 @@
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        doc->itemSelection_SetFillColor(TxFill->currentColor());
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }

 void PropertiesPalette::newTxtStroke()
 {
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        doc->itemSelection_SetStrokeColor(TxStroke->currentColor());
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
+
 }

 void PropertiesPalette::setActShade()
@@ -4788,6 +4937,8 @@
        if (!HaveDoc || !HaveItem || !m_ScMW || m_ScMW->scriptIsRunning())
                return;
        int b;
+	if (CurItem->asTextFrame())
+		CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::SELECTION : PageItem::FRAME);
        if (PM1 == sender())
        {
                b = PM1->getValue();
@@ -4798,6 +4949,8 @@
                b = PM2->getValue();
                doc->itemSelection_SetFillShade(b);
        }
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo(CurItem->HasSel? PageItem::PARAMSEL : PageItem::PARAMFULL);
 }

 void PropertiesPalette::setActFarben(QString p, QString b, double shp, double shb)
@@ -4945,6 +5098,10 @@
                return;
        CurItem->fillRule = EvenOdd->isChecked();
        CurItem->update();
+	qDebug() << "PropertiesPalette::handleFillRule() updateUNDO";
+	if (CurItem->asTextFrame())
+		CurItem->asTextFrame()->updateUndo();
+
        emit DocChanged();
 }

@@ -5028,6 +5185,11 @@
                TabManager *dia = new TabManager(this, doc->unitIndex(), style.tabValues(), i2->columnWidth());
                if (dia->exec())
                {
+			if (CurItem->asTextFrame())
+			{
+				CurItem->itemTextSaxed = CurItem->getItemTextSaxed(doc->appMode == modeEdit? PageItem::PARAGRAPH : PageItem::FRAME);
+//				CurItem->asTextFrame()->lastAction4Paragraph = true;
+			}
                        if (doc->appMode != modeEdit)
                        {
                                ParagraphStyle newStyle(CurItem->itemText.defaultStyle());
@@ -5041,6 +5203,9 @@
                                doc->itemSelection_ApplyParagraphStyle(newStyle);
                        }
                        CurItem->update();
+			if (CurItem->asTextFrame())
+				CurItem->asTextFrame()->updateUndo();
+
                        emit DocChanged();
                }
                delete dia;
Index: Scribus/scribus/undomanager.cpp
===================================================================
--- Scribus/scribus/undomanager.cpp	(wersja 16088)
+++ Scribus/scribus/undomanager.cpp	(kopia robocza)
@@ -525,6 +526,11 @@
        target->setUName(oldName);
 }

+UndoState* UndoManager::getLastUndoState()
+{
+	return stacks_[currentDoc_].undoActions_[0];
+}
+
 void UndoManager::undo(int steps)
 {
        if (!undoEnabled_)
@@ -958,6 +964,7 @@
        UndoManager::Copy               = tr("Copy");
        UndoManager::CopyPage           = tr("Copy page");
        UndoManager::ToOutlines         = tr("Convert to outlines");
+	UndoManager::EditText		= tr("Edit text");
 }

 void UndoManager::initIcons()
@@ -1158,6 +1165,7 @@
 QString UndoManager::Copy               = "";
 QString UndoManager::CopyPage           = "";
 QString UndoManager::ToOutlines         = "";
+QString UndoManager::EditText		= "";

 /*** Icons for UndoObjects *******************************************/
 QPixmap *UndoManager::IImageFrame      = 0;
Index: Scribus/scribus/loremipsum.cpp
===================================================================
--- Scribus/scribus/loremipsum.cpp	(wersja 16088)
+++ Scribus/scribus/loremipsum.cpp	(kopia robocza)
@@ -311,6 +311,7 @@
                        continue;
                if (currItem->itemText.length() != 0)
                {
+			currItem->itemTextSaxed = currItem->getItemTextSaxed(PageItem::FRAME);
                        m_Doc->itemSelection_ClearItem();
                        /* ClearItem() doesn't return true or false so
                        the following test has to be done */
@@ -342,7 +343,12 @@
 #endif

                // K.I.S.S.:
-		currItem->itemText.insertChars(0, lp->createLorem(paraCount, random));
+		QString Lorip = lp->createLorem(paraCount, random);
+		currItem->itemText.insertChars(0, Lorip);
+		if (currItem->itemTextSaxed.isEmpty())
+			currItem->asTextFrame()->updateUndo(PageItem::INS,Lorip);
+		else
+			currItem->asTextFrame()->updateUndo(PageItem::REPSAX, currItem->getTextSaxed(Lorip));
                delete lp;

                //if (ScMW->view->SelItem.at(i)->Doc->docHyphenator->AutoCheck)
Index: Scribus/scribus/undomanager.h
===================================================================
--- Scribus/scribus/undomanager.h	(wersja 16088)
+++ Scribus/scribus/undomanager.h	(kopia robocza)
@@ -434,8 +434,11 @@
        void setTexts();

 public:
-
        /**
+	 * @brief Returns last saved Undo state
+	 */
+	UndoState* getLastUndoState();
+	/**
        * @name Action strings
        * Strings describing undo actions
        */
@@ -585,6 +588,7 @@
        static QString Copy;
        static QString CopyPage;
        static QString ToOutlines;
+	static QString EditText;
        /*@}*/

        /**
Index: Scribus/scribus/charselect.cpp
===================================================================
--- Scribus/scribus/charselect.cpp	(wersja 16088)
+++ Scribus/scribus/charselect.cpp	(kopia robocza)
@@ -15,6 +15,7 @@
 #include "scpaths.h"
 #include "scribusdoc.h"
 #include "scribusview.h"
+#include "undomanager.h"
 #include "util.h"
 #include "util_icon.h"
 #include "charselectenhanced.h"
@@ -106,16 +107,20 @@
 void CharSelect::slot_insertSpecialChar()
 {
        emit insertSpecialChar();
-
        if (!m_Item)
                return;
-
        if (m_Item->HasSel)
+	{
+		m_Item->itemTextSaxed = m_Item->getItemTextSaxed(PageItem::SELECTION);
                m_Item->asTextFrame()->deleteSelectedTextFromFrame();
+		m_Item->asTextFrame()->lastUndoAction = PageItem::NOACTION;
+	}
        if (m_Item->asTextFrame())
                m_Item->asTextFrame()->invalidateLayout();
        //CB: Avox please make text->insertchar(char) so none of this happens in gui code, and item can tell doc its changed so the view and mainwindow slotdocch are not necessary
        QChar ch;
+	QString txtIns;
+	m_Item->oldCPos = m_Item->CPos;
        for (int a=0; a<chToIns.length(); ++a)
        {
                ch = chToIns.at(a);
@@ -125,7 +130,12 @@
                        ch = QChar(32);
                m_Item->itemText.insertChars(m_Item->CPos, ch, true);
                m_Item->CPos += 1;
+		txtIns.append(ch);
        }
+	if (m_Item->itemTextSaxed.isEmpty())
+		m_Item->asTextFrame()->updateUndo(PageItem::INS, txtIns);
+	else
+		m_Item->asTextFrame()->updateUndo(PageItem::REPSAX, m_Item->getTextSaxed(txtIns));
        m_doc->updateFrameItems();
        m_doc->view()->DrawNew();
        m_doc->changed();
@@ -137,7 +147,11 @@
        if (!m_Item)
                return;
        if (m_Item->HasSel)
+	{
+		m_Item->itemTextSaxed = m_Item->getItemTextSaxed(PageItem::SELECTION);
                m_Item->asTextFrame()->deleteSelectedTextFromFrame();
+		m_Item->asTextFrame()->lastUndoAction = PageItem::NOACTION;
+	}
        if (m_Item->asTextFrame())
                m_Item->asTextFrame()->invalidateLayout();
 // 	//CB: Avox please make text->insertchar(char) so none of this happens in gui code, and item can tell doc its changed so the view and mainwindow slotdocch are not necessary
@@ -145,7 +159,12 @@
                ch = QChar(13);
        if (ch == QChar(9))
                ch = QChar(32);
+	m_Item->oldCPos = m_Item->CPos;
        m_Item->itemText.insertChars(m_Item->CPos, ch, true);
+	if (m_Item->itemTextSaxed.isEmpty())
+		m_Item->asTextFrame()->updateUndo(PageItem::INS, QString(ch));
+	else
+		m_Item->asTextFrame()->updateUndo(PageItem::REPSAX, m_Item->getTextSaxed(QString(ch)));
        m_doc->updateFrameItems();
        m_Item->CPos += 1;
        m_doc->view()->DrawNew();
Index: Scribus/scribus/canvasmode_edit.cpp
===================================================================
--- Scribus/scribus/canvasmode_edit.cpp	(wersja 16160)
+++ Scribus/scribus/canvasmode_edit.cpp	(kopia robocza)
@@ -375,6 +377,7 @@
        PageItem *currItem = 0;
        if (GetItem(&currItem) && (m_doc->appMode == modeEdit) && currItem->asTextFrame())
        {
+		currItem->asTextFrame()->lastUndoAction = PageItem::NOACTION;
                //CB if annotation, open the annotation dialog
                if (currItem->isAnnotation())
                {
@@ -619,7 +622,10 @@
                                                qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
                                        }
                                        if (currItem->asTextFrame())
+					{
                                                m_view->slotSetCurs(m->globalPos().x(), m->globalPos().y());
+						currItem->asTextFrame()->lastUndoAction = PageItem::NOACTION;
+					}
                                }
                                else
                                {
@@ -636,6 +642,7 @@
                //CB Where we set the cursor for a click in text frame
                if (currItem->asTextFrame())
                {
+			currItem->asTextFrame()->lastUndoAction = PageItem::NOACTION;
                        inText = m_view->slotSetCurs(m->globalPos().x(), m->globalPos().y());
                        //CB If we clicked outside a text frame to go out of edit mode and deselect the frame
                        if (!inText)
@@ -644,6 +651,8 @@
                                m_view->Deselect(true);
                                //m_view->slotDoCurs(true);
                                m_view->requestMode(modeNormal);
+				if (currItem->isTextFrame())
+					currItem->asTextFrame()->lastUndoAction = PageItem::NOACTION;
                                return;
                        }

@@ -668,6 +677,7 @@
                                                        oldP = currItem->itemText.endOfSelection();
                                                }
                                                currItem->asTextFrame()->itemText.extendSelection(oldP, currItem->CPos);
+						currItem->asTextFrame()->lastUndoAction = PageItem::NOACTION;
                                                oldCp = currItem->CPos;
                                        }
                                        else
@@ -683,6 +693,7 @@
                                                                currItem->CPos = currItem->itemText.startOfParagraph(currItem->itemText.nrOfParagraph(currItem->CPos));
                                                }
                                                currItem->asTextFrame()->ExpandSel(dir, oldP);
+						currItem->asTextFrame()->lastUndoAction = PageItem::NOACTION;
                                                oldCp = oldP;
                                        }
                                }
@@ -690,6 +701,7 @@
                                {
                                        oldCp = currItem->CPos;
                                        currItem->itemText.deselectAll();
+					currItem->asTextFrame()->lastUndoAction = PageItem::NOACTION;
                                        currItem->HasSel = false;
                                }
                                currItem->emitAllToGUI();
@@ -704,7 +716,9 @@
                                        if (!cc.isNull())
                                        {
                                                // K.I.S.S.:
+						currItem->oldCPos = 0;
                                                currItem->itemText.insertChars(0, cc, true);
+						currItem->asTextFrame()->updateUndo(PageItem::INS,cc);
                                                if (m_doc->docHyphenator->AutoCheck)
                                                        m_doc->docHyphenator->slotHyphenate(currItem);
                                                m_ScMW->BookMarkTxT(currItem);
@@ -722,6 +736,7 @@
                else if (!currItem->asImageFrame() ||
                                 m_canvas->frameHitTest(QPointF(mousePointDoc.x(),mousePointDoc.y()), currItem) < 0)
                {
+			m_doc->m_Selection->delaySignalsOn();
                        m_view->Deselect(true);
                        if (SeleItem(m))
                        {
Index: Scribus/scribus/pageitem.h
===================================================================
--- Scribus/scribus/pageitem.h	(wersja 16088)
+++ Scribus/scribus/pageitem.h	(kopia robocza)
@@ -167,7 +167,24 @@
                TextFlowUsesContourLine = 3,
                TextFlowUsesImageClipping = 4
        };
+	//enum for actions in updateUndo and restoreEditText
+	typedef enum {
+		NOACTION = -1, //it is for checking if sequence of actions was done - after deselect text frame new sequence will be
+		PARAMFULL = 0, //parameters for whole frame
+		PARAMSEL = 1,  //paramaters for selected text
+		DELSAX = 2,	//delete with styles
+		INSSAX = 3,	//insert with styles
+		REPSAX = 4,	//replace with styles
+		INS = 5		//inserting chars without styles (from keyboard)
+	} EditAct;

+	typedef enum {  // for recognise what was changing during text edition/changing
+		FRAME = 1,
+		PARAGRAPH = 2,
+		SELECTION = 3,
+		CHAR = 4
+	} EditActPlace;
+
        /* these do essentially the same as a dynamic cast but might be more readable */
        virtual PageItem_ImageFrame * asImageFrame() { return NULL; }
        virtual PageItem_Line * asLine() { return NULL; }
@@ -448,6 +465,11 @@
        int CPos;
   /** Text des Elements */
        StoryText itemText;
+  // for itemText undo purpose
+	int oldCPos;
+	QString itemTextSaxed;
+	QString getItemTextSaxed(EditActPlace undoItem);
+	QString getTextSaxed(QString str);
   /** Flag fuer PDF-Bookmark */
        bool isBookmark;
   /** Flag for redraw in EditMode */
@@ -1060,6 +1082,8 @@

        void restoreShapeContour(UndoState *state, bool isUndo);
        void restoreImageEffects(UndoState *state, bool isUndo);
+	void restoreEditText(SimpleState *state, bool isUndo);
+
        /*@}*/

        /**
Index: Scribus/scribus/pageitem_textframe.h
===================================================================
--- Scribus/scribus/pageitem_textframe.h	(wersja 16088)
+++ Scribus/scribus/pageitem_textframe.h	(kopia robocza)
@@ -66,6 +66,7 @@
        void deleteSelectedTextFromFrame();
        void setNewPos(int oldPos, int len, int dir);
        void ExpandSel(int dir, int oldPos);
+	void ExpandParSel(); //expand selection to whole paragrpah(s)
        void deselectAll();

        virtual void invalidateLayout();
@@ -104,7 +105,9 @@
 	void setShadow();
 	QString currentShadow;
 	QMap<QString,StoryText> shadows;
-	
+public:
+	void updateUndo(EditAct action = PARAMFULL, QString str = "");
+	EditAct lastUndoAction;
 private slots:
 	void slotInvalidateLayout();
 };
undoText2.2.patch (57,515 bytes)   

cezaryece

2011-02-09 07:20

updater   ~0025547

subeditor - I create new bug for cursor jumps http://bugs.scribus.net/view.php?id=9735
If you notice this behave also you can describe it there.

I am 100% sure it is not related with text undo patch, but it is still strange issue we must solving.

subeditor

2011-02-09 12:16

reporter   ~0025548

Yes, it's very possible, true reason for posting this issue here was the fact that I avoid text editing withou your patch, so I could catch it only with your patch installed.

plinnell

2011-02-09 15:24

viewer   ~0025550

Ok, the latest patch is in svn rev 16342

plinnell

2011-02-09 15:25

viewer   ~0025551

I am not resolving this, until its out in the wold.

cbradney

2011-03-06 16:55

administrator   ~0025746

Resolving, this is in 1.4.0.rc1

Issue History

Date Modified Username Field Change
2010-12-10 14:59 cezaryece New Issue
2010-12-10 14:59 cezaryece File Added: textUndo.patch
2010-12-10 22:41 subeditor Note Added: 0025058
2010-12-13 08:57 cezaryece Note Added: 0025099
2010-12-13 09:26 cezaryece Note Added: 0025100
2010-12-14 03:58 cbradney Target Version => 1.4.0
2010-12-23 09:39 subeditor Note Added: 0025183
2010-12-23 10:48 subeditor Note Edited: 0025183
2010-12-23 21:49 ale Note Added: 0025188
2010-12-24 09:21 subeditor Note Added: 0025191
2010-12-24 13:12 ale Note Added: 0025192
2010-12-25 00:26 cezaryece Note Added: 0025196
2010-12-25 00:26 cezaryece File Added: undoText2.patch
2010-12-25 18:03 subeditor Note Added: 0025197
2010-12-25 20:37 cezaryece Note Added: 0025198
2010-12-25 21:04 subeditor Note Added: 0025199
2010-12-25 22:11 cezaryece Note Added: 0025200
2010-12-25 22:21 plinnell Assigned To => avox
2010-12-25 22:21 plinnell Status new => assigned
2010-12-25 23:41 subeditor Note Added: 0025201
2010-12-29 08:52 ale Note Added: 0025215
2010-12-30 18:02 cezaryece File Added: undoText2.1.patch
2010-12-30 18:06 cezaryece Note Added: 0025222
2010-12-30 18:23 ale Note Edited: 0025222
2010-12-30 18:29 cezaryece Note Edited: 0025222
2011-01-02 13:02 cbradney Relationship added has duplicate 0009522
2011-01-07 20:13 cbradney Note Added: 0025305
2011-01-10 20:07 plinnell Relationship added has duplicate 0009665
2011-01-12 07:50 cezaryece Note Added: 0025348
2011-01-12 08:20 jghali File Deleted: textUndo.patch
2011-01-12 08:20 jghali File Deleted: undoText2.patch
2011-01-12 08:20 jghali Note Added: 0025352
2011-01-20 12:45 subeditor Note Added: 0025446
2011-01-20 21:55 ale Note Added: 0025453
2011-01-27 09:41 subeditor Note Added: 0025484
2011-01-27 10:15 subeditor Note Added: 0025485
2011-01-28 09:11 ale Note Added: 0025490
2011-01-28 09:35 subeditor Note Added: 0025491
2011-01-30 12:45 cezaryece Note Added: 0025496
2011-01-30 12:45 cezaryece File Added: undoText2.2.patch
2011-01-30 15:31 cezaryece Note Edited: 0025496
2011-01-30 15:46 cbradney File Deleted: undoText2.1.patch
2011-02-09 07:20 cezaryece Note Added: 0025547
2011-02-09 12:16 subeditor Note Added: 0025548
2011-02-09 15:24 plinnell Note Added: 0025550
2011-02-09 15:25 plinnell Note Added: 0025551
2011-02-09 15:25 plinnell Severity text => feature
2011-02-09 15:25 plinnell Resolution open => fixed
2011-02-09 15:25 plinnell Fixed in Version => 1.4.0svn
2011-02-09 15:25 plinnell Target Version 1.4.0 => 1.4.0svn
2011-02-09 17:42 cbradney Relationship added has duplicate 0003301
2011-02-09 17:44 cbradney Relationship added has duplicate 0008744
2011-02-09 17:52 cbradney Relationship added has duplicate 0003445
2011-03-06 16:55 cbradney Note Added: 0025746
2011-03-06 16:55 cbradney Status assigned => resolved
2011-03-06 16:55 cbradney Assigned To avox => plinnell
2011-03-06 16:56 cbradney Issue cloned: 0009802
2011-03-07 19:07 cbradney Status resolved => closed