mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 21:06:59 -04:00
Switched to a new coding style, which plays nicely with the Reader/Writer. This new style allows REFLECT to be used instead of REFLECT_N in most places.
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@15 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
+22
-22
@@ -16,77 +16,77 @@ DECLARE_TYPEOF_COLLECTION(Action*);
|
||||
DECLARE_TYPEOF_COLLECTION(ActionListener*);
|
||||
|
||||
ActionStack::ActionStack()
|
||||
: savePoint(nullptr)
|
||||
: save_point(nullptr)
|
||||
{}
|
||||
|
||||
ActionStack::~ActionStack() {
|
||||
// we own the actions, delete them
|
||||
FOR_EACH(a, undoActions) delete a;
|
||||
FOR_EACH(a, redoActions) delete a;
|
||||
FOR_EACH(a, undo_actions) delete a;
|
||||
FOR_EACH(a, redo_actions) delete a;
|
||||
}
|
||||
|
||||
void ActionStack::add(Action* action, bool allowMerge) {
|
||||
void ActionStack::add(Action* action, bool allow_merge) {
|
||||
if (!action) return; // no action
|
||||
action->perform(false); // TODO: delete action if perform throws
|
||||
redoActions.clear();
|
||||
redo_actions.clear();
|
||||
tellListeners(*action);
|
||||
// try to merge?
|
||||
if (allowMerge && !undoActions.empty() && undoActions.back()->merge(action)) {
|
||||
if (allow_merge && !undo_actions.empty() && undo_actions.back()->merge(action)) {
|
||||
// merged with top undo action
|
||||
delete action;
|
||||
} else {
|
||||
undoActions.push_back(action);
|
||||
undo_actions.push_back(action);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionStack::undo() {
|
||||
assert(canUndo());
|
||||
Action* action = undoActions.back();
|
||||
Action* action = undo_actions.back();
|
||||
action->perform(true);
|
||||
// move to redo stack
|
||||
undoActions.pop_back();
|
||||
redoActions.push_back(action);
|
||||
undo_actions.pop_back();
|
||||
redo_actions.push_back(action);
|
||||
}
|
||||
void ActionStack::redo() {
|
||||
assert(canRedo());
|
||||
Action* action = redoActions.back();
|
||||
Action* action = redo_actions.back();
|
||||
action->perform(false);
|
||||
// move to undo stack
|
||||
redoActions.pop_back();
|
||||
undoActions.push_back(action);
|
||||
redo_actions.pop_back();
|
||||
undo_actions.push_back(action);
|
||||
}
|
||||
|
||||
bool ActionStack::canUndo() const {
|
||||
return !undoActions.empty();
|
||||
return !undo_actions.empty();
|
||||
}
|
||||
bool ActionStack::canRedo() const {
|
||||
return !redoActions.empty();
|
||||
return !redo_actions.empty();
|
||||
}
|
||||
|
||||
String ActionStack::undoName() const {
|
||||
if (canUndo()) {
|
||||
return _("Undo ") + capitalize(undoActions.back()->getName(true));
|
||||
return _("Undo ") + capitalize(undo_actions.back()->getName(true));
|
||||
} else {
|
||||
return _("Undo");
|
||||
}
|
||||
}
|
||||
String ActionStack::redoName() const {
|
||||
if (canRedo()) {
|
||||
return _("Redo ") + capitalize(redoActions.back()->getName(false));
|
||||
return _("Redo ") + capitalize(redo_actions.back()->getName(false));
|
||||
} else {
|
||||
return _("Redo");
|
||||
}
|
||||
}
|
||||
|
||||
bool ActionStack::atSavePoint() const {
|
||||
return (undoActions.empty() && savePoint == nullptr)
|
||||
|| (undoActions.back() == savePoint);
|
||||
return (undo_actions.empty() && save_point == nullptr)
|
||||
|| (undo_actions.back() == save_point);
|
||||
}
|
||||
void ActionStack::setSavePoint() {
|
||||
if (undoActions.empty()) {
|
||||
savePoint = nullptr;
|
||||
if (undo_actions.empty()) {
|
||||
save_point = nullptr;
|
||||
} else {
|
||||
savePoint = undoActions.back();
|
||||
save_point = undo_actions.back();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,17 +23,17 @@ class Action {
|
||||
virtual ~Action() {};
|
||||
|
||||
/// Name of the action, for use in strings like "Undo <name>"
|
||||
virtual String getName(bool toUndo) const = 0;
|
||||
virtual String getName(bool to_undo) const = 0;
|
||||
|
||||
/// Perform the action
|
||||
/** @param toUndo if true, undo the action instead of doing it
|
||||
/** @param to_undo if true, undo the action instead of doing it
|
||||
*
|
||||
* Must be implemented in derived class.
|
||||
*
|
||||
* Perform will only ever be called alternatingly with toUndo = true/false,
|
||||
* the first time with toUndo = false
|
||||
* Perform will only ever be called alternatingly with to_undo = true/false,
|
||||
* the first time with to_undo = false
|
||||
*/
|
||||
virtual void perform(bool toUndo) = 0;
|
||||
virtual void perform(bool to_undo) = 0;
|
||||
|
||||
/// Try to merge another action to the end of this action.
|
||||
/** Either: return false and do nothing
|
||||
@@ -103,12 +103,12 @@ class ActionStack {
|
||||
private:
|
||||
/// Actions to be undone
|
||||
/// Owns the action objects!
|
||||
vector<Action*> undoActions;
|
||||
vector<Action*> undo_actions;
|
||||
/// Actions to be redone
|
||||
/// Owns the action objects!
|
||||
vector<Action*> redoActions;
|
||||
vector<Action*> redo_actions;
|
||||
/// Point at which the file was saved, corresponds to the top of the undo stack at that point
|
||||
Action* savePoint;
|
||||
Action* save_point;
|
||||
/// Objects that are listening to actions
|
||||
vector<ActionListener*> listeners;
|
||||
};
|
||||
|
||||
+23
-23
@@ -13,19 +13,19 @@
|
||||
// ----------------------------------------------------------------------------- : Reader
|
||||
|
||||
Reader::Reader(const InputStreamP& input, String filename)
|
||||
: input(input), filename(filename), lineNumber(0)
|
||||
, indent(0), expectedIndent(0), justOpened(false)
|
||||
: input(input), filename(filename), line_number(0)
|
||||
, indent(0), expected_indent(0), just_opened(false)
|
||||
, stream(*input)
|
||||
{
|
||||
moveNext();
|
||||
}
|
||||
|
||||
bool Reader::enterBlock(const Char* name) {
|
||||
if (justOpened) moveNext(); // on the key of the parent block, first move inside it
|
||||
if (indent != expectedIndent) return false; // not enough indentation
|
||||
if (just_opened) moveNext(); // on the key of the parent block, first move inside it
|
||||
if (indent != expected_indent) return false; // not enough indentation
|
||||
if (name == key) {
|
||||
justOpened = true;
|
||||
expectedIndent += 1; // the indent inside the block must be at least this much
|
||||
just_opened = true;
|
||||
expected_indent += 1; // the indent inside the block must be at least this much
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -33,21 +33,21 @@ bool Reader::enterBlock(const Char* name) {
|
||||
}
|
||||
|
||||
void Reader::exitBlock() {
|
||||
assert(expectedIndent > 0);
|
||||
expectedIndent -= 1;
|
||||
multiLineStr.clear();
|
||||
if (justOpened) moveNext(); // leave this key
|
||||
assert(expected_indent > 0);
|
||||
expected_indent -= 1;
|
||||
multi_line_str.clear();
|
||||
if (just_opened) moveNext(); // leave this key
|
||||
// Dump the remainder of the block
|
||||
// TODO: issue warnings?
|
||||
while (indent > expectedIndent) {
|
||||
while (indent > expected_indent) {
|
||||
moveNext();
|
||||
}
|
||||
}
|
||||
|
||||
void Reader::moveNext() {
|
||||
justOpened = false;
|
||||
just_opened = false;
|
||||
key.clear();
|
||||
multiLineStr.clear();
|
||||
multi_line_str.clear();
|
||||
indent = -1; // if no line is read it never has the expected indentation
|
||||
// repeat until we have a good line
|
||||
while (key.empty() && !input->Eof()) {
|
||||
@@ -55,7 +55,7 @@ void Reader::moveNext() {
|
||||
}
|
||||
// did we reach the end of the file?
|
||||
if (key.empty() && input->Eof()) {
|
||||
lineNumber += 1;
|
||||
line_number += 1;
|
||||
indent = -1;
|
||||
}
|
||||
}
|
||||
@@ -70,10 +70,10 @@ void Reader::readLine() {
|
||||
}
|
||||
// read key / value
|
||||
size_t pos = line.find_first_of(_(':'), indent);
|
||||
key = trim(line.substr(indent, pos - indent));
|
||||
value = pos == String::npos ? _("") : trimLeft(line.substr(pos+1));
|
||||
key = cannocial_name_form(trim(line.substr(indent, pos - indent)));
|
||||
value = pos == String::npos ? _("") : trim_left(line.substr(pos+1));
|
||||
// we read a line
|
||||
lineNumber += 1;
|
||||
line_number += 1;
|
||||
// was it a comment?
|
||||
if (!key.empty() && key.GetChar(0) == _('#')) {
|
||||
key.clear();
|
||||
@@ -83,25 +83,25 @@ void Reader::readLine() {
|
||||
// ----------------------------------------------------------------------------- : Handling basic types
|
||||
|
||||
template <> void Reader::handle(String& s) {
|
||||
if (!multiLineStr.empty()) {
|
||||
s = multiLineStr;
|
||||
if (!multi_line_str.empty()) {
|
||||
s = multi_line_str;
|
||||
} else if (value.empty()) {
|
||||
// a multiline string
|
||||
bool first = true;
|
||||
// read all lines that are indented enough
|
||||
readLine();
|
||||
while (indent >= expectedIndent) {
|
||||
while (indent >= expected_indent) {
|
||||
if (!first) value += '\n';
|
||||
first = false;
|
||||
multiLineStr += line.substr(expectedIndent); // strip expected indent
|
||||
multi_line_str += line.substr(expected_indent); // strip expected indent
|
||||
readLine();
|
||||
}
|
||||
// moveNext(), but without emptying multiLineStr
|
||||
justOpened = false;
|
||||
just_opened = false;
|
||||
while (key.empty() && !input->Eof()) {
|
||||
readLine();
|
||||
}
|
||||
s = multiLineStr;
|
||||
s = multi_line_str;
|
||||
} else {
|
||||
s = value;
|
||||
}
|
||||
|
||||
@@ -64,18 +64,18 @@ class Reader {
|
||||
/// The key and value of the last line we read
|
||||
String key, value;
|
||||
/// A string spanning multiple lines
|
||||
String multiLineStr;
|
||||
String multi_line_str;
|
||||
/// Indentation of the last line we read
|
||||
int indent;
|
||||
/// Indentation of the block we are in
|
||||
int expectedIndent;
|
||||
int expected_indent;
|
||||
/// Did we just open a block (i.e. not read any more lines of it)?
|
||||
bool justOpened;
|
||||
bool just_opened;
|
||||
|
||||
/// Filename for error messages
|
||||
String filename;
|
||||
/// Line number for error messages
|
||||
UInt lineNumber;
|
||||
UInt line_number;
|
||||
/// Input stream we are reading from
|
||||
InputStreamP input;
|
||||
/// Text stream wrapping the input stream
|
||||
|
||||
+10
-8
@@ -16,7 +16,7 @@ Writer::Writer(const OutputStreamP& output)
|
||||
: output(output)
|
||||
, stream(*output)
|
||||
, indentation(0)
|
||||
, justOpened(false)
|
||||
, just_opened(false)
|
||||
{
|
||||
stream.WriteString(BYTE_ORDER_MARK);
|
||||
}
|
||||
@@ -24,25 +24,27 @@ Writer::Writer(const OutputStreamP& output)
|
||||
|
||||
void Writer::enterBlock(const Char* name) {
|
||||
// indenting into a sub-block?
|
||||
if (justOpened) {
|
||||
if (just_opened) {
|
||||
writeKey();
|
||||
stream.WriteString(_(":\n"));
|
||||
}
|
||||
// don't write the key yet
|
||||
indentation += 1;
|
||||
openedKey = name;
|
||||
justOpened = true;
|
||||
opened_key = name;
|
||||
just_opened = true;
|
||||
}
|
||||
|
||||
void Writer::exitBlock() {
|
||||
assert(indentation > 0);
|
||||
indentation -= 1;
|
||||
justOpened = false;
|
||||
just_opened = false;
|
||||
}
|
||||
|
||||
void Writer::writeKey() {
|
||||
writeIndentation();
|
||||
writeUTF8(stream, openedKey);
|
||||
// Use ' ' instead of '_' because it is more human readable
|
||||
FOR_EACH(c, opened_key) if (c == _('_')) c = _(' ');
|
||||
writeUTF8(stream, opened_key);
|
||||
}
|
||||
void Writer::writeIndentation() {
|
||||
for(int i = 1 ; i < indentation ; ++i) {
|
||||
@@ -53,7 +55,7 @@ void Writer::writeIndentation() {
|
||||
// ----------------------------------------------------------------------------- : Handling basic types
|
||||
|
||||
void Writer::handle(const String& value) {
|
||||
if (!justOpened) {
|
||||
if (!just_opened) {
|
||||
throw InternalError(_("Can only write a value in a key that was just opened"));
|
||||
}
|
||||
// write indentation and key
|
||||
@@ -86,7 +88,7 @@ void Writer::handle(const String& value) {
|
||||
writeUTF8(stream, value);
|
||||
}
|
||||
stream.PutChar(_('\n'));
|
||||
justOpened = false;
|
||||
just_opened = false;
|
||||
}
|
||||
|
||||
template <> void Writer::handle(const int& value) {
|
||||
|
||||
@@ -53,9 +53,9 @@ class Writer {
|
||||
/// Indentation of the current block
|
||||
int indentation;
|
||||
/// Did we just open a block (i.e. not written any lines of it)?
|
||||
bool justOpened;
|
||||
bool just_opened;
|
||||
/// Last key opened
|
||||
String openedKey;
|
||||
String opened_key;
|
||||
|
||||
/// Output stream we are writing to
|
||||
OutputStreamP output;
|
||||
@@ -69,7 +69,7 @@ class Writer {
|
||||
/// Leave the block we are in
|
||||
void exitBlock();
|
||||
|
||||
/// Write the openedKey and the required indentation
|
||||
/// Write the opened_key and the required indentation
|
||||
void writeKey();
|
||||
/// Output some taps to represent the indentation level
|
||||
void writeIndentation();
|
||||
|
||||
+20
-3
@@ -52,7 +52,7 @@ String trim(const String& s){
|
||||
}
|
||||
}
|
||||
|
||||
String trimLeft(const String& s) {
|
||||
String trim_left(const String& s) {
|
||||
size_t start = s.find_first_not_of(_(' '));
|
||||
if (start == String::npos) {
|
||||
return String();
|
||||
@@ -63,7 +63,7 @@ String trimLeft(const String& s) {
|
||||
|
||||
// ----------------------------------------------------------------------------- : Words
|
||||
|
||||
String lastWord(const String& s) {
|
||||
String last_word(const String& s) {
|
||||
size_t endLastWord = s.find_last_not_of(_(' '));
|
||||
size_t startLastWord = s.find_last_of( _(' '), endLastWord);
|
||||
if (endLastWord == String::npos) {
|
||||
@@ -75,7 +75,7 @@ String lastWord(const String& s) {
|
||||
}
|
||||
}
|
||||
|
||||
String stripLastWord(const String& s) {
|
||||
String strip_last_word(const String& s) {
|
||||
size_t endLastWord = s.find_last_not_of(_(' '));
|
||||
size_t startLastWord = s.find_last_of(_(' '), endLastWord);
|
||||
if (endLastWord == String::npos || startLastWord == String::npos) {
|
||||
@@ -116,3 +116,20 @@ String capitalize(const String& s) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String cannocial_name_form(const String& str) {
|
||||
String ret;
|
||||
ret.reserve(str.size());
|
||||
bool leading = true;
|
||||
FOR_EACH_CONST(c, str) {
|
||||
if ((c == _('_') || c == _(' ')) && !leading) {
|
||||
ret += _('_');
|
||||
} else if (isAlnum(c)) {
|
||||
ret += toLower(c);
|
||||
leading = false;
|
||||
} else {
|
||||
// ignore non alpha numeric
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
+15
-13
@@ -52,9 +52,9 @@ typedef IF_UNICODE(wchar_t, char) Char;
|
||||
String decodeUTF8BOM(const String& s);
|
||||
|
||||
/// UTF8 Byte order mark for writing at the start of files
|
||||
/** In non-unicode builds it is UTF8 encoded \xFEFF
|
||||
* In unicode builds it is a normal \xFEFF
|
||||
*/
|
||||
/** In non-unicode builds it is UTF8 encoded \xFEFF.
|
||||
* In unicode builds it is a normal \xFEFF.
|
||||
*/
|
||||
const Char BYTE_ORDER_MARK[] = IF_UNICODE(L"\xFEFF", "\xEF\xBB\xBF");
|
||||
|
||||
/// Writes a string to an output stream, encoded as UTF8
|
||||
@@ -79,30 +79,32 @@ inline Char toLower(Char c) { return IF_UNICODE( towlower(c) , tolower(c) ); }
|
||||
String trim(const String&);
|
||||
|
||||
/// Remove whitespace from the start of a string
|
||||
String trimLeft(const String&);
|
||||
String trim_left(const String&);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Words
|
||||
|
||||
/// Returns the last word in a string
|
||||
String lastWord(const String&);
|
||||
String last_word(const String&);
|
||||
|
||||
/// Remove the last word from a string, leaves whitespace before that word
|
||||
String stripLastWord(const String&);
|
||||
String strip_last_word(const String&);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Caseing
|
||||
|
||||
/// Make each word in a string start with an upper case character.
|
||||
/// for use in menus
|
||||
/** for use in menus */
|
||||
String capitalize(const String&);
|
||||
|
||||
/// Make the first word in a string start with an upper case character.
|
||||
/// for use in dialogs
|
||||
String capitalizeSentence(const String&);
|
||||
/** for use in dialogs */
|
||||
String capitalize_sentence(const String&);
|
||||
|
||||
/// Convert a field name to cannocial form: lower case and ' ' instead of '_'
|
||||
/// non alphanumeric characters are ignored
|
||||
/// "camalCase" is converted to words "camel case"
|
||||
String cannocialNameForm(const String&);
|
||||
/// Convert a field name to cannocial form
|
||||
/** - lower case and '_' instead of ' '.
|
||||
* - non alphanumeric characters are droped
|
||||
* - "camalCase" is converted to words "camel case" (TODO)
|
||||
*/
|
||||
String cannocial_name_form(const String&);
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
#ifndef HEADER_UTIL_VERSION
|
||||
#define HEADER_UTIL_VERSION
|
||||
|
||||
/** @file util/version.hpp
|
||||
*
|
||||
* @brief Utility functions related to version numbers.
|
||||
* This header also stores the MSE version number.
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : Version datatype
|
||||
|
||||
/// A version number
|
||||
struct Version {
|
||||
public:
|
||||
Version() : version(0) {}
|
||||
Version(UInt version) : version(version) {}
|
||||
|
||||
bool operator < (Version v) { return version < v.versionSuffix; }
|
||||
|
||||
/// Convert a version number to a string
|
||||
String toString();
|
||||
|
||||
/// Convert a string to a version number
|
||||
static Version fromString(UInt version);
|
||||
|
||||
private:
|
||||
UInt version; ///< Version number encoded as aabbcc, where a=major, b=minor, c=revision
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Versions
|
||||
|
||||
/// The verwsion number of MSE
|
||||
const Version app_version = 000300; // 0.3.0
|
||||
const Char* version_suffix = _(" (beta)");
|
||||
|
||||
/// File version, usually the same as program version,
|
||||
/** When no files are changed the file version is not incremented
|
||||
* Changes:
|
||||
* 0.2.0 : start of version numbering practice
|
||||
* 0.2.2 : _("include file")
|
||||
* 0.2.6 : fix in settings loading
|
||||
* 0.2.7 : new tag system, different style of close tags
|
||||
* 0.3.0 : port of code to C++
|
||||
*/
|
||||
const Version file_version = 000300; // 0.3.0
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
Reference in New Issue
Block a user