<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Recent changes to Home</title><link>https://sourceforge.net/p/undoredomanager/wiki/Home/</link><description>Recent changes to Home</description><atom:link href="https://sourceforge.net/p/undoredomanager/wiki/Home/feed" rel="self"/><language>en</language><lastBuildDate>Mon, 30 May 2022 11:50:19 -0000</lastBuildDate><atom:link href="https://sourceforge.net/p/undoredomanager/wiki/Home/feed" rel="self" type="application/rss+xml"/><item><title>Home modified by Miguel Alejan. Moreno Barrientos</title><link>https://sourceforge.net/p/undoredomanager/wiki/Home/</link><description>&lt;div class="markdown_content"&gt;&lt;pre&gt;--- v5
+++ v6
@@ -2,7 +2,7 @@

 1. **UndoRedoable**:  derived classes must implement **#undo**, **#redo** methods to define invertible actions
 2. **UndoRedoTransaction**: defines a undoredoable **transaction** *(multiple actions treated atomically)*. Main method **#add** for adding single actions or another transactions to this one
-3. **UndoRedoManager**: stack with all added undoredoable actions and transactions. Main method **#addItem** for adding new single actions or transactions. **subscribers** property for adding subscribers on change
+3. **UndoRedoManager**: stack with all added undoredoable actions and transactions. Main method **#addItem** for adding new single actions or transactions


 -----
&lt;/pre&gt;
&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Miguel Alejan. Moreno Barrientos</dc:creator><pubDate>Mon, 30 May 2022 11:50:19 -0000</pubDate><guid>https://sourceforge.net2846d49f904ef3d1c3cde5b778d0b0b9084e2a7e</guid></item><item><title>Home modified by Miguel Alejan. Moreno Barrientos</title><link>https://sourceforge.net/p/undoredomanager/wiki/Home/</link><description>&lt;div class="markdown_content"&gt;&lt;pre&gt;--- v4
+++ v5
@@ -2,7 +2,7 @@

 1. **UndoRedoable**:  derived classes must implement **#undo**, **#redo** methods to define invertible actions
 2. **UndoRedoTransaction**: defines a undoredoable **transaction** *(multiple actions treated atomically)*. Main method **#add** for adding single actions or another transactions to this one
-3. **UndoRedoManager**: queue with all added undoredoable actions and transactions. Main method **#addItem** for adding new single actions or transactions
+3. **UndoRedoManager**: stack with all added undoredoable actions and transactions. Main method **#addItem** for adding new single actions or transactions. **subscribers** property for adding subscribers on change


 -----
&lt;/pre&gt;
&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Miguel Alejan. Moreno Barrientos</dc:creator><pubDate>Mon, 30 May 2022 07:26:37 -0000</pubDate><guid>https://sourceforge.net9e4bdcf0e2ff7e7435d4a7a5989982f80a76cda6</guid></item><item><title>Home modified by Miguel Alejan. Moreno Barrientos</title><link>https://sourceforge.net/p/undoredomanager/wiki/Home/</link><description>&lt;div class="markdown_content"&gt;&lt;pre&gt;--- v3
+++ v4
@@ -242,15 +242,15 @@
 ~~~

 &amp;gt; **Output:**
-*charBuffer: "Lorem ipsum"
+charBuffer: "Lorem ipsum"
 charBuffer: "Lorem ipsum, consectetur adipiscing elit."
 charBuffer: "Lorem ipsum, consectetur adipiscing elit."
 charBuffer: "Lorem ipsum, consectetur adipiscing elit."
 charBuffer: "Hi World!!"
 charBuffer: "Lorem ipsum"
 charBuffer: "Hi World!!"
-*
-        ...
+
+        


 * * *
&lt;/pre&gt;
&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Miguel Alejan. Moreno Barrientos</dc:creator><pubDate>Sat, 28 May 2022 22:21:07 -0000</pubDate><guid>https://sourceforge.net1d47b29d1eac78882ba54b81e6348f1aa73e97a1</guid></item><item><title>Home modified by Miguel Alejan. Moreno Barrientos</title><link>https://sourceforge.net/p/undoredomanager/wiki/Home/</link><description>&lt;div class="markdown_content"&gt;&lt;pre&gt;--- v2
+++ v3
@@ -1,10 +1,8 @@
 The library consists of three clases:

