Commit 026c09c5 authored by daniel_e's avatar daniel_e
Browse files

New files, implementing the core of the new undo system. Basically it's a

* src/undostack.{cc,h}: New files, implementing the core of the
new undo system.  Basically it's a simple undo stack using virtual
methods for the implementation of undo actions.  But the undo stack
is itself an undo action, which allows for nested stacks.  This
functionality is necessary since the undo works globally on all
files.

* src/filebufferundo.{cc,h}: New files, defining the implementation
of the FileBuffer undo actions.

* src/fileshared.{cc,h}: Move parts of filebuffer.{cc,h} into these
new files, in order to avoid circular dependencies and to reduce the
length of single source files.

* src/filetreeprivate.{cc,h}: Move the anonymous namespace parts
and the nested classes of the FileTree implementation into separate
files.  filetreeprivate.h is included by filetree.cc only.

* src/Makefile.am (regexxer_SOURCES): Add new source files.

* src/filebuffer.{cc,h}: Heavily modify and enhance the code in
order to support the new undo framework.

* src/filetree.{cc,h}: ditto.

* src/controller.{cc,h}: Add undo ControlItem and show menu/toolbar
items for it.

* src/mainwindow.{cc,h}: Add code to maintain the undo stack and
connect the GUI bits.
parent e9ac0619
2002-11-29 Daniel Elstner <daniel.elstner@gmx.net>
OK folks, this is the new undo system. It works, but don't rely
too much on undo yet since there are still some issues to work out.
Try stress testing the system a bit and you'll be able to trigger
assertions, misbehaviour and all other kind of evil stuff.
However, the framework is basically working and the remaining
problems are going to be sorted out very soon. Happy testing!
* src/undostack.{cc,h}: New files, implementing the core of the
new undo system. Basically it's a simple undo stack using virtual
methods for the implementation of undo actions. But the undo stack
is itself an undo action, which allows for nested stacks. This
functionality is necessary since the undo works globally on all
files.
* src/filebufferundo.{cc,h}: New files, defining the implementation
of the FileBuffer undo actions.
* src/fileshared.{cc,h}: Move parts of filebuffer.{cc,h} into these
new files, in order to avoid circular dependencies and to reduce the
length of single source files.
* src/filetreeprivate.{cc,h}: Move the anonymous namespace parts
and the nested classes of the FileTree implementation into separate
files. filetreeprivate.h is included by filetree.cc only.
* src/Makefile.am (regexxer_SOURCES): Add new source files.
* src/filebuffer.{cc,h}: Heavily modify and enhance the code in
order to support the new undo framework.
* src/filetree.{cc,h}: ditto.
* src/controller.{cc,h}: Add undo ControlItem and show menu/toolbar
items for it.
* src/mainwindow.{cc,h}: Add code to maintain the undo stack and
connect the GUI bits.
2002-11-25 Daniel Elstner <daniel.elstner@gmx.net>
* src/signalutils.h (ScopedBlock): Move from filetree.cc hitherto.
......
......@@ -20,36 +20,44 @@
bin_PROGRAMS = regexxer
regexxer_SOURCES = \
aboutdialog.cc \
aboutdialog.h \
configdata.cc \
configdata.h \
controller.cc \
controller.h \
filebuffer.cc \
filebuffer.h \
fileio.cc \
fileio.h \
filetree.cc \
filetree.h \
imagebutton.cc \
imagebutton.h \
main.cc \
mainwindow.cc \
mainwindow.h \
pcreshell.cc \
pcreshell.h \
prefdialog.cc \
prefdialog.h \
sharedptr.cc \
sharedptr.h \
signalutils.cc \
signalutils.h \
statusline.cc \
statusline.h \
stringutils.cc \
stringutils.h
regexxer_SOURCES = \
aboutdialog.cc \
aboutdialog.h \
configdata.cc \
configdata.h \
controller.cc \
controller.h \
filebuffer.cc \
filebuffer.h \
filebufferundo.cc \
filebufferundo.h \
fileio.cc \
fileio.h \
fileshared.cc \
fileshared.h \
filetree.cc \
filetree.h \
filetreeprivate.cc \
filetreeprivate.h \
imagebutton.cc \
imagebutton.h \
main.cc \
mainwindow.cc \
mainwindow.h \
pcreshell.cc \
pcreshell.h \
prefdialog.cc \
prefdialog.h \
sharedptr.cc \
sharedptr.h \
signalutils.cc \
signalutils.h \
statusline.cc \
statusline.h \
stringutils.cc \
stringutils.h \
undostack.cc \
undostack.h
DEFS = @DEFS@ \
-DG_DISABLE_DEPRECATED \
......
......@@ -173,12 +173,13 @@ void ControlGroup::set_enabled(bool enable)
Controller::Controller()
:
match_actions (true),
save_file (false),
save_all (false),
undo (false),
preferences (true),
quit (true),
info (true),
match_actions (true),
find_files (false),
find_matches (false),
next_file (false),
......@@ -189,6 +190,7 @@ Controller::Controller()
replace_file (false),
replace_all (false)
{
match_actions.add(undo);
match_actions.add(find_files);
match_actions.add(find_matches);
match_actions.add(next_file);
......@@ -223,6 +225,10 @@ Gtk::MenuBar* Controller::create_menubar()
items.push_back(SeparatorElem());
add_menu_stock(items, Stock::UNDO, undo);
items.push_back(SeparatorElem());
add_menu_stock(items, Stock::PREFERENCES, preferences);
items.push_back(SeparatorElem());
......@@ -273,6 +279,9 @@ Gtk::Toolbar* Controller::create_toolbar()
add_tool_stock(tools, Stock::SAVE, save_file);
add_tool_stock(tools, StockID("regexxer-save-all"), save_all);
tools.push_back(Space());
add_tool_stock(tools, Stock::UNDO, undo);
tools.push_back(Space());
add_tool_stock(tools, Stock::PREFERENCES, preferences);
......
......@@ -87,16 +87,17 @@ public:
Controller();
virtual ~Controller();
// Group for all controls that could change matches
// or require match information to operate.
ControlGroup match_actions;
ControlItem save_file;
ControlItem save_all;
ControlItem undo;
ControlItem preferences;
ControlItem quit;
ControlItem info;
// Group for all controls that could change matches
// or require match information to operate.
ControlGroup match_actions;
ControlItem find_files;
ControlItem find_matches;
......
This diff is collapsed.
......@@ -21,72 +21,17 @@
#ifndef REGEXXER_FILEBUFFER_H_INCLUDED
#define REGEXXER_FILEBUFFER_H_INCLUDED
#include "fileshared.h"
#include "signalutils.h"
#include "undostack.h"
#include <list>
#include <utility>
#include <vector>
#include <gtkmm/textbuffer.h>
#include <set>
namespace Pcre { class Pattern; }
namespace Regexxer
{
enum BoundState
{
BOUND_NONE = 0,
BOUND_FIRST = 1 << 0,
BOUND_LAST = 1 << 1,
BOUND_MASK = BOUND_FIRST | BOUND_LAST
};
inline BoundState operator|(BoundState lhs, BoundState rhs)
{ return static_cast<BoundState>(static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs)); }
inline BoundState operator&(BoundState lhs, BoundState rhs)
{ return static_cast<BoundState>(static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs)); }
inline BoundState operator^(BoundState lhs, BoundState rhs)
{ return static_cast<BoundState>(static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs)); }
inline BoundState operator~(BoundState flags)
{ return static_cast<BoundState>(~static_cast<unsigned>(flags)); }
inline BoundState& operator|=(BoundState& lhs, BoundState rhs)
{ return (lhs = static_cast<BoundState>(static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs))); }
inline BoundState& operator&=(BoundState& lhs, BoundState rhs)
{ return (lhs = static_cast<BoundState>(static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs))); }
inline BoundState& operator^=(BoundState& lhs, BoundState rhs)
{ return (lhs = static_cast<BoundState>(static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs))); }
// This struct holds all the information that's necessary to locate a
// match's position in the buffer and to substitute captured substrings
// into a replacement string. The latter is achived by storing the whole
// subject string (i.e. the line in the buffer) together with a table of
// indices into it. This arrangement should consume less memory than the
// alternative of storing a vector of captured substrings since we want
// to support $&, $`, $' too.
//
struct MatchData
{
MatchData(int match_index, const Glib::RefPtr<Gtk::TextMark>& position,
const Glib::ustring& line, const Pcre::Pattern& pattern, int capture_count);
~MatchData();
int get_bytes_length() const;
int index;
Glib::RefPtr<Gtk::TextMark> mark;
Glib::ustring subject;
std::vector< std::pair<int,int> > captures;
};
class FileBuffer : public Gtk::TextBuffer
{
public:
......@@ -99,6 +44,7 @@ public:
virtual ~FileBuffer();
bool is_freeable() const;
bool in_user_action() const;
int find_matches(Pcre::Pattern& pattern, bool multiple);
......@@ -116,10 +62,16 @@ public:
int get_line_preview(const Glib::ustring& substitution, Glib::ustring& preview);
SigC::Signal1<void,int> signal_match_count_changed;
SigC::Signal1<void,BoundState> signal_bound_state_changed;
Util::QueuedSignal signal_preview_line_changed;
SigC::Signal0<bool> signal_pulse;
// Special API for the FileBufferAction classes.
void increment_stamp();
void decrement_stamp();
void undo_remove_match(const MatchDataPtr& match, int offset);
SigC::Signal1<void,int> signal_match_count_changed;
SigC::Signal1<void,BoundState> signal_bound_state_changed;
Util::QueuedSignal signal_preview_line_changed;
SigC::Signal0<bool> signal_pulse;
SigC::Signal1<void,UndoActionPtr> signal_undo_stack_push;
protected:
FileBuffer();
......@@ -127,31 +79,34 @@ protected:
virtual void on_insert(const iterator& pos, const Glib::ustring& text, int bytes);
virtual void on_erase(const iterator& rbegin, const iterator& rend);
virtual void on_mark_deleted(const Glib::RefPtr<TextBuffer::Mark>& mark);
virtual void on_modified_changed();
virtual void on_begin_user_action();
virtual void on_end_user_action();
private:
class ScopedLock;
std::list<MatchData> match_list_;
int match_count_;
int original_match_count_;
std::list<MatchData>::iterator current_match_;
bool match_removed_;
BoundState bound_state_;
bool locked_;
void replace_match(std::list<MatchData>::iterator pos, const Glib::ustring& substitution);
Glib::RefPtr<Mark> create_match_mark(const iterator& where, int length);
static bool is_match_mark(const Glib::RefPtr<Mark>& mark);
static int get_match_length(const Glib::RefPtr<Mark>& mark);
static bool is_match_start(const iterator& where);
class ScopedUserAction;
typedef std::set<MatchDataPtr,MatchDataLess> MatchSet;
MatchSet match_set_;
int match_count_;
int original_match_count_;
MatchSet::iterator current_match_;
bool match_removed_;
BoundState bound_state_;
bool locked_;
UndoStackPtr user_action_stack_;
unsigned long stamp_modified_;
unsigned long stamp_saved_;
void replace_match(MatchSet::iterator pos, const Glib::ustring& substitution);
void remove_match_at_iter(const iterator& start);
void remove_tag_current();
void apply_tag_current();
void remove_match_at_iter(const iterator& start);
static bool is_match_start(const iterator& where);
static void find_line_bounds(iterator& line_begin, iterator& line_end);
void update_bound_state();
......
/* $Id$
*
* Copyright (c) 2002 Daniel Elstner <daniel.elstner@gmx.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License VERSION 2 as
* published by the Free Software Foundation. You are not allowed to
* use any other version of the license; unless you got the explicit
* permission from the author to do so.
*
* This program 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "filebufferundo.h"
#include "filebuffer.h"
#include <glib.h>
namespace Regexxer
{
/**** Regexxer::FileBufferActionInsert *************************************/
FileBufferActionInsert::FileBufferActionInsert(FileBuffer& filebuffer, int offset,
const Glib::ustring& text)
:
FileBufferAction(filebuffer),
text_ (text),
offset_ (offset)
{
buffer().increment_stamp();
}
FileBufferActionInsert::~FileBufferActionInsert()
{}
bool FileBufferActionInsert::do_undo()
{
g_return_val_if_fail(!buffer().in_user_action(), false);
const FileBuffer::iterator text_begin = buffer().get_iter_at_offset(offset_);
FileBuffer::iterator text_end = text_begin;
text_end.forward_chars(text_.length());
text_end = buffer().erase(text_begin, text_end);
buffer().place_cursor(text_end);
buffer().decrement_stamp();
return false;
}
/**** Regexxer::FileBufferActionErase **************************************/
FileBufferActionErase::FileBufferActionErase(FileBuffer& filebuffer, int offset,
const Glib::ustring& text)
:
FileBufferAction(filebuffer),
text_ (text),
offset_ (offset)
{
buffer().increment_stamp();
}
FileBufferActionErase::~FileBufferActionErase()
{}
bool FileBufferActionErase::do_undo()
{
g_return_val_if_fail(!buffer().in_user_action(), false);
FileBuffer::iterator pos = buffer().get_iter_at_offset(offset_);
pos = buffer().insert(pos, text_);
buffer().place_cursor(pos);
buffer().decrement_stamp();
return false;
}
/**** Regexxer::FileBufferActionRemoveMatch ********************************/
FileBufferActionRemoveMatch::FileBufferActionRemoveMatch(
FileBuffer& filebuffer, int offset, const MatchDataPtr& match)
:
FileBufferAction(filebuffer),
match_ (match),
offset_ (offset)
{}
FileBufferActionRemoveMatch::~FileBufferActionRemoveMatch()
{}
bool FileBufferActionRemoveMatch::do_undo()
{
g_return_val_if_fail(!buffer().in_user_action(), true);
buffer().undo_remove_match(match_, offset_);
return true;
}
} // namespace Regexxer
/* $Id$
*
* Copyright (c) 2002 Daniel Elstner <daniel.elstner@gmx.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License VERSION 2 as
* published by the Free Software Foundation. You are not allowed to
* use any other version of the license; unless you got the explicit
* permission from the author to do so.
*
* This program 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef REGEXXER_FILEBUFFERUNDO_H_INCLUDED
#define REGEXXER_FILEBUFFERUNDO_H_INCLUDED
#include "undostack.h"
#include "fileshared.h"
namespace Regexxer
{
class FileBuffer;
class FileBufferAction : public UndoAction
{
public:
explicit FileBufferAction(FileBuffer& filebuffer) : buffer_ (filebuffer) {}
protected:
FileBuffer& buffer() { return buffer_; }
private:
FileBuffer& buffer_;
};
class FileBufferActionInsert : public FileBufferAction
{
public:
FileBufferActionInsert(FileBuffer& filebuffer, int offset, const Glib::ustring& text);
virtual ~FileBufferActionInsert();
private:
Glib::ustring text_;
int offset_;
virtual bool do_undo();
};
class FileBufferActionErase : public FileBufferAction
{
public:
FileBufferActionErase(FileBuffer& filebuffer, int offset, const Glib::ustring& text);
virtual ~FileBufferActionErase();
private:
Glib::ustring text_;
int offset_;
virtual bool do_undo();
};
class FileBufferActionRemoveMatch : public FileBufferAction
{
public:
FileBufferActionRemoveMatch(FileBuffer& filebuffer, int offset, const MatchDataPtr& match);
virtual ~FileBufferActionRemoveMatch();
private:
MatchDataPtr match_;
int offset_;
virtual bool do_undo();
};
} // namespace Regexxer
#endif /* REGEXXER_FILEBUFFERUNDO_H_INCLUDED */
/* $Id$
*
* Copyright (c) 2002 Daniel Elstner <daniel.elstner@gmx.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License VERSION 2 as
* published by the Free Software Foundation. You are not allowed to
* use any other version of the license; unless you got the explicit
* permission from the author to do so.
*
* This program 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "fileshared.h"
#include "pcreshell.h"
#include <glib/gmacros.h>
#include <algorithm>
namespace
{
Glib::Quark file_buffer_match_quark() G_GNUC_CONST;
Glib::Quark file_buffer_match_quark()
{
// Regexxer::FileBuffer uses anonymous Gtk::TextMark objects to remember
// the position of matches. This quark is used to identify a match mark,
// in order to be able to distinguish it from other anonymous marks.
static Glib::Quark quark ("regexxer-file-buffer-match-quark");
return quark;
}
int calculate_match_length(const Glib::ustring& subject, const std::pair<int,int>& bounds)
{
const std::string::const_iterator begin = subject.begin().base();
const Glib::ustring::const_iterator start (begin + bounds.first);
const Glib::ustring::const_iterator stop (begin + bounds.second);
return std::distance(start, stop);
}
} // anonymous namespace
namespace Regexxer
{
/**** Regexxer::MatchData **************************************************/
MatchData::MatchData(int match_index, const Glib::ustring& line,
const Pcre::Pattern& pattern, int capture_count)
:
index (match_index),
subject (line)
{
captures.reserve(capture_count);
for(int i = 0; i < capture_count; ++i)
captures.push_back(pattern.get_substring_bounds(i));
length = calculate_match_length(subject, captures.front());
}
MatchData::~MatchData()
{}
void MatchData::install_mark(const Gtk::TextBuffer::iterator& pos)
{
g_return_if_fail(!mark);
mark = pos.get_buffer()->create_mark(pos, false); // right gravity
mark->set_data(file_buffer_match_quark(), this);
}