diff --git a/.gitmodules b/.gitmodules index 729f1ee4c..bcb194467 100644 --- a/.gitmodules +++ b/.gitmodules @@ -33,3 +33,6 @@ [submodule "src/observable"] path = src/observable url = https://github.com/dacap/observable.git +[submodule "src/undo"] + path = src/undo + url = https://github.com/aseprite/undo.git diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 34d830f8a..65ee95ee1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -85,16 +85,20 @@ set(OBSERVABLE_TESTS OFF CACHE BOOL "Compile observable tests") add_subdirectory(observable) include_directories(observable) +# Disable clip examples +set(CLIP_EXAMPLES OFF CACHE BOOL "Compile clip examples") +add_subdirectory(clip) + +# Disable undo tests +set(UNDO_TESTS OFF CACHE BOOL "Compile undo tests") +add_subdirectory(undo) + # Our base library add_subdirectory(base) # Directory where base/config.h file is located include_directories(${BASE_INCLUDE_DIR}) -# Disable clip examples -set(CLIP_EXAMPLES OFF CACHE BOOL "Compile clip examples") -add_subdirectory(clip) - add_subdirectory(cfg) add_subdirectory(css) add_subdirectory(doc) @@ -108,7 +112,6 @@ add_subdirectory(render) add_subdirectory(script) add_subdirectory(she) add_subdirectory(ui) -add_subdirectory(undo) if(ENABLE_UPDATER) add_subdirectory(updater) @@ -178,7 +181,6 @@ if(ENABLE_TESTS) include(FindTests) find_tests(base base-lib) - find_tests(undo undo-lib) find_tests(gfx gfx-lib) find_tests(doc doc-lib) find_tests(render render-lib) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index e4e162b2e..32b7f38d5 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -459,7 +459,7 @@ target_link_libraries(app-lib script-lib she ui-lib - undo-lib + undo ${TINYXML_LIBRARY} ${JPEG_LIBRARIES} ${GIF_LIBRARIES} diff --git a/src/undo b/src/undo new file mode 160000 index 000000000..f39b188e2 --- /dev/null +++ b/src/undo @@ -0,0 +1 @@ +Subproject commit f39b188e29d0f9adaa49c8705c0f492939d967a9 diff --git a/src/undo/CMakeLists.txt b/src/undo/CMakeLists.txt deleted file mode 100644 index 08eec024b..000000000 --- a/src/undo/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Aseprite Undo Library -# Copyright (C) 2015 David Capello - -add_library(undo-lib - undo_history.cpp) diff --git a/src/undo/LICENSE.txt b/src/undo/LICENSE.txt deleted file mode 100644 index 11f1e5e40..000000000 --- a/src/undo/LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2015 David Capello - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/undo/README.md b/src/undo/README.md deleted file mode 100644 index 81ee40992..000000000 --- a/src/undo/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Aseprite Undo Library -*Copyright (C) 2015 David Capello* - -> Distributed under [MIT license](LICENSE.txt) diff --git a/src/undo/undo_command.h b/src/undo/undo_command.h deleted file mode 100644 index d01135f0f..000000000 --- a/src/undo/undo_command.h +++ /dev/null @@ -1,23 +0,0 @@ -// Aseprite Undo Library -// Copyright (C) 2015 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifndef UNDO_UNDO_COMMAND_H_INCLUDED -#define UNDO_UNDO_COMMAND_H_INCLUDED -#pragma once - -namespace undo { - - class UndoCommand { - public: - virtual ~UndoCommand() { } - virtual void undo() = 0; - virtual void redo() = 0; - virtual void dispose() = 0; - }; - -} // namespace undo - -#endif // UNDO_UNDO_COMMAND_H_INCLUDED diff --git a/src/undo/undo_history.cpp b/src/undo/undo_history.cpp deleted file mode 100644 index 0d3420320..000000000 --- a/src/undo/undo_history.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// Aseprite Undo Library -// Copyright (C) 2015 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "undo/undo_history.h" - -#include "undo/undo_command.h" -#include "undo/undo_state.h" - -#include -#include - -namespace undo { - -UndoHistory::UndoHistory() - : m_first(nullptr) - , m_last(nullptr) - , m_cur(nullptr) -{ -} - -UndoHistory::~UndoHistory() -{ - m_cur = nullptr; - clearRedo(); -} - -bool UndoHistory::canUndo() const -{ - return m_cur != nullptr; -} - -bool UndoHistory::canRedo() const -{ - return m_cur != m_last; -} - -void UndoHistory::undo() -{ - assert(m_cur); - if (!m_cur) - return; - - assert( - (m_cur != m_first && m_cur->m_prev) || - (m_cur == m_first && !m_cur->m_prev)); - - moveTo(m_cur->m_prev); -} - -void UndoHistory::redo() -{ - if (!m_cur) - moveTo(m_first); - else - moveTo(m_cur->m_next); -} - -void UndoHistory::clearRedo() -{ - for (UndoState* state = m_last, *prev; - state && state != m_cur; - state = prev) { - prev = state->m_prev; - delete state; - } - - if (m_cur) { - m_cur->m_next = nullptr; - m_last = m_cur; - } - else { - m_first = m_last = nullptr; - } -} - -void UndoHistory::add(UndoCommand* cmd) -{ - UndoState* state = new UndoState(cmd); - state->m_prev = m_last; - state->m_next = nullptr; - state->m_parent = m_cur; - - if (!m_first) - m_first = state; - - m_cur = m_last = state; - - if (state->m_prev) { - assert(!state->m_prev->m_next); - state->m_prev->m_next = state; - } -} - -const UndoState* UndoHistory::findCommonParent(const UndoState* a, - const UndoState* b) -{ - const UndoState* pA = a; - const UndoState* pB = b; - - if (pA == nullptr || pB == nullptr) - return nullptr; - - while (pA != pB) { - pA = pA->m_parent; - if (!pA) { - pA = a; - pB = pB->m_parent; - if (!pB) - return nullptr; - } - } - - return pA; -} - -void UndoHistory::moveTo(const UndoState* new_state) -{ - const UndoState* common = findCommonParent(m_cur, new_state); - - if (m_cur) { - while (m_cur != common) { - m_cur->m_cmd->undo(); - m_cur = m_cur->m_parent; - } - } - - if (new_state) { - std::stack redo_parents; - const UndoState* p = new_state; - while (p != common) { - redo_parents.push(p); - p = p->m_parent; - } - - while (!redo_parents.empty()) { - p = redo_parents.top(); - redo_parents.pop(); - - p->m_cmd->redo(); - } - } - - m_cur = const_cast(new_state); -} - -} // namespace undo diff --git a/src/undo/undo_history.h b/src/undo/undo_history.h deleted file mode 100644 index 53d196239..000000000 --- a/src/undo/undo_history.h +++ /dev/null @@ -1,47 +0,0 @@ -// Aseprite Undo Library -// Copyright (C) 2015 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifndef UNDO_UNDO_HISTORY_H_INCLUDED -#define UNDO_UNDO_HISTORY_H_INCLUDED -#pragma once - -namespace undo { - - class UndoCommand; - class UndoState; - - class UndoHistory { - public: - UndoHistory(); - virtual ~UndoHistory(); - - const UndoState* firstState() const { return m_first; } - const UndoState* lastState() const { return m_last; } - const UndoState* currentState() const { return m_cur; } - - void add(UndoCommand* cmd); - bool canUndo() const; - bool canRedo() const; - void undo(); - void redo(); - void clearRedo(); - - // This can be used to jump to a specific UndoState in the whole - // history. - void moveTo(const UndoState* new_state); - - private: - const UndoState* findCommonParent(const UndoState* a, - const UndoState* b); - - UndoState* m_first; - UndoState* m_last; - UndoState* m_cur; // Current action that can be undone - }; - -} // namespace undo - -#endif // UNDO_UNDO_HISTORY_H_INCLUDED diff --git a/src/undo/undo_state.h b/src/undo/undo_state.h deleted file mode 100644 index b23fe5390..000000000 --- a/src/undo/undo_state.h +++ /dev/null @@ -1,45 +0,0 @@ -// Aseprite Undo Library -// Copyright (C) 2015 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifndef UNDO_UNDO_STATE_H_INCLUDED -#define UNDO_UNDO_STATE_H_INCLUDED -#pragma once - -#include "undo/undo_command.h" - -namespace undo { - - class UndoCommand; - class UndoHistory; - - // Represents a state that can be undone. If we are in this state, - // is because the command was already executed. - class UndoState { - friend class UndoHistory; - public: - UndoState(UndoCommand* cmd) - : m_prev(nullptr) - , m_next(nullptr) - , m_parent(nullptr) - , m_cmd(cmd) { - } - ~UndoState() { - if (m_cmd) - m_cmd->dispose(); - } - UndoState* prev() const { return m_prev; } - UndoState* next() const { return m_next; } - UndoCommand* cmd() const { return m_cmd; } - private: - UndoState* m_prev; - UndoState* m_next; - UndoState* m_parent; // Parent state, after we undo - UndoCommand* m_cmd; - }; - -} // namespace undo - -#endif // UNDO_UNDO_STATE_H_INCLUDED diff --git a/src/undo/undo_tests.cpp b/src/undo/undo_tests.cpp deleted file mode 100644 index fce7808d6..000000000 --- a/src/undo/undo_tests.cpp +++ /dev/null @@ -1,182 +0,0 @@ -// Aseprite Undo Library -// Copyright (C) 2015-2016 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "undo/undo_command.h" -#include "undo/undo_history.h" - -using namespace undo; - -class Cmd : public UndoCommand { -public: - Cmd(int& model, int redo_value, int undo_value) - : m_model(model) - , m_redo_value(redo_value) - , m_undo_value(undo_value) { - } - void redo() override { m_model = m_redo_value; } - void undo() override { m_model = m_undo_value; } - void dispose() override { } -private: - int& m_model; - int m_redo_value; - int m_undo_value; -}; - -TEST(Undo, Basics) -{ - int model = 0; - Cmd cmd1(model, 1, 0); - Cmd cmd2(model, 2, 1); - - EXPECT_EQ(0, model); - cmd1.redo(); - EXPECT_EQ(1, model); - cmd2.redo(); - EXPECT_EQ(2, model); - - UndoHistory history; - EXPECT_FALSE(history.canUndo()); - EXPECT_FALSE(history.canRedo()); - history.add(&cmd1); - EXPECT_TRUE(history.canUndo()); - EXPECT_FALSE(history.canRedo()); - history.add(&cmd2); - EXPECT_TRUE(history.canUndo()); - EXPECT_FALSE(history.canRedo()); - - history.undo(); - EXPECT_EQ(1, model); - EXPECT_TRUE(history.canUndo()); - EXPECT_TRUE(history.canRedo()); - history.undo(); - EXPECT_EQ(0, model); - EXPECT_FALSE(history.canUndo()); - EXPECT_TRUE(history.canRedo()); - - history.redo(); - EXPECT_EQ(1, model); - EXPECT_TRUE(history.canUndo()); - EXPECT_TRUE(history.canRedo()); - history.redo(); - EXPECT_EQ(2, model); - EXPECT_TRUE(history.canUndo()); - EXPECT_FALSE(history.canRedo()); -} - -TEST(Undo, Tree) -{ - // 1 --- 2 - // | - // ------ 3 --- 4 - int model = 0; - Cmd cmd1(model, 1, 0); - Cmd cmd2(model, 2, 1); - Cmd cmd3(model, 3, 1); - Cmd cmd4(model, 4, 3); - - UndoHistory history; - cmd1.redo(); history.add(&cmd1); - cmd2.redo(); history.add(&cmd2); - history.undo(); - cmd3.redo(); history.add(&cmd3); // Creates a branch in the history - cmd4.redo(); history.add(&cmd4); - - EXPECT_EQ(4, model); - history.undo(); - EXPECT_EQ(3, model); - history.undo(); - EXPECT_EQ(2, model); - history.undo(); - EXPECT_EQ(1, model); - history.undo(); - EXPECT_EQ(0, model); - EXPECT_FALSE(history.canUndo()); - history.redo(); - EXPECT_EQ(1, model); - history.redo(); - EXPECT_EQ(2, model); - history.redo(); - EXPECT_EQ(3, model); - history.redo(); - EXPECT_EQ(4, model); - EXPECT_FALSE(history.canRedo()); -} - -TEST(Undo, ComplexTree) -{ - // 1 --- 2 --- 3 --- 4 ------ 7 --- 8 - // | | - // ------------- 5 --- 6 - int model = 0; - Cmd cmd1(model, 1, 0); - Cmd cmd2(model, 2, 1); - Cmd cmd3(model, 3, 2); - Cmd cmd4(model, 4, 3); - Cmd cmd5(model, 5, 2); - Cmd cmd6(model, 6, 5); - Cmd cmd7(model, 7, 5); - Cmd cmd8(model, 8, 7); - - UndoHistory history; - cmd1.redo(); history.add(&cmd1); - cmd2.redo(); history.add(&cmd2); - cmd3.redo(); history.add(&cmd3); - cmd4.redo(); history.add(&cmd4); - history.undo(); - cmd5.redo(); history.add(&cmd5); - cmd6.redo(); history.add(&cmd6); - history.undo(); - cmd7.redo(); history.add(&cmd7); - cmd8.redo(); history.add(&cmd8); - - EXPECT_EQ(8, model); - history.undo(); - EXPECT_EQ(7, model); - history.undo(); - EXPECT_EQ(6, model); - history.undo(); - EXPECT_EQ(5, model); - history.undo(); - EXPECT_EQ(4, model); - history.undo(); - EXPECT_EQ(3, model); - history.undo(); - EXPECT_EQ(2, model); - history.undo(); - EXPECT_EQ(1, model); - history.undo(); - EXPECT_EQ(0, model); - EXPECT_FALSE(history.canUndo()); - history.redo(); - EXPECT_EQ(1, model); - history.redo(); - EXPECT_EQ(2, model); - history.redo(); - EXPECT_EQ(3, model); - history.redo(); - EXPECT_EQ(4, model); - history.redo(); - EXPECT_EQ(5, model); - history.redo(); - EXPECT_EQ(6, model); - history.redo(); - EXPECT_EQ(7, model); - history.redo(); - EXPECT_EQ(8, model); - EXPECT_FALSE(history.canRedo()); -} - -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}