diff --git a/src/db/db/dbManager.cc b/src/db/db/dbManager.cc index 499abaef7..a49555e1f 100644 --- a/src/db/db/dbManager.cc +++ b/src/db/db/dbManager.cc @@ -283,7 +283,7 @@ Manager::redo () } } -std::pair +std::pair Manager::available_undo () const { if (m_opened || m_current == m_transactions.begin ()) { @@ -305,6 +305,71 @@ Manager::available_redo () const } } +int +Manager::available_undo_items () +{ + if (m_opened) { + return 0; + } + + int n = 0; + for (auto i = m_current; i != m_transactions.begin (); --i) { + ++n; + } + return n; +} + +int +Manager::available_redo_items () +{ + if (m_opened) { + return 0; + } + + int n = 0; + for (auto i = m_current; i != m_transactions.end (); ++i) { + ++n; + } + return n; +} + +std::string +Manager::undo_or_redo_item (int delta) const +{ + if (delta < 0) { + + auto i = m_current; + while (delta < 0) { + if (i == m_transactions.begin ()) { + return std::string (); + } + ++delta; + --i; + } + + return i->second; + + } else { + + auto i = m_current; + while (delta > 0) { + if (i == m_transactions.end ()) { + return std::string (); + } + --delta; + ++i; + } + + if (i == m_transactions.end ()) { + return std::string (); + } else { + return i->second; + } + + } +} + + db::Op * Manager::last_queued (db::Object *object) { diff --git a/src/db/db/dbManager.h b/src/db/db/dbManager.h index fbeb8b122..c57f2243c 100644 --- a/src/db/db/dbManager.h +++ b/src/db/db/dbManager.h @@ -202,6 +202,29 @@ class DB_PUBLIC Manager */ std::pair available_redo () const; + /** + * @brief Gets the number of available undo items + */ + int available_undo_items (); + + /** + * @brief Gets the number of available redo items + */ + int available_redo_items (); + + /** + * @brief Gets an item from the list + * + * @param delta A positive value or 0 for the nth redo item, A negative value for the nth undo item + * + * A delta of "0" will give you the next redo item, a delta of "1" the second next one. + * A delta of "-1" will give you the first undo item. + * delta must be less than "available_redo_items" and larger or equal than "-available_undo_items". + * + * @return The description of the transaction + */ + std::string undo_or_redo_item (int delta) const; + /** * @brief Queue a operation for undo * diff --git a/src/lay/lay/gsiDeclLayMainWindow.cc b/src/lay/lay/gsiDeclLayMainWindow.cc index 39cecc9e3..a3500ef9c 100644 --- a/src/lay/lay/gsiDeclLayMainWindow.cc +++ b/src/lay/lay/gsiDeclLayMainWindow.cc @@ -51,6 +51,8 @@ static const char *cm_symbols[] = { "cm_unselect_all", "cm_undo", "cm_redo", + "cm_undo_list", + "cm_redo_list", "cm_delete", "cm_show_properties", "cm_copy", diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 061fda5c6..dce7dd237 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -1707,6 +1707,27 @@ MainWindow::cm_undo () } } +void +MainWindow::cm_undo_list () +{ + if (current_view () && m_manager.available_undo ().first) { + + std::unique_ptr dialog (new lay::UndoRedoListForm (this, &m_manager, true)); + + int steps = 0; + if (dialog->exec (steps)) { + for (std::vector ::iterator vp = mp_views.begin (); vp != mp_views.end (); ++vp) { + (*vp)->view ()->clear_selection (); + (*vp)->view ()->cancel (); + } + while (steps-- > 0) { + m_manager.undo (); + } + } + + } +} + void MainWindow::cm_redo () { @@ -1719,6 +1740,27 @@ MainWindow::cm_redo () } } +void +MainWindow::cm_redo_list () +{ + if (current_view () && m_manager.available_redo ().first) { + + std::unique_ptr dialog (new lay::UndoRedoListForm (this, &m_manager, false)); + + int steps = 0; + if (dialog->exec (steps)) { + for (std::vector ::iterator vp = mp_views.begin (); vp != mp_views.end (); ++vp) { + (*vp)->view ()->clear_selection (); + (*vp)->view ()->cancel (); + } + while (steps-- > 0) { + m_manager.redo (); + } + } + + } +} + void MainWindow::cm_goto_position () { @@ -1847,6 +1889,11 @@ MainWindow::update_action_states () undo_action->set_title (undo_txt); undo_action->set_enabled (undo_enable && edits_enabled ()); + if (menu ()->is_valid ("edit_menu.undo_list")) { + Action *undo_list_action = menu ()->action ("edit_menu.undo_list"); + undo_list_action->set_enabled (undo_enable && edits_enabled ()); + } + } if (menu ()->is_valid ("edit_menu.redo")) { @@ -1862,6 +1909,11 @@ MainWindow::update_action_states () redo_action->set_title (redo_txt); redo_action->set_enabled (redo_enable && edits_enabled ()); + if (menu ()->is_valid ("edit_menu.redo_list")) { + Action *redo_list_action = menu ()->action ("edit_menu.redo_list"); + redo_list_action->set_enabled (redo_enable && edits_enabled ()); + } + } if (menu ()->is_valid ("edit_menu.paste")) { @@ -3922,6 +3974,10 @@ MainWindow::menu_activated (const std::string &symbol) cm_undo (); } else if (symbol == "cm_redo") { cm_redo (); + } else if (symbol == "cm_undo_list") { + cm_undo_list (); + } else if (symbol == "cm_redo_list") { + cm_redo_list (); } else if (symbol == "cm_goto_position") { cm_goto_position (); } else if (symbol == "cm_manage_bookmarks") { diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 64e457c89..90e3f2693 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -800,6 +800,8 @@ protected slots: void cm_reset_window_state (); void cm_undo (); void cm_redo (); + void cm_undo_list (); + void cm_redo_list (); void cm_goto_position (); void cm_manage_bookmarks (); void cm_bookmark_view (); diff --git a/src/layui/layui/UndoRedoListForm.ui b/src/layui/layui/UndoRedoListForm.ui new file mode 100644 index 000000000..a5017a9e6 --- /dev/null +++ b/src/layui/layui/UndoRedoListForm.ui @@ -0,0 +1,97 @@ + + + UndoRedoListForm + + + + 0 + 0 + 307 + 320 + + + + Undo / Redo by List + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + (tbd) + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + true + + + + + + + + buttonBox + + + + + buttonBox + accepted() + UndoRedoListForm + accept() + + + 211 + 282 + + + 211 + 152 + + + + + buttonBox + rejected() + UndoRedoListForm + reject() + + + 211 + 282 + + + 211 + 152 + + + + + diff --git a/src/layui/layui/layDialogs.cc b/src/layui/layui/layDialogs.cc index b1c60d1c4..0c40c089d 100644 --- a/src/layui/layui/layDialogs.cc +++ b/src/layui/layui/layDialogs.cc @@ -55,6 +55,7 @@ #include "ui_FlattenInstOptionsDialog.h" #include "ui_UserPropertiesForm.h" #include "ui_UserPropertiesEditForm.h" +#include "ui_UndoRedoListForm.h" #include #include @@ -1447,6 +1448,142 @@ BEGIN_PROTECTED END_PROTECTED } +// ---------------------------------------------------------------------- +// UndoRedoListForm implementation + +namespace { + +class UndoRedoListViewModel + : public QAbstractListModel +{ +public: + UndoRedoListViewModel (QObject *parent, db::Manager *manager, bool for_undo) + : QAbstractListModel (parent), mp_manager (manager), m_for_undo (for_undo), m_step (-1) + { + // .. nothing yet .. + } + + int columnCount (const QModelIndex &) const + { + return 1; + } + + QVariant data (const QModelIndex &index, int role) const + { + if (role == Qt::DisplayRole) { + int delta; + if (m_for_undo) { + delta = -(index.row () + 1); + } else { + delta = index.row (); + } + return QVariant (tl::to_qstring (mp_manager->undo_or_redo_item (delta))); + } else if (role == Qt::FontRole) { + if (index.row () <= m_step) { + QFont font; + font.setBold (true); + return QVariant (font); + } + } + return QVariant (); + } + + QModelIndex index (int row, int column, const QModelIndex &parent) const + { + if (parent.isValid ()) { + return QModelIndex (); + } else { + return createIndex (row, column); + } + } + + QModelIndex parent(const QModelIndex &) const + { + return QModelIndex (); + } + + int rowCount (const QModelIndex &parent) const + { + if (parent.isValid ()) { + return 0; + } else { + return m_for_undo ? mp_manager->available_undo_items () : mp_manager->available_redo_items (); + } + } + + void set_current_step (int step) + { + if (m_step != step) { + m_step = step; + emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ())); + } + } + +private: + db::Manager *mp_manager; + bool m_for_undo; + int m_step; +}; + +} + +UndoRedoListForm::UndoRedoListForm (QWidget *parent, db::Manager *manager, bool for_undo) + : QDialog (parent), m_for_undo (for_undo), mp_manager (manager) +{ + setObjectName (QString::fromUtf8 ("undo_redo_list_form")); + + mp_ui = new Ui::UndoRedoListForm (); + mp_ui->setupUi (this); + + setWindowTitle (for_undo ? tr ("Undo By List") : tr ("Redo By List")); + + mp_ui->items->setModel (new UndoRedoListViewModel (mp_ui->items, manager, for_undo)); + + connect (mp_ui->items->selectionModel (), SIGNAL (currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT (selection_changed(const QModelIndex &))); + selection_changed (QModelIndex ()); +} + +UndoRedoListForm::~UndoRedoListForm () +{ + delete mp_ui; + mp_ui = 0; +} + +void +UndoRedoListForm::selection_changed (const QModelIndex ¤t) +{ + if (! current.isValid () || current.row () < 0) { + mp_ui->title_lbl->setText (m_for_undo ? tr ("Undo to step (select one)") : tr ("Redo to step (select one)")); + m_steps = -1; + } else { + + m_steps = current.row () + 1; + if (m_steps == 1) { + mp_ui->title_lbl->setText (m_for_undo ? tr ("Undo one step") : tr ("Redo one step")); + } else { + mp_ui->title_lbl->setText ((m_for_undo ? tr ("Undo %1 steps") : tr ("Redo %1 steps")).arg (m_steps)); + } + + UndoRedoListViewModel *model = dynamic_cast (mp_ui->items->model ()); + if (model) { + model->set_current_step (m_steps - 1); + } + + } +} + +bool +UndoRedoListForm::exec (int &steps) +{ + if (QDialog::exec ()) { + steps = m_steps; + return true; + } else { + return false; + } +} + + } #endif diff --git a/src/layui/layui/layDialogs.h b/src/layui/layui/layDialogs.h index cea190b36..88678d289 100644 --- a/src/layui/layui/layDialogs.h +++ b/src/layui/layui/layDialogs.h @@ -32,6 +32,7 @@ #include class QTreeWidgetItem; +class QModelIndex; namespace lay { @@ -57,6 +58,7 @@ namespace Ui class FlattenInstOptionsDialog; class UserPropertiesForm; class UserPropertiesEditForm; + class UndoRedoListForm; } namespace lay @@ -460,6 +462,30 @@ class LAYUI_PUBLIC UserPropertiesEditForm Ui::UserPropertiesEditForm *mp_ui; }; +/** + * @brief The undo/redo list form + */ +class LAYUI_PUBLIC UndoRedoListForm + : public QDialog +{ +Q_OBJECT + +public: + UndoRedoListForm (QWidget *parent, db::Manager *manager, bool for_undo); + virtual ~UndoRedoListForm (); + + bool exec (int &steps); + +private slots: + void selection_changed (const QModelIndex ¤t); + +private: + Ui::UndoRedoListForm *mp_ui; + bool m_for_undo; + db::Manager *mp_manager; + int m_steps; +}; + } #endif diff --git a/src/layui/layui/layLayoutViewFunctions.cc b/src/layui/layui/layLayoutViewFunctions.cc index 4d09f325f..01a133436 100644 --- a/src/layui/layui/layLayoutViewFunctions.cc +++ b/src/layui/layui/layLayoutViewFunctions.cc @@ -2113,6 +2113,8 @@ class LayoutViewPluginDeclaration at = "edit_menu.edit_options_group"; menu_entries.push_back (lay::menu_item ("cm_undo", "undo:edit", at, tl::to_string (tr ("Undo(Ctrl+Z)")))); menu_entries.push_back (lay::menu_item ("cm_redo", "redo:edit", at, tl::to_string (tr ("Redo(Ctrl+Y)")))); + menu_entries.push_back (lay::menu_item ("cm_undo_list", "undo_list:edit", at, tl::to_string (tr ("Undo by List")))); + menu_entries.push_back (lay::menu_item ("cm_redo_list", "redo_list:edit", at, tl::to_string (tr ("Redo by List")))); menu_entries.push_back (lay::separator ("basic_group", at)); menu_entries.push_back (lay::submenu ("layout_menu:edit:edit_mode", at, tl::to_string (tr ("Layout")))); diff --git a/src/layui/layui/layui.pro b/src/layui/layui/layui.pro index ed6ac69a0..094e58410 100644 --- a/src/layui/layui/layui.pro +++ b/src/layui/layui/layui.pro @@ -61,6 +61,7 @@ FORMS = \ SaveLayoutAsOptionsDialog.ui \ SelectStippleForm.ui \ TipDialog.ui \ + UndoRedoListForm.ui \ UserPropertiesForm.ui \ UserPropertiesEditForm.ui \ SpecificLoadLayoutOptionsDialog.ui \