-**UndoRedoable**:  derived classes must implement **#undo**, **#redo** methods to define invertible actions
-|
-+-- **UndoRedoTransaction**: defines a undoredoable **transaction** *(multiple actions treated atomically)*. Main method **#add** for adding single actions or another transactions to this one
-|
-+-- **UndoRedoManager**: queue with all added undoredoable actions and transactions. Main method **#addItem** for adding new single actions or transactions
+1. **UndoRedoable**:  derived classes must implement **#undo**, **#redo** methods to define invertible actions
+2. **UndoRedoTransaction**: defines a undoredoable **transaction** *(multiple actions treated atomically)*. Main method **#add** for adding single actions or another transactions to this one
+3. **UndoRedoManager**: queue with all added undoredoable actions and transactions. Main method **#addItem** for adding new single actions or transactions


 -----
&lt;/pre&gt;
&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Miguel Alejan. Moreno Barrientos</dc:creator><pubDate>Sat, 28 May 2022 20:14:01 -0000</pubDate><guid>https://sourceforge.net53abb5ef362677005ff3fdc9f20f84f8ff7168d5</guid></item><item><title>Home modified by Miguel Alejan. Moreno Barrientos</title><link>https://sourceforge.net/p/undoredomanager/wiki/Home/</link><description>&lt;div class="markdown_content"&gt;&lt;pre&gt;--- v1
+++ v2
@@ -1,8 +1,450 @@
-Welcome to your wiki!
-
-This is the default page, edit it as you see fit. To add a new page simply reference it within brackets, e.g.: [SamplePage].
-
-The wiki uses [Markdown](/p/undoredomanager/wiki/markdown_syntax/) syntax.
+The library consists of three clases:
+
+**UndoRedoable**:  derived classes must implement **#undo**, **#redo** methods to define invertible actions
+|
++-- **UndoRedoTransaction**: defines a undoredoable **transaction** *(multiple actions treated atomically)*. Main method **#add** for adding single actions or another transactions to this one
+|
++-- **UndoRedoManager**: queue with all added undoredoable actions and transactions. Main method **#addItem** for adding new single actions or transactions
+
+
+-----
+
+
+####***Example with StringBuilder***
+
+In this test example, a **StringBuilder** is modified and changes are registered in the undo-redo manger using three derived classes from **UndoRedoable** ;
+1. *UndoRedoableAppendText*
+2. *UndoRedoableInsertText*
+3. *UndoRedoableClearText*
+
+~~~ kotlin
+import org.junit.jupiter.api.*
+import undoredomanager.UndoRedoManager
+import undoredomanager.UndoRedoTransaction
+import undoredomanager.UndoRedoable
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+private val urm = UndoRedoManager()
+private val charBuffer = StringBuilder()
+
+@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+class TestUndoRedoManager
+{
+    @Test
+    @Order(1)
+    fun append1()
+    {
+        assertTrue { charBuffer.isEmpty() }
+
+        ////////////////// UndoRedoable Action ///////////////////
+        "Lorem ipsum".also {
+            charBuffer.append( it )
+            urm.addItem( UndoRedoableAppendText( charBuffer, it ) )
+        }
+        //////////////////////////////////////////////////////////
+
+        assertTrue( urm.getDescription().also { urm.undo() } ) { charBuffer.isEmpty() }
+        urm.redoDescription().also {
+            urm.redo()
+            assertEquals( "Lorem ipsum", charBuffer.toString(), it )
+        }
+
+    }
+
+    @Test
+    @Order(2)
+    fun append2()
+    {
+        assertTrue { charBuffer.isNotEmpty() }
+
+        ////////////////// UndoRedoable Action ///////////////////
+        ", consectetur adipiscing elit.".also {
+            charBuffer.append( it )
+            urm.addItem( UndoRedoableAppendText( charBuffer, it ) )
+        }
+        //////////////////////////////////////////////////////////
+
+        urm.undoDescription().also {
+            urm.undo()
+            assertEquals( "Lorem ipsum", charBuffer.toString(), it )
+        }
+        urm.redoDescription().also {
+            urm.redo()
+            assertEquals( "Lorem ipsum, consectetur adipiscing elit.", charBuffer.toString(), it )
+        }
+    }
+
+    @Test
+    @Order(3)
+    fun clear1()
+    {
+        assertTrue { charBuffer.isNotEmpty() }
+
+        ////////////////// UndoRedoable Action ///////////////////
+        urm.addItem( UndoRedoableClearText( charBuffer ) )  // must be before clear buffer
+        charBuffer.clear()
+        //////////////////////////////////////////////////////////
+
+        assertTrue( urm.getDescription() ) { charBuffer.isEmpty() }
+        urm.undoDescription().also {
+            urm.undo()
+            assertEquals( "Lorem ipsum, consectetur adipiscing elit.", charBuffer.toString(), it )
+        }
+        assertTrue( urm.redoDescription().also { urm.redo() } ) { charBuffer.isEmpty() }
+        urm.undoDescription().also {
+            urm.undo()
+            assertEquals( "Lorem ipsum, consectetur adipiscing elit.", charBuffer.toString(), it )
+        }
+    }
+
+    @Test
+    @Order(4)
+    fun insert1()
+    {
+        assertTrue { charBuffer.isNotEmpty() }
+
+        ////////////////// UndoRedoable Action ///////////////////
+        "FOO BAR".also {
+            charBuffer.insert( 12, it )
+            urm.addItem( UndoRedoableInsertText( charBuffer, 12, it ) )
+        }
+        //////////////////////////////////////////////////////////
+
+        urm.undoDescription().also {
+            urm.undo()
+            assertEquals( "Lorem ipsum, consectetur adipiscing elit.", charBuffer.toString(), it )
+        }
+        urm.redoDescription().also {
+            urm.redo()
+            assertEquals( "Lorem ipsum,FOO BAR consectetur adipiscing elit.", charBuffer.toString(), it )
+        }
+        urm.undoDescription().also {
+            urm.undo()
+            assertEquals( "Lorem ipsum, consectetur adipiscing elit.", charBuffer.toString(), it )
+        }
+    }
+
+    @Test
+    @Order(5)
+    fun transaction1()
+    {
+        assertTrue { charBuffer.isNotEmpty() }
+
+        val txt = "Hi World!!"
+
+        ////////////////// UndoRedoable Transaction ///////////////////
+        val transaction = object: UndoRedoTransaction() {
+            override fun getDescription() = "Clear and append $txt"
+            override fun redoDescription() = "Redo ${getDescription()}"
+            override fun undoDescription() = "Undo ${getDescription()}"
+        }
+        transaction.add( UndoRedoableClearText( charBuffer ) )  // must be before clear buffer
+        charBuffer.clear()
+        charBuffer.append( txt )
+        transaction.add( UndoRedoableAppendText( charBuffer, txt ) )
+        urm.addItem( transaction )
+        //////////////////////////////////////////////////////////////
+
+        assertEquals( txt, charBuffer.toString(), urm.getDescription() )
+
+        urm.undoDescription().also {
+            urm.undo()
+            assertEquals( "Lorem ipsum, consectetur adipiscing elit.", charBuffer.toString(), it )
+        }
+        urm.redoDescription().also {
+            urm.redo()
+            assertEquals( txt, charBuffer.toString(), it )
+        }
+    }
+
+    @Test
+    @Order(6)
+    fun twoUndos1()
+    {
+        assertTrue { charBuffer.isNotEmpty() }
+
+        repeat(2) { urm.undo() }
+
+        assertEquals( "Lorem ipsum", charBuffer.toString() )
+    }
+
+    @Test
+    @Order(7)
+    fun twoRedos1()
+    {
+        assertTrue { charBuffer.isNotEmpty() }
+
+        repeat(2) { urm.redo() }
+
+        assertEquals( "Hi World!!", charBuffer.toString() )
+    }
+
+    @AfterEach
+    fun afterEach()
+    {
+        println( "charBuffer: \"$charBuffer\"" )
+    }
+
+    @AfterAll
+    fun afterAll()
+    {
+        urm.clearAll()
+    }
+}
+
+class UndoRedoableAppendText( private val buffer: StringBuilder, private val txt: String ) : UndoRedoable()
+{
+    override fun getDescription() = "append \"$txt\""
+
+    override fun redo()
+    {
+        buffer.append( txt )
+    }
+
+    override fun undo()
+    {
+        buffer.delete( buffer.length - txt.length, buffer.length )
+    }
+}
+
+class UndoRedoableInsertText( private val buffer: StringBuilder, private val idx: Int = 0, private val txt: String )
+    : UndoRedoable()
+{
+    override fun getDescription() = "insert in $idx the text \"$txt\""
+
+    override fun redo()
+    {
+        buffer.insert( idx, txt )
+    }
+
+    override fun undo()
+    {
+        buffer.delete( idx, idx + txt.length )
+    }
+}
+
+class UndoRedoableClearText( private val buffer: StringBuilder ) : UndoRedoable()
+{
+    private val oldBuffer = StringBuilder( buffer )
+
+    override fun getDescription() = "clear buffer"
+
+    override fun redo()
+    {
+        buffer.clear()
+    }
+
+    override fun undo()
+    {
+        buffer.append( oldBuffer )
+    }
+}
+~~~
+
+&amp;gt; **Output:**
+*charBuffer: "Lorem ipsum"
+charBuffer: "Lorem ipsum, consectetur adipiscing elit."
+charBuffer: "Lorem ipsum, consectetur adipiscing elit."
+charBuffer: "Lorem ipsum, consectetur adipiscing elit."
+charBuffer: "Hi World!!"
+charBuffer: "Lorem ipsum"
+charBuffer: "Hi World!!"
+*
+        ...
+
+
+* * *
+
+
+
+
+####Source code (included in jar)
+
+
+~~~ kotlin
+///////////////////////////////////////////////////////////////////////////
+// UndoRedoManager is a small library to implement an undo&amp;amp;redo system
+// Copyright (C) 2022  Miguel Alejandro Moreno Barrientos
+//
+// UndoRedoManager is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// UndoRedoManager is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see &amp;lt;https: www.gnu.org="" licenses=""&amp;gt;.
+///////////////////////////////////////////////////////////////////////////
+
+package undoredomanager
+
+
+typealias SubscriberAction = (UndoRedoable?) -&amp;gt; Unit
+
+/**
+ * Base class for any UndoRedoable action
+ */
+abstract class UndoRedoable
+{
+    val subscribers = mutableListOf&amp;lt;subscriberaction&amp;gt;()
+
+    abstract fun undo()
+
+    abstract fun redo()
+
+    open fun canUndo() = true
+
+    open fun canRedo() = true
+
+    open fun undoDescription() = "Undo ${getDescription()}"
+
+    open fun redoDescription() = "Redo ${getDescription()}"
+
+    abstract fun getDescription(): String
+
+    fun notifySubscribers() = subscribers.forEach { it( this ) }
+
+    override fun toString() = "UndoRedoable(undoDescription=${undoDescription()}," +
+                              "redoDescription=${redoDescription()}," +
+                              "canUndo=${canUndo()}," +
+                              "canRedo=${canRedo()})"
+}
+
+/**
+ * Transaction for grouping several UndoRedoables.
+ */
+open class UndoRedoTransaction private constructor( private val list: MutableList&amp;lt;undoredoable&amp;gt; )
+    : MutableList&amp;lt;undoredoable&amp;gt; by list, UndoRedoable()
+{
+    constructor(): this( mutableListOf() )
+
+    private var undone = false
+
+    final override fun undo()
+    {
+        if ( canUndo() )
+        {
+            for ( undoRedoable in reversed() )
+                undoRedoable.undo()
+            undone = true
+        }
+        else
+            throw IllegalStateException( "Can't undo" )
+    }
+
+    final override fun redo()
+    {
+        if ( canRedo() )
+        {
+            for ( undoRedoable in this )
+                undoRedoable.redo()
+            undone = false
+        }
+        else
+            throw IllegalStateException( "Can't redo" )
+    }
+
+    final override fun canUndo() = isNotEmpty() &amp;amp;&amp;amp; !undone
+
+    final override fun canRedo() = isNotEmpty() &amp;amp;&amp;amp; undone
+
+    override fun undoDescription() = if ( isNotEmpty() ) last().undoDescription() else "Empty transaction"
+
+    override fun redoDescription() = if ( isNotEmpty() ) last().redoDescription() else "Empty transaction"
+
+    override fun getDescription() = if ( isNotEmpty() ) last().getDescription() else "Empty transaction"
+
+    override fun toString()
+            = "UndoRedoTransaction(undoDescription=${undoDescription()}, " +
+            "redoDescription=${redoDescription()}, " +
+            "list=$list, " +
+            "canUndo=${canUndo()}, " +
+            "canRedo=${canRedo()}, " +
+            "subscribers=$subscribers)"
+
+}  // Class UndoRedoTransaction
+
+
+/**
+ * Main class to manage the undo-redo system. ***Note: use implemented methods to modify the manager, to use list methods
+ * will cause unexpected behaviour***
+ * @param limit maximum number of UndoRedoable actions.
+ *                 If this number is exceeded, first actions are removed (limited queue)
+ */
+open class UndoRedoManager private constructor( private val list: MutableList&amp;lt;undoredoable&amp;gt;,
+                                                private var limit: Int )
+    : MutableList&amp;lt;undoredoable&amp;gt; by list, UndoRedoable()
+{
+    constructor( limit: Int = Int.MAX_VALUE ): this( mutableListOf(), limit )
+
+    private var index = list.size - 1
+
+    final override fun undo()
+    {
+        if ( canUndo() )
+            this[index--].undo()
+        else
+            throw IllegalStateException( "Can't undo" )
+
+        notifySubscribers()
+    }
+
+    final override fun redo()
+    {
+        if ( canRedo() )
+            this[++index].redo()
+        else
+            throw IllegalStateException( "Can't redo" )
+
+        notifySubscribers()
+    }
+
+    final override fun canUndo() = isNotEmpty() &amp;amp;&amp;amp; index &amp;gt;= 0 &amp;amp;&amp;amp; this[index].canUndo()
+
+    final override fun canRedo() = isNotEmpty() &amp;amp;&amp;amp; index &amp;lt; size-1 &amp;amp;&amp;amp; this[index+1].canRedo()
+
+    override fun undoDescription()
+            = if ( canUndo() ) this[index].undoDescription() else "Can´t undo"
+
+    override fun redoDescription()
+            = if ( canRedo() ) this[index+1].redoDescription() else "Can't redo"
+
+    override fun getDescription() = if ( isNotEmpty() &amp;amp;&amp;amp; index &amp;gt;= 0 ) this[index].getDescription()
+                                    else "Empty or rewound manager"
+
+    fun addItem( undoRedo: UndoRedoable)
+    {
+        subList( index + 1, size ).clear()
+
+        if ( size &amp;gt;= limit )
+            subList( 0, size - limit + 1 ).clear()
+
+        add( undoRedo )
+        index = size - 1
+
+        notifySubscribers()
+    }
+
+    fun clearAll()
+    {
+        clear()
+        index = size - 1
+
+        notifySubscribers()
+    }
+
+    override fun toString() = "UndoRedoManager(list=$list, limit=$limit, canUndo=${canUndo()}, canRedo=${canRedo()})"
+
+}  // class UndoRedoManager
+~~~ 
+
+

 [[members limit=20]]
 [[download_button]]
