HaikuDepot: Base-classes for generic undo/redo support.
I plan to use these in the text view and editor framework.
This commit is contained in:
parent
ee3b36afff
commit
651e8326ca
@ -4,7 +4,7 @@ UsePrivateHeaders interface shared package ;
|
||||
|
||||
# source directories
|
||||
local sourceDirs =
|
||||
model textview ui ui_generic
|
||||
edits_generic model textview ui ui_generic
|
||||
;
|
||||
|
||||
local sourceDir ;
|
||||
@ -15,6 +15,13 @@ for sourceDir in $(sourceDirs) {
|
||||
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src servers package ] ;
|
||||
|
||||
local textDocumentSources =
|
||||
# edits_generic
|
||||
CompoundEdit.cpp
|
||||
EditContext.cpp
|
||||
EditManager.cpp
|
||||
EditStack.cpp
|
||||
UndoableEdit.cpp
|
||||
# textview
|
||||
Bullet.cpp
|
||||
BulletData.cpp
|
||||
CharacterStyle.cpp
|
||||
|
117
src/apps/haikudepot/edits_generic/CompoundEdit.cpp
Normal file
117
src/apps/haikudepot/edits_generic/CompoundEdit.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2006-2112, Stephan Aßmus <superstippi@gmx.de>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "CompoundEdit.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
CompoundEdit::CompoundEdit(const char* name)
|
||||
: UndoableEdit()
|
||||
, fEdits()
|
||||
, fName(name)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CompoundEdit::~CompoundEdit()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CompoundEdit::InitCheck()
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CompoundEdit::Perform(EditContext& context)
|
||||
{
|
||||
status_t status = B_OK;
|
||||
|
||||
int32 count = fEdits.CountItems();
|
||||
int32 i = 0;
|
||||
for (; i < count; i++) {
|
||||
status = fEdits.ItemAtFast(i)->Perform(context);
|
||||
if (status != B_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != B_OK) {
|
||||
// roll back
|
||||
i--;
|
||||
for (; i >= 0; i--) {
|
||||
fEdits.ItemAtFast(i)->Undo(context);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CompoundEdit::Undo(EditContext& context)
|
||||
{
|
||||
status_t status = B_OK;
|
||||
|
||||
int32 count = fEdits.CountItems();
|
||||
int32 i = count - 1;
|
||||
for (; i >= 0; i--) {
|
||||
status = fEdits.ItemAtFast(i)->Undo(context);
|
||||
if (status != B_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != B_OK) {
|
||||
// roll back
|
||||
i++;
|
||||
for (; i < count; i++) {
|
||||
fEdits.ItemAtFast(i)->Redo(context);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CompoundEdit::Redo(EditContext& context)
|
||||
{
|
||||
status_t status = B_OK;
|
||||
|
||||
int32 count = fEdits.CountItems();
|
||||
int32 i = 0;
|
||||
for (; i < count; i++) {
|
||||
status = fEdits.ItemAtFast(i)->Redo(context);
|
||||
if (status != B_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != B_OK) {
|
||||
// roll back
|
||||
i--;
|
||||
for (; i >= 0; i--) {
|
||||
fEdits.ItemAtFast(i)->Undo(context);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CompoundEdit::GetName(BString& name)
|
||||
{
|
||||
name << fName;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
CompoundEdit::AppendEdit(const UndoableEditRef& edit)
|
||||
{
|
||||
return fEdits.Add(edit);
|
||||
}
|
36
src/apps/haikudepot/edits_generic/CompoundEdit.h
Normal file
36
src/apps/haikudepot/edits_generic/CompoundEdit.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2006-2112, Stephan Aßmus <superstippi@gmx.de>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef COMPOUND_EDIT_H
|
||||
#define COMPOUND_EDIT_H
|
||||
|
||||
#include <String.h>
|
||||
|
||||
#include "List.h"
|
||||
#include "UndoableEdit.h"
|
||||
|
||||
class CompoundEdit : public UndoableEdit {
|
||||
public:
|
||||
CompoundEdit(const char* name);
|
||||
virtual ~CompoundEdit();
|
||||
|
||||
virtual status_t InitCheck();
|
||||
|
||||
virtual status_t Perform(EditContext& context);
|
||||
virtual status_t Undo(EditContext& context);
|
||||
virtual status_t Redo(EditContext& context);
|
||||
|
||||
virtual void GetName(BString& name);
|
||||
|
||||
bool AppendEdit(const UndoableEditRef& edit);
|
||||
|
||||
private:
|
||||
typedef List<UndoableEditRef, false, 2> EditList;
|
||||
|
||||
EditList fEdits;
|
||||
BString fName;
|
||||
};
|
||||
|
||||
#endif // COMPOUND_EDIT_H
|
18
src/apps/haikudepot/edits_generic/EditContext.cpp
Normal file
18
src/apps/haikudepot/edits_generic/EditContext.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2013 Stephan Aßmus <superstippi@gmx.de>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "EditContext.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
EditContext::EditContext()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
EditContext::~EditContext()
|
||||
{
|
||||
}
|
26
src/apps/haikudepot/edits_generic/EditContext.h
Normal file
26
src/apps/haikudepot/edits_generic/EditContext.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2013 Stephan Aßmus <superstippi@gmx.de>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef EDIT_CONTEXT_H
|
||||
#define EDIT_CONTEXT_H
|
||||
|
||||
#include <Referenceable.h>
|
||||
|
||||
/**
|
||||
* EditContext is passed to UndoableEdits in Perform(), Undo(), and Redo().
|
||||
* It provides a context in which the user performs these operations
|
||||
* and can be used for example to control the selection or visible
|
||||
* portion of the document to focus the user's attention on the
|
||||
* elements affected by the UndoableEdit.
|
||||
*/
|
||||
class EditContext : public BReferenceable {
|
||||
public:
|
||||
EditContext();
|
||||
virtual ~EditContext();
|
||||
};
|
||||
|
||||
typedef BReference<EditContext> EditContextRef;
|
||||
|
||||
#endif // EDIT_CONTEXT_H
|
244
src/apps/haikudepot/edits_generic/EditManager.cpp
Normal file
244
src/apps/haikudepot/edits_generic/EditManager.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright 2006-2012, Stephan Aßmus <superstippi@gmx.de>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "EditManager.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Locker.h>
|
||||
#include <String.h>
|
||||
|
||||
|
||||
EditManager::Listener::~Listener()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
EditManager::EditManager()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
EditManager::~EditManager()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
EditManager::Perform(UndoableEdit* edit, EditContext& context)
|
||||
{
|
||||
if (edit == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
return Perform(UndoableEditRef(edit, true), context);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
EditManager::Perform(const UndoableEditRef& edit, EditContext& context)
|
||||
{
|
||||
status_t ret = edit.Get() != NULL ? B_OK : B_BAD_VALUE;
|
||||
if (ret == B_OK)
|
||||
ret = edit->InitCheck();
|
||||
|
||||
if (ret == B_OK)
|
||||
ret = edit->Perform(context);
|
||||
|
||||
if (ret == B_OK) {
|
||||
ret = _AddEdit(edit);
|
||||
if (ret != B_OK)
|
||||
edit->Undo(context);
|
||||
}
|
||||
|
||||
_NotifyListeners();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
EditManager::Undo(EditContext& context)
|
||||
{
|
||||
status_t status = B_ERROR;
|
||||
if (!fUndoHistory.IsEmpty()) {
|
||||
UndoableEditRef edit(fUndoHistory.Top());
|
||||
fUndoHistory.Pop();
|
||||
status = edit->Undo(context);
|
||||
if (status == B_OK)
|
||||
fRedoHistory.Push(edit);
|
||||
else
|
||||
fUndoHistory.Push(edit);
|
||||
}
|
||||
|
||||
_NotifyListeners();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
EditManager::Redo(EditContext& context)
|
||||
{
|
||||
status_t status = B_ERROR;
|
||||
if (!fRedoHistory.IsEmpty()) {
|
||||
UndoableEditRef edit(fRedoHistory.Top());
|
||||
fRedoHistory.Pop();
|
||||
status = edit->Redo(context);
|
||||
if (status == B_OK)
|
||||
fUndoHistory.Push(edit);
|
||||
else
|
||||
fRedoHistory.Push(edit);
|
||||
}
|
||||
|
||||
_NotifyListeners();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
EditManager::GetUndoName(BString& name)
|
||||
{
|
||||
if (!fUndoHistory.IsEmpty()) {
|
||||
name << " ";
|
||||
fUndoHistory.Top()->GetName(name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
EditManager::GetRedoName(BString& name)
|
||||
{
|
||||
if (!fRedoHistory.IsEmpty()) {
|
||||
name << " ";
|
||||
fRedoHistory.Top()->GetName(name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
EditManager::Clear()
|
||||
{
|
||||
while (!fUndoHistory.IsEmpty())
|
||||
fUndoHistory.Pop();
|
||||
while (!fRedoHistory.IsEmpty())
|
||||
fRedoHistory.Pop();
|
||||
|
||||
_NotifyListeners();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
EditManager::Save()
|
||||
{
|
||||
if (!fUndoHistory.IsEmpty())
|
||||
fEditAtSave = fUndoHistory.Top();
|
||||
|
||||
_NotifyListeners();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
EditManager::IsSaved()
|
||||
{
|
||||
bool saved = fUndoHistory.IsEmpty();
|
||||
if (fEditAtSave.Get() != NULL && !saved) {
|
||||
if (fEditAtSave == fUndoHistory.Top())
|
||||
saved = true;
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
bool
|
||||
EditManager::AddListener(Listener* listener)
|
||||
{
|
||||
return fListeners.Add(listener);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
EditManager::RemoveListener(Listener* listener)
|
||||
{
|
||||
fListeners.Remove(listener);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
status_t
|
||||
EditManager::_AddEdit(const UndoableEditRef& edit)
|
||||
{
|
||||
status_t status = B_OK;
|
||||
|
||||
bool add = true;
|
||||
if (!fUndoHistory.IsEmpty()) {
|
||||
// Try to collapse edits to a single edit
|
||||
// or remove this and the previous edit if
|
||||
// they reverse each other
|
||||
const UndoableEditRef& top = fUndoHistory.Top();
|
||||
if (edit->UndoesPrevious(top.Get())) {
|
||||
add = false;
|
||||
fUndoHistory.Pop();
|
||||
} else if (top->CombineWithNext(edit.Get())) {
|
||||
add = false;
|
||||
// After collapsing, the edit might
|
||||
// have changed it's mind about InitCheck()
|
||||
// (the commands reversed each other)
|
||||
if (top->InitCheck() != B_OK) {
|
||||
fUndoHistory.Pop();
|
||||
}
|
||||
} else if (edit->CombineWithPrevious(top.Get())) {
|
||||
fUndoHistory.Pop();
|
||||
// After collapsing, the edit might
|
||||
// have changed it's mind about InitCheck()
|
||||
// (the commands reversed each other)
|
||||
if (edit->InitCheck() != B_OK) {
|
||||
add = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (add) {
|
||||
if (!fUndoHistory.Push(edit))
|
||||
status = B_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (status == B_OK) {
|
||||
// The redo stack needs to be empty
|
||||
// as soon as an edit was added (also in case of collapsing)
|
||||
while (!fRedoHistory.IsEmpty()) {
|
||||
fRedoHistory.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
EditManager::_NotifyListeners()
|
||||
{
|
||||
int32 count = fListeners.CountItems();
|
||||
if (count == 0)
|
||||
return;
|
||||
// Iterate a copy of the list, so we don't crash if listeners
|
||||
// detach themselves while being notified.
|
||||
ListenerList listenersCopy(fListeners);
|
||||
if (listenersCopy.CountItems() != count)
|
||||
return;
|
||||
for (int32 i = 0; i < count; i++)
|
||||
listenersCopy.ItemAtFast(i)->EditManagerChanged(this);
|
||||
}
|
||||
|
60
src/apps/haikudepot/edits_generic/EditManager.h
Normal file
60
src/apps/haikudepot/edits_generic/EditManager.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2006-2012, Stephan Aßmus <superstippi@gmx.de>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef EDIT_MANAGER_H
|
||||
#define EDIT_MANAGER_H
|
||||
|
||||
#include "EditStack.h"
|
||||
#include "UndoableEdit.h"
|
||||
|
||||
class BString;
|
||||
class EditContext;
|
||||
|
||||
|
||||
class EditManager {
|
||||
public:
|
||||
class Listener {
|
||||
public:
|
||||
virtual ~Listener();
|
||||
virtual void EditManagerChanged(
|
||||
const EditManager* manager) = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
EditManager();
|
||||
virtual ~EditManager();
|
||||
|
||||
status_t Perform(UndoableEdit* edit,
|
||||
EditContext& context);
|
||||
status_t Perform(const UndoableEditRef& edit,
|
||||
EditContext& context);
|
||||
|
||||
status_t Undo(EditContext& context);
|
||||
status_t Redo(EditContext& context);
|
||||
|
||||
bool GetUndoName(BString& name);
|
||||
bool GetRedoName(BString& name);
|
||||
|
||||
void Clear();
|
||||
void Save();
|
||||
bool IsSaved();
|
||||
|
||||
bool AddListener(Listener* listener);
|
||||
void RemoveListener(Listener* listener);
|
||||
|
||||
private:
|
||||
status_t _AddEdit(const UndoableEditRef& edit);
|
||||
|
||||
void _NotifyListeners();
|
||||
|
||||
private:
|
||||
EditStack fUndoHistory;
|
||||
EditStack fRedoHistory;
|
||||
UndoableEditRef fEditAtSave;
|
||||
|
||||
typedef List<Listener*, true, 4> ListenerList;
|
||||
ListenerList fListeners;
|
||||
};
|
||||
|
||||
#endif // EDIT_MANAGER_H
|
49
src/apps/haikudepot/edits_generic/EditStack.cpp
Normal file
49
src/apps/haikudepot/edits_generic/EditStack.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012, Stephan Aßmus <superstippi@gmx.de>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "EditStack.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
EditStack::EditStack()
|
||||
: fEdits()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
EditStack::~EditStack()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
EditStack::Push(const UndoableEditRef& edit)
|
||||
{
|
||||
return fEdits.Add(edit);
|
||||
}
|
||||
|
||||
|
||||
UndoableEditRef
|
||||
EditStack::Pop()
|
||||
{
|
||||
UndoableEditRef edit(Top());
|
||||
fEdits.Remove();
|
||||
return edit;
|
||||
}
|
||||
|
||||
|
||||
const UndoableEditRef&
|
||||
EditStack::Top() const
|
||||
{
|
||||
return fEdits.LastItem();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
EditStack::IsEmpty() const
|
||||
{
|
||||
return fEdits.CountItems() == 0;
|
||||
}
|
30
src/apps/haikudepot/edits_generic/EditStack.h
Normal file
30
src/apps/haikudepot/edits_generic/EditStack.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2012, Stephan Aßmus <superstippi@gmx.de>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef EDIT_STACK_H
|
||||
#define EDIT_STACK_H
|
||||
|
||||
#include "List.h"
|
||||
#include "UndoableEdit.h"
|
||||
|
||||
class EditStack {
|
||||
public:
|
||||
EditStack();
|
||||
virtual ~EditStack();
|
||||
|
||||
bool Push(const UndoableEditRef& edit);
|
||||
UndoableEditRef Pop();
|
||||
|
||||
const UndoableEditRef& Top() const;
|
||||
|
||||
bool IsEmpty() const;
|
||||
|
||||
private:
|
||||
typedef List<UndoableEditRef, false> EditList;
|
||||
|
||||
EditList fEdits;
|
||||
};
|
||||
|
||||
#endif // EDIT_STACK_H
|
86
src/apps/haikudepot/edits_generic/UndoableEdit.cpp
Normal file
86
src/apps/haikudepot/edits_generic/UndoableEdit.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2006-2012, Stephan Aßmus <superstippi@gmx.de>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "UndoableEdit.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <OS.h>
|
||||
#include <String.h>
|
||||
|
||||
|
||||
//static int32 sInstanceCount = 0;
|
||||
|
||||
|
||||
UndoableEdit::UndoableEdit()
|
||||
:
|
||||
fTimeStamp(system_time())
|
||||
{
|
||||
// sInstanceCount++;
|
||||
// printf("UndoableEdits: %ld\n", sInstanceCount);
|
||||
}
|
||||
|
||||
|
||||
UndoableEdit::~UndoableEdit()
|
||||
{
|
||||
// sInstanceCount--;
|
||||
// printf("UndoableEdits: %ld\n", sInstanceCount);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UndoableEdit::InitCheck()
|
||||
{
|
||||
return B_NO_INIT;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UndoableEdit::Perform(EditContext& context)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UndoableEdit::Undo(EditContext& context)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UndoableEdit::Redo(EditContext& context)
|
||||
{
|
||||
return Perform(context);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UndoableEdit::GetName(BString& name)
|
||||
{
|
||||
name << "Name of edit goes here.";
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
UndoableEdit::UndoesPrevious(const UndoableEdit* previous)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
UndoableEdit::CombineWithNext(const UndoableEdit* next)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
UndoableEdit::CombineWithPrevious(const UndoableEdit* previous)
|
||||
{
|
||||
return false;
|
||||
}
|
41
src/apps/haikudepot/edits_generic/UndoableEdit.h
Normal file
41
src/apps/haikudepot/edits_generic/UndoableEdit.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2006-2012 Stephan Aßmus <superstippi@gmx.de>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef UNDOABLE_EDIT_H
|
||||
#define UNDOABLE_EDIT_H
|
||||
|
||||
#include <Referenceable.h>
|
||||
|
||||
class BString;
|
||||
class EditContext;
|
||||
|
||||
class UndoableEdit : public BReferenceable {
|
||||
public:
|
||||
UndoableEdit();
|
||||
virtual ~UndoableEdit();
|
||||
|
||||
virtual status_t InitCheck();
|
||||
|
||||
virtual status_t Perform(EditContext& context);
|
||||
virtual status_t Undo(EditContext& context);
|
||||
virtual status_t Redo(EditContext& context);
|
||||
|
||||
virtual void GetName(BString& name);
|
||||
|
||||
virtual bool UndoesPrevious(const UndoableEdit* previous);
|
||||
virtual bool CombineWithNext(const UndoableEdit* next);
|
||||
virtual bool CombineWithPrevious(
|
||||
const UndoableEdit* previous);
|
||||
|
||||
inline bigtime_t TimeStamp() const
|
||||
{ return fTimeStamp; }
|
||||
|
||||
protected:
|
||||
bigtime_t fTimeStamp;
|
||||
};
|
||||
|
||||
typedef BReference<UndoableEdit> UndoableEditRef;
|
||||
|
||||
#endif // UNDOABLE_EDIT_H
|
Loading…
Reference in New Issue
Block a user