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:
twanvl
2006-10-11 22:26:55 +00:00
parent 33abea6221
commit 9de743030e
51 changed files with 1041 additions and 767 deletions
+22 -22
View File
@@ -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();
}
}
+8 -8
View File
@@ -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
View File
@@ -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;
}
+4 -4
View File
@@ -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
View File
@@ -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) {
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
+58
View File
@@ -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