&amp;lt;/undoredoable&amp;gt;&amp;lt;/undoredoable&amp;gt;&amp;lt;/undoredoable&amp;gt;&amp;lt;/undoredoable&amp;gt;&amp;lt;/subscriberaction&amp;gt;&amp;lt;/https:&amp;gt;&lt;/pre&gt;
&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Miguel Alejan. Moreno Barrientos</dc:creator><pubDate>Sat, 28 May 2022 19:24:49 -0000</pubDate><guid>https://sourceforge.net714b1efb11fd766c9caac79e2a4517dbe9ef56c4</guid></item><item><title>Home modified by Miguel Alejan. Moreno Barrientos</title><link>https://sourceforge.net/p/undoredomanager/wiki/Home/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;Welcome to your wiki!&lt;/p&gt;
&lt;p&gt;This is the default page, edit it as you see fit. To add a new page simply reference it within brackets, e.g.: &lt;span&gt;[SamplePage]&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The wiki uses &lt;a class="" href="/p/undoredomanager/wiki/markdown_syntax/" rel="nofollow"&gt;Markdown&lt;/a&gt; syntax.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;h6&gt;Project Members:&lt;/h6&gt;
	&lt;ul class="md-users-list"&gt;
		&lt;li&gt;&lt;a href="/u/mamb/"&gt;Miguel Alejan. Moreno Barrientos&lt;/a&gt; (admin)&lt;/li&gt;
		
	&lt;/ul&gt;&lt;br/&gt;
&lt;p&gt;&lt;span class="download-button-62925e24884e258d654585b2" style="margin-bottom: 1em; display: block;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Miguel Alejan. Moreno Barrientos</dc:creator><pubDate>Sat, 28 May 2022 17:38:44 -0000</pubDate><guid>https://sourceforge.netb01a964894c963cafeaa4e4cd9aef63a19c513f4</guid></item></channel></rss>