mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-12 13:37:00 -04:00
Change tabs to two spaces.
This commit is contained in:
+70
-70
@@ -17,107 +17,107 @@ DECLARE_TYPEOF_COLLECTION(Action*);
|
||||
DECLARE_TYPEOF_COLLECTION(ActionListener*);
|
||||
|
||||
ActionStack::ActionStack()
|
||||
: save_point(nullptr)
|
||||
, last_was_add(false)
|
||||
: save_point(nullptr)
|
||||
, last_was_add(false)
|
||||
{}
|
||||
|
||||
ActionStack::~ActionStack() {
|
||||
// we own the actions, delete them
|
||||
FOR_EACH(a, undo_actions) delete a;
|
||||
FOR_EACH(a, redo_actions) delete a;
|
||||
// we own the actions, delete them
|
||||
FOR_EACH(a, undo_actions) delete a;
|
||||
FOR_EACH(a, redo_actions) delete a;
|
||||
}
|
||||
|
||||
void ActionStack::addAction(Action* action, bool allow_merge) {
|
||||
if (!action) return; // no action
|
||||
action->perform(false); // TODO: delete action if perform throws
|
||||
tellListeners(*action, false);
|
||||
// clear redo list
|
||||
if (!redo_actions.empty()) allow_merge = false; // don't merge after undo
|
||||
FOR_EACH(a, redo_actions) delete a;
|
||||
redo_actions.clear();
|
||||
// try to merge?
|
||||
if (allow_merge && !undo_actions.empty() &&
|
||||
last_was_add && // never merge with something that was redone once already
|
||||
undo_actions.back() != save_point && // never merge with the save point
|
||||
undo_actions.back()->merge(*action) // merged with top undo action
|
||||
) {
|
||||
delete action;
|
||||
} else {
|
||||
undo_actions.push_back(action);
|
||||
}
|
||||
last_was_add = true;
|
||||
if (!action) return; // no action
|
||||
action->perform(false); // TODO: delete action if perform throws
|
||||
tellListeners(*action, false);
|
||||
// clear redo list
|
||||
if (!redo_actions.empty()) allow_merge = false; // don't merge after undo
|
||||
FOR_EACH(a, redo_actions) delete a;
|
||||
redo_actions.clear();
|
||||
// try to merge?
|
||||
if (allow_merge && !undo_actions.empty() &&
|
||||
last_was_add && // never merge with something that was redone once already
|
||||
undo_actions.back() != save_point && // never merge with the save point
|
||||
undo_actions.back()->merge(*action) // merged with top undo action
|
||||
) {
|
||||
delete action;
|
||||
} else {
|
||||
undo_actions.push_back(action);
|
||||
}
|
||||
last_was_add = true;
|
||||
}
|
||||
|
||||
void ActionStack::undo() {
|
||||
assert(canUndo());
|
||||
if (!canUndo()) return;
|
||||
Action* action = undo_actions.back();
|
||||
action->perform(true);
|
||||
tellListeners(*action, true);
|
||||
// move to redo stack
|
||||
undo_actions.pop_back();
|
||||
redo_actions.push_back(action);
|
||||
last_was_add = false;
|
||||
assert(canUndo());
|
||||
if (!canUndo()) return;
|
||||
Action* action = undo_actions.back();
|
||||
action->perform(true);
|
||||
tellListeners(*action, true);
|
||||
// move to redo stack
|
||||
undo_actions.pop_back();
|
||||
redo_actions.push_back(action);
|
||||
last_was_add = false;
|
||||
}
|
||||
void ActionStack::redo() {
|
||||
assert(canRedo());
|
||||
if (!canRedo()) return;
|
||||
Action* action = redo_actions.back();
|
||||
action->perform(false);
|
||||
tellListeners(*action, false);
|
||||
// move to undo stack
|
||||
redo_actions.pop_back();
|
||||
undo_actions.push_back(action);
|
||||
last_was_add = false;
|
||||
assert(canRedo());
|
||||
if (!canRedo()) return;
|
||||
Action* action = redo_actions.back();
|
||||
action->perform(false);
|
||||
tellListeners(*action, false);
|
||||
// move to undo stack
|
||||
redo_actions.pop_back();
|
||||
undo_actions.push_back(action);
|
||||
last_was_add = false;
|
||||
}
|
||||
|
||||
bool ActionStack::canUndo() const {
|
||||
return !undo_actions.empty();
|
||||
return !undo_actions.empty();
|
||||
}
|
||||
bool ActionStack::canRedo() const {
|
||||
return !redo_actions.empty();
|
||||
return !redo_actions.empty();
|
||||
}
|
||||
|
||||
String ActionStack::undoName() const {
|
||||
if (canUndo()) {
|
||||
return _(" ") + capitalize(undo_actions.back()->getName(true));
|
||||
} else {
|
||||
return wxEmptyString;
|
||||
}
|
||||
if (canUndo()) {
|
||||
return _(" ") + capitalize(undo_actions.back()->getName(true));
|
||||
} else {
|
||||
return wxEmptyString;
|
||||
}
|
||||
}
|
||||
String ActionStack::redoName() const {
|
||||
if (canRedo()) {
|
||||
return _(" ") + capitalize(redo_actions.back()->getName(false));
|
||||
} else {
|
||||
return wxEmptyString;
|
||||
}
|
||||
if (canRedo()) {
|
||||
return _(" ") + capitalize(redo_actions.back()->getName(false));
|
||||
} else {
|
||||
return wxEmptyString;
|
||||
}
|
||||
}
|
||||
|
||||
bool ActionStack::atSavePoint() const {
|
||||
return (undo_actions.empty() && save_point == nullptr)
|
||||
|| (undo_actions.back() == save_point);
|
||||
return (undo_actions.empty() && save_point == nullptr)
|
||||
|| (undo_actions.back() == save_point);
|
||||
}
|
||||
void ActionStack::setSavePoint() {
|
||||
if (undo_actions.empty()) {
|
||||
save_point = nullptr;
|
||||
} else {
|
||||
save_point = undo_actions.back();
|
||||
}
|
||||
if (undo_actions.empty()) {
|
||||
save_point = nullptr;
|
||||
} else {
|
||||
save_point = undo_actions.back();
|
||||
}
|
||||
}
|
||||
|
||||
void ActionStack::addListener(ActionListener* listener) {
|
||||
listeners.push_back(listener);
|
||||
listeners.push_back(listener);
|
||||
}
|
||||
void ActionStack::removeListener(ActionListener* listener) {
|
||||
listeners.erase(
|
||||
std::remove(
|
||||
listeners.begin(),
|
||||
listeners.end(),
|
||||
listener
|
||||
),
|
||||
listeners.end()
|
||||
);
|
||||
listeners.erase(
|
||||
std::remove(
|
||||
listeners.begin(),
|
||||
listeners.end(),
|
||||
listener
|
||||
),
|
||||
listeners.end()
|
||||
);
|
||||
}
|
||||
void ActionStack::tellListeners(const Action& action, bool undone) {
|
||||
FOR_EACH(l, listeners) l->onAction(action, undone);
|
||||
FOR_EACH(l, listeners) l->onAction(action, undone);
|
||||
}
|
||||
|
||||
+86
-86
@@ -21,26 +21,26 @@
|
||||
*/
|
||||
class Action {
|
||||
public:
|
||||
virtual ~Action() {};
|
||||
|
||||
/// Name of the action, for use in strings like "Undo <name>"
|
||||
virtual String getName(bool to_undo) const = 0;
|
||||
|
||||
/// Perform the action
|
||||
/** @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 to_undo = true/false,
|
||||
* the first time with to_undo = false
|
||||
*/
|
||||
virtual void perform(bool to_undo) = 0;
|
||||
|
||||
/// Try to merge another action to the end of this action.
|
||||
/** Either: return false and do nothing
|
||||
* Or: return true and change this action to incorporate both actions
|
||||
*/
|
||||
virtual bool merge(const Action& action) { return false; }
|
||||
virtual ~Action() {};
|
||||
|
||||
/// Name of the action, for use in strings like "Undo <name>"
|
||||
virtual String getName(bool to_undo) const = 0;
|
||||
|
||||
/// Perform the action
|
||||
/** @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 to_undo = true/false,
|
||||
* the first time with to_undo = false
|
||||
*/
|
||||
virtual void perform(bool to_undo) = 0;
|
||||
|
||||
/// Try to merge another action to the end of this action.
|
||||
/** Either: return false and do nothing
|
||||
* Or: return true and change this action to incorporate both actions
|
||||
*/
|
||||
virtual bool merge(const Action& action) { return false; }
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Action listeners
|
||||
@@ -48,9 +48,9 @@ class Action {
|
||||
/// Base class/interface for objects that listen to actions
|
||||
class ActionListener {
|
||||
public:
|
||||
virtual ~ActionListener() {}
|
||||
/// Notification that an action a has been performed or undone
|
||||
virtual void onAction(const Action& a, bool undone) = 0;
|
||||
virtual ~ActionListener() {}
|
||||
/// Notification that an action a has been performed or undone
|
||||
virtual void onAction(const Action& a, bool undone) = 0;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Action stack
|
||||
@@ -63,61 +63,61 @@ class ActionListener {
|
||||
*/
|
||||
class ActionStack {
|
||||
public:
|
||||
ActionStack();
|
||||
~ActionStack();
|
||||
|
||||
/// Add an action to the stack, and perform that action.
|
||||
/** Tells all listeners about the action.
|
||||
* The ActionStack takes ownership of the action.
|
||||
* If allow_merge == true then we attempt to merge this action with previous ones
|
||||
*/
|
||||
void addAction(Action* action, bool allow_merge = true);
|
||||
|
||||
/// Undoes the last action that was (re)done
|
||||
/** @pre canUndo() */
|
||||
void undo();
|
||||
/// Redoes the last action that was undone
|
||||
/** @pre canRedo() */
|
||||
void redo();
|
||||
|
||||
/// Is undoing possible?
|
||||
bool canUndo() const;
|
||||
/// Is redoing possible?
|
||||
bool canRedo() const;
|
||||
|
||||
/// Name of the action that will be undone next, in the form " <Action>"
|
||||
/** If there is no action to undo returns "" */
|
||||
String undoName() const;
|
||||
/// Name of the action that will be redone next " <Action>"
|
||||
/** If there is no action to undo returns "" */
|
||||
String redoName() const;
|
||||
|
||||
/// Is the file currently at a 'savepoint'?
|
||||
/** This is the last point at which the file was saved. */
|
||||
bool atSavePoint() const;
|
||||
/// Indicate that the file is at a savepoint.
|
||||
void setSavePoint();
|
||||
|
||||
/// Add an action listener
|
||||
void addListener(ActionListener* listener);
|
||||
/// Remove an action listener
|
||||
void removeListener(ActionListener* listener);
|
||||
/// Tell all listeners about an action
|
||||
void tellListeners(const Action&, bool undone);
|
||||
|
||||
ActionStack();
|
||||
~ActionStack();
|
||||
|
||||
/// Add an action to the stack, and perform that action.
|
||||
/** Tells all listeners about the action.
|
||||
* The ActionStack takes ownership of the action.
|
||||
* If allow_merge == true then we attempt to merge this action with previous ones
|
||||
*/
|
||||
void addAction(Action* action, bool allow_merge = true);
|
||||
|
||||
/// Undoes the last action that was (re)done
|
||||
/** @pre canUndo() */
|
||||
void undo();
|
||||
/// Redoes the last action that was undone
|
||||
/** @pre canRedo() */
|
||||
void redo();
|
||||
|
||||
/// Is undoing possible?
|
||||
bool canUndo() const;
|
||||
/// Is redoing possible?
|
||||
bool canRedo() const;
|
||||
|
||||
/// Name of the action that will be undone next, in the form " <Action>"
|
||||
/** If there is no action to undo returns "" */
|
||||
String undoName() const;
|
||||
/// Name of the action that will be redone next " <Action>"
|
||||
/** If there is no action to undo returns "" */
|
||||
String redoName() const;
|
||||
|
||||
/// Is the file currently at a 'savepoint'?
|
||||
/** This is the last point at which the file was saved. */
|
||||
bool atSavePoint() const;
|
||||
/// Indicate that the file is at a savepoint.
|
||||
void setSavePoint();
|
||||
|
||||
/// Add an action listener
|
||||
void addListener(ActionListener* listener);
|
||||
/// Remove an action listener
|
||||
void removeListener(ActionListener* listener);
|
||||
/// Tell all listeners about an action
|
||||
void tellListeners(const Action&, bool undone);
|
||||
|
||||
private:
|
||||
/// Actions to be undone.
|
||||
/** Owns the action objects! */
|
||||
vector<Action*> undo_actions;
|
||||
/// Actions to be redone
|
||||
/** Owns the action objects! */
|
||||
vector<Action*> redo_actions;
|
||||
/// Point at which the file was saved, corresponds to the top of the undo stack at that point
|
||||
Action* save_point;
|
||||
/// Was the last thing the user did addAction? (as opposed to undo/redo)
|
||||
bool last_was_add;
|
||||
/// Objects that are listening to actions
|
||||
vector<ActionListener*> listeners;
|
||||
/// Actions to be undone.
|
||||
/** Owns the action objects! */
|
||||
vector<Action*> undo_actions;
|
||||
/// Actions to be redone
|
||||
/** Owns the action objects! */
|
||||
vector<Action*> redo_actions;
|
||||
/// Point at which the file was saved, corresponds to the top of the undo stack at that point
|
||||
Action* save_point;
|
||||
/// Was the last thing the user did addAction? (as opposed to undo/redo)
|
||||
bool last_was_add;
|
||||
/// Objects that are listening to actions
|
||||
vector<ActionListener*> listeners;
|
||||
};
|
||||
|
||||
|
||||
@@ -126,21 +126,21 @@ class ActionStack {
|
||||
/// Tests if variable has the type Type.
|
||||
/** Uses dynamic cast, so Type must have a virtual function.
|
||||
*/
|
||||
#define TYPE_CASE_(variable, Type) \
|
||||
if (dynamic_cast<const Type*>(&variable))
|
||||
#define TYPE_CASE_(variable, Type) \
|
||||
if (dynamic_cast<const Type*>(&variable))
|
||||
|
||||
/// Tests if variable has the type Type.
|
||||
/** If this is the case, makes variable have type Type inside the statement.
|
||||
*
|
||||
* Uses dynamic cast, so Type must have a virtual function.
|
||||
*/
|
||||
#define TYPE_CASE(variable, Type) \
|
||||
pair<const Type*,bool> Type##variable \
|
||||
(dynamic_cast<const Type*>(&variable), true); \
|
||||
if (Type##variable.first) \
|
||||
for (const Type& variable = *Type##variable.first ; \
|
||||
Type##variable.second ; \
|
||||
Type##variable.second = false)
|
||||
#define TYPE_CASE(variable, Type) \
|
||||
pair<const Type*,bool> Type##variable \
|
||||
(dynamic_cast<const Type*>(&variable), true); \
|
||||
if (Type##variable.first) \
|
||||
for (const Type& variable = *Type##variable.first ; \
|
||||
Type##variable.second ; \
|
||||
Type##variable.second = false)
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+28
-28
@@ -19,35 +19,35 @@
|
||||
/** Age is counted using a global variable */
|
||||
class Age {
|
||||
public:
|
||||
/// Construct a new age value
|
||||
Age() {
|
||||
update();
|
||||
}
|
||||
/// Create a special age
|
||||
/** 0: dummy value, used for other purposes
|
||||
* 1: before 'beginning of time', the age conceptually just before program start
|
||||
* 2..: normal ages
|
||||
*/
|
||||
Age(AtomicIntEquiv age) : age(age) {}
|
||||
|
||||
/// Update the age to become the newest one
|
||||
inline void update() {
|
||||
age = ++new_age;
|
||||
}
|
||||
|
||||
/// Compare two ages, smaller means earlier
|
||||
inline bool operator < (Age a) const { return age < a.age; }
|
||||
/// Compare two ages
|
||||
inline bool operator == (Age a) const { return age == a.age; }
|
||||
|
||||
/// A number corresponding to the age
|
||||
inline AtomicIntEquiv get() const { return age; }
|
||||
|
||||
/// Construct a new age value
|
||||
Age() {
|
||||
update();
|
||||
}
|
||||
/// Create a special age
|
||||
/** 0: dummy value, used for other purposes
|
||||
* 1: before 'beginning of time', the age conceptually just before program start
|
||||
* 2..: normal ages
|
||||
*/
|
||||
Age(AtomicIntEquiv age) : age(age) {}
|
||||
|
||||
/// Update the age to become the newest one
|
||||
inline void update() {
|
||||
age = ++new_age;
|
||||
}
|
||||
|
||||
/// Compare two ages, smaller means earlier
|
||||
inline bool operator < (Age a) const { return age < a.age; }
|
||||
/// Compare two ages
|
||||
inline bool operator == (Age a) const { return age == a.age; }
|
||||
|
||||
/// A number corresponding to the age
|
||||
inline AtomicIntEquiv get() const { return age; }
|
||||
|
||||
private:
|
||||
/// This age
|
||||
AtomicIntEquiv age;
|
||||
/// Global age counter, value of the last age created
|
||||
static AtomicInt new_age;
|
||||
/// This age
|
||||
AtomicIntEquiv age;
|
||||
/// Global age counter, value of the last age created
|
||||
static AtomicInt new_age;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+74
-74
@@ -13,112 +13,112 @@
|
||||
// ----------------------------------------------------------------------------- : Alignment
|
||||
|
||||
double align_delta_x(Alignment align, double box_width, double obj_width) {
|
||||
if (align & ALIGN_CENTER) return (box_width - obj_width) / 2;
|
||||
else if (align & ALIGN_RIGHT) return box_width - obj_width;
|
||||
else return 0;
|
||||
if (align & ALIGN_CENTER) return (box_width - obj_width) / 2;
|
||||
else if (align & ALIGN_RIGHT) return box_width - obj_width;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
|
||||
double align_delta_y(Alignment align, double box_height, double obj_height) {
|
||||
if (align & ALIGN_MIDDLE) return (box_height - obj_height) / 2;
|
||||
else if (align & ALIGN_BOTTOM) return box_height - obj_height;
|
||||
else return 0;
|
||||
if (align & ALIGN_MIDDLE) return (box_height - obj_height) / 2;
|
||||
else if (align & ALIGN_BOTTOM) return box_height - obj_height;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
RealPoint align_in_rect(Alignment align, const RealSize& to_align, const RealRect& outer) {
|
||||
return RealPoint(
|
||||
outer.x + align_delta_x(align, outer.width, to_align.width),
|
||||
outer.y + align_delta_y(align, outer.height, to_align.height)
|
||||
);
|
||||
return RealPoint(
|
||||
outer.x + align_delta_x(align, outer.width, to_align.width),
|
||||
outer.y + align_delta_y(align, outer.height, to_align.height)
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Reflection stuff
|
||||
|
||||
/// Convert a String to an Alignment
|
||||
Alignment from_string(const String& s) {
|
||||
int al = ALIGN_TOP_LEFT;
|
||||
if (s.find(_("left")) !=String::npos) al = ALIGN_LEFT | (al & ~ALIGN_HORIZONTAL);
|
||||
if (s.find(_("center")) !=String::npos) al = ALIGN_CENTER | (al & ~ALIGN_HORIZONTAL);
|
||||
if (s.find(_("right")) !=String::npos) al = ALIGN_RIGHT | (al & ~ALIGN_HORIZONTAL);
|
||||
|
||||
if (s.find(_("justify")) !=String::npos) al = ALIGN_JUSTIFY_WORDS | (al & ~ALIGN_FILL);
|
||||
if (s.find(_("justify-all")) !=String::npos) al = ALIGN_JUSTIFY_ALL | (al & ~ALIGN_FILL);
|
||||
if (s.find(_("shrink")) !=String::npos) al = ALIGN_STRETCH | (al & ~ALIGN_FILL);
|
||||
if (s.find(_("stretch")) !=String::npos) al = ALIGN_STRETCH | (al & ~ALIGN_FILL); // compatability
|
||||
|
||||
if (s.find(_("overflow")) !=String::npos) al |= ALIGN_IF_OVERFLOW;
|
||||
if (s.find(_("force")) ==String::npos) al |= ALIGN_IF_SOFTBREAK; // force = !if_softbreak
|
||||
|
||||
if (s.find(_("top")) !=String::npos) al = ALIGN_TOP | (al & ~ALIGN_VERTICAL);
|
||||
if (s.find(_("middle")) !=String::npos) al = ALIGN_MIDDLE | (al & ~ALIGN_VERTICAL);
|
||||
if (s.find(_("bottom")) !=String::npos) al = ALIGN_BOTTOM | (al & ~ALIGN_VERTICAL);
|
||||
|
||||
return static_cast<Alignment>(al);
|
||||
int al = ALIGN_TOP_LEFT;
|
||||
if (s.find(_("left")) !=String::npos) al = ALIGN_LEFT | (al & ~ALIGN_HORIZONTAL);
|
||||
if (s.find(_("center")) !=String::npos) al = ALIGN_CENTER | (al & ~ALIGN_HORIZONTAL);
|
||||
if (s.find(_("right")) !=String::npos) al = ALIGN_RIGHT | (al & ~ALIGN_HORIZONTAL);
|
||||
|
||||
if (s.find(_("justify")) !=String::npos) al = ALIGN_JUSTIFY_WORDS | (al & ~ALIGN_FILL);
|
||||
if (s.find(_("justify-all")) !=String::npos) al = ALIGN_JUSTIFY_ALL | (al & ~ALIGN_FILL);
|
||||
if (s.find(_("shrink")) !=String::npos) al = ALIGN_STRETCH | (al & ~ALIGN_FILL);
|
||||
if (s.find(_("stretch")) !=String::npos) al = ALIGN_STRETCH | (al & ~ALIGN_FILL); // compatability
|
||||
|
||||
if (s.find(_("overflow")) !=String::npos) al |= ALIGN_IF_OVERFLOW;
|
||||
if (s.find(_("force")) ==String::npos) al |= ALIGN_IF_SOFTBREAK; // force = !if_softbreak
|
||||
|
||||
if (s.find(_("top")) !=String::npos) al = ALIGN_TOP | (al & ~ALIGN_VERTICAL);
|
||||
if (s.find(_("middle")) !=String::npos) al = ALIGN_MIDDLE | (al & ~ALIGN_VERTICAL);
|
||||
if (s.find(_("bottom")) !=String::npos) al = ALIGN_BOTTOM | (al & ~ALIGN_VERTICAL);
|
||||
|
||||
return static_cast<Alignment>(al);
|
||||
}
|
||||
|
||||
/// Convert an Alignment to a String
|
||||
String to_string(Alignment align) {
|
||||
String ret;
|
||||
// vertical
|
||||
if (align & ALIGN_TOP) ret += _("top ");
|
||||
if (align & ALIGN_MIDDLE) ret += _("middle ");
|
||||
if (align & ALIGN_BOTTOM) ret += _("bottom ");
|
||||
// horizontal
|
||||
if (align & ALIGN_LEFT) ret += _("left ");
|
||||
if (align & ALIGN_CENTER) ret += _("center ");
|
||||
if (align & ALIGN_RIGHT) ret += _("right ");
|
||||
// fill
|
||||
if (align & ALIGN_FILL) {
|
||||
// force = !if_softbreak && some fill type
|
||||
if (!(align & ALIGN_IF_SOFTBREAK)) ret += _("force ");
|
||||
// fill
|
||||
if (align & ALIGN_STRETCH) ret += _("stretch ");
|
||||
if (align & ALIGN_JUSTIFY_WORDS) ret += _("justify ");
|
||||
if (align & ALIGN_JUSTIFY_ALL) ret += _("justify-all ");
|
||||
// modifier
|
||||
if (align & ALIGN_IF_OVERFLOW) ret += _("if-overflow ");
|
||||
}
|
||||
ret.resize(ret.size() - 1); // drop trailing ' '
|
||||
return ret;
|
||||
String ret;
|
||||
// vertical
|
||||
if (align & ALIGN_TOP) ret += _("top ");
|
||||
if (align & ALIGN_MIDDLE) ret += _("middle ");
|
||||
if (align & ALIGN_BOTTOM) ret += _("bottom ");
|
||||
// horizontal
|
||||
if (align & ALIGN_LEFT) ret += _("left ");
|
||||
if (align & ALIGN_CENTER) ret += _("center ");
|
||||
if (align & ALIGN_RIGHT) ret += _("right ");
|
||||
// fill
|
||||
if (align & ALIGN_FILL) {
|
||||
// force = !if_softbreak && some fill type
|
||||
if (!(align & ALIGN_IF_SOFTBREAK)) ret += _("force ");
|
||||
// fill
|
||||
if (align & ALIGN_STRETCH) ret += _("stretch ");
|
||||
if (align & ALIGN_JUSTIFY_WORDS) ret += _("justify ");
|
||||
if (align & ALIGN_JUSTIFY_ALL) ret += _("justify-all ");
|
||||
// modifier
|
||||
if (align & ALIGN_IF_OVERFLOW) ret += _("if-overflow ");
|
||||
}
|
||||
ret.resize(ret.size() - 1); // drop trailing ' '
|
||||
return ret;
|
||||
}
|
||||
|
||||
// we need custom io, because there can be both a horizontal and a vertical component
|
||||
|
||||
template <> void Reader::handle(Alignment& align) {
|
||||
align = from_string(getValue());
|
||||
align = from_string(getValue());
|
||||
}
|
||||
template <> void Writer::handle(const Alignment& align) {
|
||||
handle(to_string(align));
|
||||
handle(to_string(align));
|
||||
}
|
||||
template <> void GetDefaultMember::handle(const Alignment& align) {
|
||||
handle(to_string(align));
|
||||
handle(to_string(align));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Direction
|
||||
|
||||
IMPLEMENT_REFLECTION_ENUM(Direction) {
|
||||
VALUE_N("left to right", LEFT_TO_RIGHT);
|
||||
VALUE_N("right to left", RIGHT_TO_LEFT);
|
||||
VALUE_N("top to bottom", TOP_TO_BOTTOM);
|
||||
VALUE_N("bottom to top", BOTTOM_TO_TOP);
|
||||
VALUE_N("horizontal", LEFT_TO_RIGHT);
|
||||
VALUE_N("vertical", TOP_TO_BOTTOM);
|
||||
VALUE_N("top-right to bottom-left", TOP_RIGHT_TO_BOTTOM_LEFT);
|
||||
VALUE_N("bottom-left to top-right", BOTTOM_LEFT_TO_TOP_RIGHT);
|
||||
VALUE_N("top-left to bottom-right", TOP_LEFT_TO_BOTTOM_RIGHT);
|
||||
VALUE_N("bottom-right to top-left", BOTTOM_RIGHT_TO_TOP_LEFT);
|
||||
VALUE_N("left to right", LEFT_TO_RIGHT);
|
||||
VALUE_N("right to left", RIGHT_TO_LEFT);
|
||||
VALUE_N("top to bottom", TOP_TO_BOTTOM);
|
||||
VALUE_N("bottom to top", BOTTOM_TO_TOP);
|
||||
VALUE_N("horizontal", LEFT_TO_RIGHT);
|
||||
VALUE_N("vertical", TOP_TO_BOTTOM);
|
||||
VALUE_N("top-right to bottom-left", TOP_RIGHT_TO_BOTTOM_LEFT);
|
||||
VALUE_N("bottom-left to top-right", BOTTOM_LEFT_TO_TOP_RIGHT);
|
||||
VALUE_N("top-left to bottom-right", TOP_LEFT_TO_BOTTOM_RIGHT);
|
||||
VALUE_N("bottom-right to top-left", BOTTOM_RIGHT_TO_TOP_LEFT);
|
||||
}
|
||||
|
||||
RealPoint move_in_direction(Direction dir, const RealPoint& point, const RealSize to_move, double spacing) {
|
||||
switch (dir) {
|
||||
case LEFT_TO_RIGHT: return RealPoint(point.x + to_move.width + spacing, point.y);
|
||||
case RIGHT_TO_LEFT: return RealPoint(point.x - to_move.width - spacing, point.y);
|
||||
case TOP_TO_BOTTOM: return RealPoint(point.x, point.y + to_move.height + spacing);
|
||||
case BOTTOM_TO_TOP: return RealPoint(point.x, point.y - to_move.height - spacing);
|
||||
case TOP_RIGHT_TO_BOTTOM_LEFT: return RealPoint(point.x - to_move.width - spacing, point.y + to_move.height + spacing);
|
||||
case BOTTOM_LEFT_TO_TOP_RIGHT: return RealPoint(point.x + to_move.width + spacing, point.y - to_move.height - spacing);
|
||||
case TOP_LEFT_TO_BOTTOM_RIGHT: return RealPoint(point.x + to_move.width + spacing, point.y + to_move.height + spacing);
|
||||
case BOTTOM_RIGHT_TO_TOP_LEFT: return RealPoint(point.x - to_move.width - spacing, point.y - to_move.height - spacing);
|
||||
default: return point; // should not happen
|
||||
}
|
||||
switch (dir) {
|
||||
case LEFT_TO_RIGHT: return RealPoint(point.x + to_move.width + spacing, point.y);
|
||||
case RIGHT_TO_LEFT: return RealPoint(point.x - to_move.width - spacing, point.y);
|
||||
case TOP_TO_BOTTOM: return RealPoint(point.x, point.y + to_move.height + spacing);
|
||||
case BOTTOM_TO_TOP: return RealPoint(point.x, point.y - to_move.height - spacing);
|
||||
case TOP_RIGHT_TO_BOTTOM_LEFT: return RealPoint(point.x - to_move.width - spacing, point.y + to_move.height + spacing);
|
||||
case BOTTOM_LEFT_TO_TOP_RIGHT: return RealPoint(point.x + to_move.width + spacing, point.y - to_move.height - spacing);
|
||||
case TOP_LEFT_TO_BOTTOM_RIGHT: return RealPoint(point.x + to_move.width + spacing, point.y + to_move.height + spacing);
|
||||
case BOTTOM_RIGHT_TO_TOP_LEFT: return RealPoint(point.x - to_move.width - spacing, point.y - to_move.height - spacing);
|
||||
default: return point; // should not happen
|
||||
}
|
||||
}
|
||||
|
||||
+28
-28
@@ -17,31 +17,31 @@
|
||||
// Alignment in a textbox, specifies both horizontal and vertical alignment
|
||||
enum Alignment
|
||||
// horizontal
|
||||
{ ALIGN_LEFT = 0x01
|
||||
, ALIGN_CENTER = 0x02
|
||||
, ALIGN_RIGHT = 0x04
|
||||
, ALIGN_HORIZONTAL = ALIGN_LEFT | ALIGN_CENTER | ALIGN_RIGHT
|
||||
{ ALIGN_LEFT = 0x01
|
||||
, ALIGN_CENTER = 0x02
|
||||
, ALIGN_RIGHT = 0x04
|
||||
, ALIGN_HORIZONTAL = ALIGN_LEFT | ALIGN_CENTER | ALIGN_RIGHT
|
||||
// horizontal filling
|
||||
, ALIGN_STRETCH = 0x10
|
||||
, ALIGN_JUSTIFY_WORDS = 0x20
|
||||
, ALIGN_JUSTIFY_ALL = 0x40
|
||||
, ALIGN_FILL = ALIGN_STRETCH | ALIGN_JUSTIFY_WORDS | ALIGN_JUSTIFY_ALL
|
||||
, ALIGN_STRETCH = 0x10
|
||||
, ALIGN_JUSTIFY_WORDS = 0x20
|
||||
, ALIGN_JUSTIFY_ALL = 0x40
|
||||
, ALIGN_FILL = ALIGN_STRETCH | ALIGN_JUSTIFY_WORDS | ALIGN_JUSTIFY_ALL
|
||||
// horizontal fill modifiers
|
||||
, ALIGN_IF_OVERFLOW = 0x1000 // only fill if text_width > box_width
|
||||
, ALIGN_IF_SOFTBREAK = 0x2000 // only fill before soft line breaks
|
||||
, ALIGN_IF_OVERFLOW = 0x1000 // only fill if text_width > box_width
|
||||
, ALIGN_IF_SOFTBREAK = 0x2000 // only fill before soft line breaks
|
||||
// vertical
|
||||
, ALIGN_TOP = 0x100
|
||||
, ALIGN_MIDDLE = 0x200
|
||||
, ALIGN_BOTTOM = 0x400
|
||||
, ALIGN_VERTICAL = ALIGN_TOP | ALIGN_MIDDLE | ALIGN_BOTTOM
|
||||
, ALIGN_TOP = 0x100
|
||||
, ALIGN_MIDDLE = 0x200
|
||||
, ALIGN_BOTTOM = 0x400
|
||||
, ALIGN_VERTICAL = ALIGN_TOP | ALIGN_MIDDLE | ALIGN_BOTTOM
|
||||
// modifiers
|
||||
// common combinations
|
||||
, ALIGN_TOP_LEFT = ALIGN_TOP | ALIGN_LEFT
|
||||
, ALIGN_TOP_CENTER = ALIGN_TOP | ALIGN_CENTER
|
||||
, ALIGN_TOP_RIGHT = ALIGN_TOP | ALIGN_RIGHT
|
||||
, ALIGN_MIDDLE_LEFT = ALIGN_MIDDLE | ALIGN_LEFT
|
||||
, ALIGN_MIDDLE_CENTER = ALIGN_MIDDLE | ALIGN_CENTER
|
||||
, ALIGN_MIDDLE_RIGHT = ALIGN_MIDDLE | ALIGN_RIGHT
|
||||
, ALIGN_TOP_LEFT = ALIGN_TOP | ALIGN_LEFT
|
||||
, ALIGN_TOP_CENTER = ALIGN_TOP | ALIGN_CENTER
|
||||
, ALIGN_TOP_RIGHT = ALIGN_TOP | ALIGN_RIGHT
|
||||
, ALIGN_MIDDLE_LEFT = ALIGN_MIDDLE | ALIGN_LEFT
|
||||
, ALIGN_MIDDLE_CENTER = ALIGN_MIDDLE | ALIGN_CENTER
|
||||
, ALIGN_MIDDLE_RIGHT = ALIGN_MIDDLE | ALIGN_RIGHT
|
||||
};
|
||||
|
||||
|
||||
@@ -60,14 +60,14 @@ RealPoint align_in_rect(Alignment align, const RealSize& to_align, const RealRec
|
||||
|
||||
/// Direction to place something in
|
||||
enum Direction {
|
||||
LEFT_TO_RIGHT, RIGHT_TO_LEFT,
|
||||
TOP_TO_BOTTOM, BOTTOM_TO_TOP,
|
||||
BOTTOM_RIGHT_TO_TOP_LEFT,
|
||||
TOP_LEFT_TO_BOTTOM_RIGHT,
|
||||
BOTTOM_LEFT_TO_TOP_RIGHT,
|
||||
TOP_RIGHT_TO_BOTTOM_LEFT,
|
||||
HORIZONTAL = LEFT_TO_RIGHT,
|
||||
VERTICAL = TOP_TO_BOTTOM
|
||||
LEFT_TO_RIGHT, RIGHT_TO_LEFT,
|
||||
TOP_TO_BOTTOM, BOTTOM_TO_TOP,
|
||||
BOTTOM_RIGHT_TO_TOP_LEFT,
|
||||
TOP_LEFT_TO_BOTTOM_RIGHT,
|
||||
BOTTOM_LEFT_TO_TOP_RIGHT,
|
||||
TOP_RIGHT_TO_BOTTOM_LEFT,
|
||||
HORIZONTAL = LEFT_TO_RIGHT,
|
||||
VERTICAL = TOP_TO_BOTTOM
|
||||
};
|
||||
|
||||
/// Move a point in a direction
|
||||
|
||||
+12
-12
@@ -32,44 +32,44 @@ const Radians rad360 = 2.0*M_PI;
|
||||
|
||||
/// Are two floating point numbers equal up to a small epsilon?
|
||||
inline bool almost_equal(double x, double y) {
|
||||
return fabs(x-y) < 1e-10;
|
||||
return fabs(x-y) < 1e-10;
|
||||
}
|
||||
inline bool is_rad0(double x) {
|
||||
return almost_equal(x,0) || almost_equal(x,rad360);
|
||||
return almost_equal(x,0) || almost_equal(x,rad360);
|
||||
}
|
||||
inline bool is_rad90(double x) {
|
||||
return almost_equal(x,rad90);
|
||||
return almost_equal(x,rad90);
|
||||
}
|
||||
inline bool is_rad180(double x) {
|
||||
return almost_equal(x,rad180);
|
||||
return almost_equal(x,rad180);
|
||||
}
|
||||
inline bool is_rad270(double x) {
|
||||
return almost_equal(x,rad270);
|
||||
return almost_equal(x,rad270);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Angle functions
|
||||
|
||||
// mod as it should be: answer in range [0..m)
|
||||
inline double sane_fmod(double x, double m) {
|
||||
double ans = fmod(x,m);
|
||||
if (ans < 0) return ans + m;
|
||||
else return ans;
|
||||
double ans = fmod(x,m);
|
||||
if (ans < 0) return ans + m;
|
||||
else return ans;
|
||||
}
|
||||
|
||||
// constrain an angle to [0..2pi)
|
||||
inline Radians constrain_radians(Radians angle) {
|
||||
return sane_fmod(angle, 2*M_PI);
|
||||
return sane_fmod(angle, 2*M_PI);
|
||||
}
|
||||
|
||||
/// Is an angle a multiple of 90 degrees?
|
||||
inline bool is_straight(Radians angle) {
|
||||
return almost_equal(sane_fmod(angle+rad45,rad90), rad45);
|
||||
return almost_equal(sane_fmod(angle+rad45,rad90), rad45);
|
||||
}
|
||||
|
||||
/// Is an angle sideways (i.e. closer to 90 or 270 degrees than to 0 or 180 degrees)?
|
||||
inline bool is_sideways(Radians angle) {
|
||||
double a = sane_fmod(angle,M_PI);
|
||||
return (a > 0.25*M_PI && a < 0.75*M_PI);
|
||||
double a = sane_fmod(angle,M_PI);
|
||||
return (a > 0.25*M_PI && a < 0.75*M_PI);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+90
-90
@@ -17,100 +17,100 @@
|
||||
// ----------------------------------------------------------------------------- : AtomicInt : windows
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
#ifdef _MSC_VER
|
||||
extern "C" {
|
||||
LONG __cdecl _InterlockedIncrement(LONG volatile *Addend);
|
||||
LONG __cdecl _InterlockedDecrement(LONG volatile *Addend);
|
||||
}
|
||||
#pragma intrinsic (_InterlockedIncrement)
|
||||
#define InterlockedIncrement _InterlockedIncrement
|
||||
#pragma intrinsic (_InterlockedDecrement)
|
||||
#define InterlockedDecrement _InterlockedDecrement
|
||||
#endif
|
||||
|
||||
/// An integer which is equivalent to an AtomicInt, but which doesn't support attomic operations
|
||||
typedef LONG AtomicIntEquiv;
|
||||
|
||||
/// An integer that can be incremented and decremented atomicly
|
||||
class AtomicInt {
|
||||
public:
|
||||
AtomicInt(AtomicIntEquiv v) : v(v) {}
|
||||
inline operator AtomicIntEquiv() const {
|
||||
return v;
|
||||
}
|
||||
/// Attomicly increments this AtomicInt, returns the new value
|
||||
inline AtomicIntEquiv operator ++ () {
|
||||
return InterlockedIncrement(&v);
|
||||
}
|
||||
/// Attomicly decrements this AtomicInt, returns the new value
|
||||
inline AtomicIntEquiv operator -- () {
|
||||
return InterlockedDecrement(&v);
|
||||
}
|
||||
private:
|
||||
AtomicIntEquiv v; ///< The value
|
||||
};
|
||||
|
||||
/// We have a fast AtomicInt
|
||||
#define HAVE_FAST_ATOMIC
|
||||
|
||||
#ifdef _MSC_VER
|
||||
extern "C" {
|
||||
LONG __cdecl _InterlockedIncrement(LONG volatile *Addend);
|
||||
LONG __cdecl _InterlockedDecrement(LONG volatile *Addend);
|
||||
}
|
||||
#pragma intrinsic (_InterlockedIncrement)
|
||||
#define InterlockedIncrement _InterlockedIncrement
|
||||
#pragma intrinsic (_InterlockedDecrement)
|
||||
#define InterlockedDecrement _InterlockedDecrement
|
||||
#endif
|
||||
|
||||
/// An integer which is equivalent to an AtomicInt, but which doesn't support attomic operations
|
||||
typedef LONG AtomicIntEquiv;
|
||||
|
||||
/// An integer that can be incremented and decremented atomicly
|
||||
class AtomicInt {
|
||||
public:
|
||||
AtomicInt(AtomicIntEquiv v) : v(v) {}
|
||||
inline operator AtomicIntEquiv() const {
|
||||
return v;
|
||||
}
|
||||
/// Attomicly increments this AtomicInt, returns the new value
|
||||
inline AtomicIntEquiv operator ++ () {
|
||||
return InterlockedIncrement(&v);
|
||||
}
|
||||
/// Attomicly decrements this AtomicInt, returns the new value
|
||||
inline AtomicIntEquiv operator -- () {
|
||||
return InterlockedDecrement(&v);
|
||||
}
|
||||
private:
|
||||
AtomicIntEquiv v; ///< The value
|
||||
};
|
||||
|
||||
/// We have a fast AtomicInt
|
||||
#define HAVE_FAST_ATOMIC
|
||||
|
||||
// ----------------------------------------------------------------------------- : AtomicInt : GCC
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
/// An integer which is equivalent to an AtomicInt, but which doesn't support attomic operations
|
||||
typedef unsigned int AtomicIntEquiv;
|
||||
|
||||
/// An integer that can be incremented and decremented atomicly
|
||||
class AtomicInt {
|
||||
public:
|
||||
AtomicInt(AtomicIntEquiv v) : v(v) {}
|
||||
inline operator AtomicIntEquiv() const {
|
||||
return v;
|
||||
}
|
||||
inline AtomicInt operator ++ () {
|
||||
return __sync_add_and_fetch(&v,1);
|
||||
}
|
||||
inline AtomicInt operator -- () {
|
||||
return __sync_sub_and_fetch(&v,1);
|
||||
}
|
||||
private:
|
||||
AtomicIntEquiv v;
|
||||
};
|
||||
|
||||
/// We have a fast AtomicInt
|
||||
#define HAVE_FAST_ATOMIC
|
||||
|
||||
|
||||
/// An integer which is equivalent to an AtomicInt, but which doesn't support attomic operations
|
||||
typedef unsigned int AtomicIntEquiv;
|
||||
|
||||
/// An integer that can be incremented and decremented atomicly
|
||||
class AtomicInt {
|
||||
public:
|
||||
AtomicInt(AtomicIntEquiv v) : v(v) {}
|
||||
inline operator AtomicIntEquiv() const {
|
||||
return v;
|
||||
}
|
||||
inline AtomicInt operator ++ () {
|
||||
return __sync_add_and_fetch(&v,1);
|
||||
}
|
||||
inline AtomicInt operator -- () {
|
||||
return __sync_sub_and_fetch(&v,1);
|
||||
}
|
||||
private:
|
||||
AtomicIntEquiv v;
|
||||
};
|
||||
|
||||
/// We have a fast AtomicInt
|
||||
#define HAVE_FAST_ATOMIC
|
||||
|
||||
// ----------------------------------------------------------------------------- : AtomicInt : portable
|
||||
#else
|
||||
|
||||
/// An integer which is equivalent to an AtomicInt, but which doesn't support attomic operations
|
||||
typedef long AtomicIntEquiv;
|
||||
|
||||
/// An integer that can be incremented and decremented atomicly
|
||||
class AtomicInt {
|
||||
public:
|
||||
AtomicInt(AtomicIntEquiv v) : v(v) {}
|
||||
AtomicInt(const AtomicInt& i) {
|
||||
wxCriticalSectionLocker lock(i.cs);
|
||||
v = i.v;
|
||||
}
|
||||
inline operator AtomicIntEquiv() const {
|
||||
return v;
|
||||
}
|
||||
/// Attomicly increments this AtomicInt, returns the new value
|
||||
inline AtomicIntEquiv operator ++ () {
|
||||
wxCriticalSectionLocker lock(cs);
|
||||
return ++v;
|
||||
}
|
||||
/// Attomicly decrements this AtomicInt, returns the new value
|
||||
inline AtomicIntEquiv operator -- () {
|
||||
wxCriticalSectionLocker lock(cs);
|
||||
return --v;
|
||||
}
|
||||
private:
|
||||
AtomicIntEquiv v; ///< The value
|
||||
mutable wxCriticalSection cs; ///< Critical section protecting v
|
||||
};
|
||||
|
||||
|
||||
/// An integer which is equivalent to an AtomicInt, but which doesn't support attomic operations
|
||||
typedef long AtomicIntEquiv;
|
||||
|
||||
/// An integer that can be incremented and decremented atomicly
|
||||
class AtomicInt {
|
||||
public:
|
||||
AtomicInt(AtomicIntEquiv v) : v(v) {}
|
||||
AtomicInt(const AtomicInt& i) {
|
||||
wxCriticalSectionLocker lock(i.cs);
|
||||
v = i.v;
|
||||
}
|
||||
inline operator AtomicIntEquiv() const {
|
||||
return v;
|
||||
}
|
||||
/// Attomicly increments this AtomicInt, returns the new value
|
||||
inline AtomicIntEquiv operator ++ () {
|
||||
wxCriticalSectionLocker lock(cs);
|
||||
return ++v;
|
||||
}
|
||||
/// Attomicly decrements this AtomicInt, returns the new value
|
||||
inline AtomicIntEquiv operator -- () {
|
||||
wxCriticalSectionLocker lock(cs);
|
||||
return --v;
|
||||
}
|
||||
private:
|
||||
AtomicIntEquiv v; ///< The value
|
||||
mutable wxCriticalSection cs; ///< Critical section protecting v
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+51
-51
@@ -20,70 +20,70 @@
|
||||
template <typename T>
|
||||
class Defaultable {
|
||||
public:
|
||||
inline Defaultable() : is_default(true) {}
|
||||
inline Defaultable(const T& v, bool def = false) : value(v), is_default(def) {}
|
||||
|
||||
/// Assigning a value takes this object out of the default state
|
||||
inline void assign(const T& new_value) {
|
||||
value = new_value;
|
||||
is_default = false;
|
||||
}
|
||||
/// Assigning a value keep this object in the default state
|
||||
inline void assignDefault(const T& new_value) {
|
||||
assert(is_default);
|
||||
value = new_value;
|
||||
}
|
||||
/// Assigning a value, don't change the defaultness
|
||||
inline void assignDontChangeDefault(const T& new_value) {
|
||||
value = new_value;
|
||||
}
|
||||
|
||||
/// Get access to the value
|
||||
inline operator const T& () const { return value; }
|
||||
inline const T& operator () () const { return value; }
|
||||
/// Get access to the value, for changing it
|
||||
inline T& mutate () {
|
||||
is_default = false;
|
||||
return value;
|
||||
}
|
||||
/// Get access to the value, for changing it, don't change the defaultness
|
||||
inline T& mutateDontChangeDefault() { return value; }
|
||||
|
||||
/// Is this value in the default state?
|
||||
inline bool isDefault() const { return is_default; }
|
||||
/// Set the defaultness to d
|
||||
inline void makeDefault(bool d = true) { is_default = d; }
|
||||
|
||||
/// Compare the values, ignore defaultness
|
||||
/** used by scriptable to check for changes */
|
||||
inline bool operator != (const Defaultable& that) const { return value != that.value; }
|
||||
|
||||
inline Defaultable() : is_default(true) {}
|
||||
inline Defaultable(const T& v, bool def = false) : value(v), is_default(def) {}
|
||||
|
||||
/// Assigning a value takes this object out of the default state
|
||||
inline void assign(const T& new_value) {
|
||||
value = new_value;
|
||||
is_default = false;
|
||||
}
|
||||
/// Assigning a value keep this object in the default state
|
||||
inline void assignDefault(const T& new_value) {
|
||||
assert(is_default);
|
||||
value = new_value;
|
||||
}
|
||||
/// Assigning a value, don't change the defaultness
|
||||
inline void assignDontChangeDefault(const T& new_value) {
|
||||
value = new_value;
|
||||
}
|
||||
|
||||
/// Get access to the value
|
||||
inline operator const T& () const { return value; }
|
||||
inline const T& operator () () const { return value; }
|
||||
/// Get access to the value, for changing it
|
||||
inline T& mutate () {
|
||||
is_default = false;
|
||||
return value;
|
||||
}
|
||||
/// Get access to the value, for changing it, don't change the defaultness
|
||||
inline T& mutateDontChangeDefault() { return value; }
|
||||
|
||||
/// Is this value in the default state?
|
||||
inline bool isDefault() const { return is_default; }
|
||||
/// Set the defaultness to d
|
||||
inline void makeDefault(bool d = true) { is_default = d; }
|
||||
|
||||
/// Compare the values, ignore defaultness
|
||||
/** used by scriptable to check for changes */
|
||||
inline bool operator != (const Defaultable& that) const { return value != that.value; }
|
||||
|
||||
private:
|
||||
/// The value
|
||||
T value;
|
||||
/// Is this value in the default state?
|
||||
bool is_default;
|
||||
|
||||
friend class Reader;
|
||||
friend class Writer;
|
||||
/// The value
|
||||
T value;
|
||||
/// Is this value in the default state?
|
||||
bool is_default;
|
||||
|
||||
friend class Reader;
|
||||
friend class Writer;
|
||||
};
|
||||
|
||||
// we need some custom io, because the behaviour is different for each of Reader/Writer/GetMember
|
||||
|
||||
template <typename T>
|
||||
void Reader::handle(Defaultable<T>& def) {
|
||||
def.is_default = false;
|
||||
handle(def.value);
|
||||
def.is_default = false;
|
||||
handle(def.value);
|
||||
}
|
||||
template <typename T>
|
||||
void Writer::handle(const Defaultable<T>& def) {
|
||||
if (!def.isDefault()) {
|
||||
handle(def());
|
||||
}
|
||||
if (!def.isDefault()) {
|
||||
handle(def());
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
void GetDefaultMember::handle(const Defaultable<T>& def) {
|
||||
handle(def());
|
||||
handle(def());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -19,63 +19,63 @@
|
||||
|
||||
template <typename Key, typename Value>
|
||||
IndexMap<Key,Value>& DelayedIndexMaps<Key,Value>::get(const String& name, const vector<Key>& init_with) {
|
||||
intrusive_ptr<DelayedIndexMapsData<Key,Value> >& item = data[name];
|
||||
if (!item) { // no item, make a new one
|
||||
item = intrusive(new DelayedIndexMapsData<Key,Value>);
|
||||
item->read_data.init(init_with);
|
||||
} else if (!item->unread_data.empty()) { // not read, read now
|
||||
item->read_data.init(init_with);
|
||||
Reader reader(shared(new wxStringInputStream(item->unread_data)), nullptr, _("delayed data for ") + name);
|
||||
reader.handle_greedy(item->read_data);
|
||||
item->unread_data.clear();
|
||||
}
|
||||
return item->read_data;
|
||||
intrusive_ptr<DelayedIndexMapsData<Key,Value> >& item = data[name];
|
||||
if (!item) { // no item, make a new one
|
||||
item = intrusive(new DelayedIndexMapsData<Key,Value>);
|
||||
item->read_data.init(init_with);
|
||||
} else if (!item->unread_data.empty()) { // not read, read now
|
||||
item->read_data.init(init_with);
|
||||
Reader reader(shared(new wxStringInputStream(item->unread_data)), nullptr, _("delayed data for ") + name);
|
||||
reader.handle_greedy(item->read_data);
|
||||
item->unread_data.clear();
|
||||
}
|
||||
return item->read_data;
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void DelayedIndexMaps<Key,Value>::clear() {
|
||||
data.clear();
|
||||
data.clear();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Reflection
|
||||
|
||||
// custom reflection : it's a template class
|
||||
template <typename Key, typename Value> void Reader::handle(DelayedIndexMaps<Key,Value>& dim) {
|
||||
handle(dim.data);
|
||||
handle(dim.data);
|
||||
}
|
||||
template <typename Key, typename Value> void Writer::handle(const DelayedIndexMaps<Key,Value>& dim) {
|
||||
handle(dim.data);
|
||||
handle(dim.data);
|
||||
}
|
||||
template <typename Key, typename Value> void GetMember::handle(const DelayedIndexMaps<Key,Value>& dim) {
|
||||
handle(dim.data);
|
||||
handle(dim.data);
|
||||
}
|
||||
|
||||
// custom reflection : read into unread_data
|
||||
template <typename Key, typename Value>
|
||||
void Reader::handle(DelayedIndexMapsData<Key,Value>& d) {
|
||||
handle(d.unread_data);
|
||||
if (d.unread_data.empty()) d.unread_data = _("\n"); // never empty (invariant)
|
||||
handle(d.unread_data);
|
||||
if (d.unread_data.empty()) d.unread_data = _("\n"); // never empty (invariant)
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
void Writer::handle(const DelayedIndexMapsData<Key,Value>& d) {
|
||||
if (!d.unread_data.empty()) {
|
||||
if (d.unread_data == _("\n")) {
|
||||
// this is not interesting, it is only used to make unread_data nonempty (see above)
|
||||
// we don't need to write it
|
||||
} else {
|
||||
handle(d.unread_data); // TODO: how to handle filenames
|
||||
}
|
||||
} else {
|
||||
handle(d.read_data);
|
||||
}
|
||||
if (!d.unread_data.empty()) {
|
||||
if (d.unread_data == _("\n")) {
|
||||
// this is not interesting, it is only used to make unread_data nonempty (see above)
|
||||
// we don't need to write it
|
||||
} else {
|
||||
handle(d.unread_data); // TODO: how to handle filenames
|
||||
}
|
||||
} else {
|
||||
handle(d.read_data);
|
||||
}
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
void GetMember::handle(const DelayedIndexMapsData<Key,Value>& d) {
|
||||
handle(d.read_data);
|
||||
handle(d.read_data);
|
||||
}
|
||||
template <typename Key, typename Value>
|
||||
void GetDefaultMember::handle(const DelayedIndexMapsData<Key,Value>& d) {
|
||||
handle(d.read_data);
|
||||
handle(d.read_data);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+78
-78
@@ -21,100 +21,100 @@
|
||||
// ----------------------------------------------------------------------------- : Dynamic argument
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define THREAD_LOCAL __declspec(thread)
|
||||
# define HAVE_TLS 1
|
||||
# define THREAD_LOCAL __declspec(thread)
|
||||
# define HAVE_TLS 1
|
||||
#elif defined __GNUC__
|
||||
# define THREAD_LOCAL __thread
|
||||
# define HAVE_TLS 1
|
||||
# define THREAD_LOCAL __thread
|
||||
# define HAVE_TLS 1
|
||||
#else
|
||||
# define HAVE_TLS 0
|
||||
# define HAVE_TLS 0
|
||||
#endif
|
||||
|
||||
#if HAVE_TLS
|
||||
|
||||
/// Declare a dynamic argument.
|
||||
/** The value of the argument can be got with: name()
|
||||
* To change the value use WITH_DYNAMIC_ARG(name, newValue)
|
||||
* To be used in a header file. Use IMPLEMENT_DYN_ARG in a source file
|
||||
*/
|
||||
#define DECLARE_DYNAMIC_ARG(Type, name) \
|
||||
extern THREAD_LOCAL Type name##_private; \
|
||||
inline Type name() { return name##_private; } \
|
||||
class name##_changer { \
|
||||
public: \
|
||||
inline name##_changer(Type const& newValue) \
|
||||
: oldValue(name##_private) { \
|
||||
name##_private = newValue; \
|
||||
} \
|
||||
inline ~name##_changer() { \
|
||||
name##_private = oldValue; \
|
||||
} \
|
||||
private: \
|
||||
Type oldValue; \
|
||||
}
|
||||
|
||||
/// Implementation of a dynamic argument
|
||||
#define IMPLEMENT_DYNAMIC_ARG(Type, name, initial) \
|
||||
THREAD_LOCAL Type name##_private = initial;
|
||||
/// Declare a dynamic argument.
|
||||
/** The value of the argument can be got with: name()
|
||||
* To change the value use WITH_DYNAMIC_ARG(name, newValue)
|
||||
* To be used in a header file. Use IMPLEMENT_DYN_ARG in a source file
|
||||
*/
|
||||
#define DECLARE_DYNAMIC_ARG(Type, name) \
|
||||
extern THREAD_LOCAL Type name##_private; \
|
||||
inline Type name() { return name##_private; } \
|
||||
class name##_changer { \
|
||||
public: \
|
||||
inline name##_changer(Type const& newValue) \
|
||||
: oldValue(name##_private) { \
|
||||
name##_private = newValue; \
|
||||
} \
|
||||
inline ~name##_changer() { \
|
||||
name##_private = oldValue; \
|
||||
} \
|
||||
private: \
|
||||
Type oldValue; \
|
||||
}
|
||||
|
||||
/// Implementation of a dynamic argument
|
||||
#define IMPLEMENT_DYNAMIC_ARG(Type, name, initial) \
|
||||
THREAD_LOCAL Type name##_private = initial;
|
||||
|
||||
/// Locally change the value of a dynamic argument
|
||||
/** Usage:
|
||||
* @code
|
||||
* // here name() == old value
|
||||
* {
|
||||
* WITH_DYNAMIC_ARG(name, newValue);
|
||||
* // here name() == newValue
|
||||
* }
|
||||
* // here name() == old value
|
||||
* @endcode
|
||||
*/
|
||||
#define WITH_DYNAMIC_ARG(name, value) \
|
||||
name##_changer name##_dummmy(value)
|
||||
/// Locally change the value of a dynamic argument
|
||||
/** Usage:
|
||||
* @code
|
||||
* // here name() == old value
|
||||
* {
|
||||
* WITH_DYNAMIC_ARG(name, newValue);
|
||||
* // here name() == newValue
|
||||
* }
|
||||
* // here name() == old value
|
||||
* @endcode
|
||||
*/
|
||||
#define WITH_DYNAMIC_ARG(name, value) \
|
||||
name##_changer name##_dummmy(value)
|
||||
|
||||
#else
|
||||
|
||||
template <typename T> struct ThreadLocalObject {
|
||||
map<int,T*> objects;
|
||||
T def;
|
||||
wxCriticalSection container_access;
|
||||
template <typename T> struct ThreadLocalObject {
|
||||
map<int,T*> objects;
|
||||
T def;
|
||||
wxCriticalSection container_access;
|
||||
|
||||
inline T operator () () {
|
||||
wxCriticalSectionLocker lock(container_access);
|
||||
T*& p = objects[wxThread::GetCurrentId()];
|
||||
if (!p) {
|
||||
p = new T(def);
|
||||
}
|
||||
return *objects[wxThread::GetCurrentId()];
|
||||
}
|
||||
inline T operator () () {
|
||||
wxCriticalSectionLocker lock(container_access);
|
||||
T*& p = objects[wxThread::GetCurrentId()];
|
||||
if (!p) {
|
||||
p = new T(def);
|
||||
}
|
||||
return *objects[wxThread::GetCurrentId()];
|
||||
}
|
||||
|
||||
inline void store (T value) {
|
||||
wxCriticalSectionLocker lock(container_access);
|
||||
T*& p = objects[wxThread::GetCurrentId()];
|
||||
if (!p) {
|
||||
p = new T(def);
|
||||
}
|
||||
*objects[wxThread::GetCurrentId()] = value;
|
||||
}
|
||||
inline void store (T value) {
|
||||
wxCriticalSectionLocker lock(container_access);
|
||||
T*& p = objects[wxThread::GetCurrentId()];
|
||||
if (!p) {
|
||||
p = new T(def);
|
||||
}
|
||||
*objects[wxThread::GetCurrentId()] = value;
|
||||
}
|
||||
|
||||
ThreadLocalObject (T def)
|
||||
: def(def)
|
||||
{}
|
||||
ThreadLocalObject (T def)
|
||||
: def(def)
|
||||
{}
|
||||
|
||||
~ThreadLocalObject () {
|
||||
for (typename map<int,T*>::iterator i = objects.begin(); i != objects.end(); ++i) {
|
||||
delete (*i).second;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define DECLARE_DYNAMIC_ARG(Type, name) \
|
||||
extern ThreadLocalObject<Type> name;
|
||||
~ThreadLocalObject () {
|
||||
for (typename map<int,T*>::iterator i = objects.begin(); i != objects.end(); ++i) {
|
||||
delete (*i).second;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define DECLARE_DYNAMIC_ARG(Type, name) \
|
||||
extern ThreadLocalObject<Type> name;
|
||||
|
||||
#define IMPLEMENT_DYNAMIC_ARG(Type, name, initial) \
|
||||
ThreadLocalObject<Type> name (initial);
|
||||
#define IMPLEMENT_DYNAMIC_ARG(Type, name, initial) \
|
||||
ThreadLocalObject<Type> name (initial);
|
||||
|
||||
#define WITH_DYNAMIC_ARG(name, value) \
|
||||
name.store(value);
|
||||
#define WITH_DYNAMIC_ARG(name, value) \
|
||||
name.store(value);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+69
-69
@@ -11,7 +11,7 @@
|
||||
#include <cli/text_io_handler.hpp>
|
||||
#include <cli/text_io_handler.hpp>
|
||||
#if wxUSE_STACKWALKER
|
||||
#include <wx/stackwalk.h>
|
||||
#include <wx/stackwalk.h>
|
||||
#endif
|
||||
#include <queue>
|
||||
|
||||
@@ -20,32 +20,32 @@ DECLARE_TYPEOF_COLLECTION(ScriptParseError);
|
||||
// ----------------------------------------------------------------------------- : Debug utilities
|
||||
|
||||
#if defined(_MSC_VER) && defined(_DEBUG) && defined(_CRT_WIDE)
|
||||
void msvc_assert(const wchar_t* msg, const wchar_t* expr, const wchar_t* file, unsigned line) {
|
||||
if (IsDebuggerPresent()) {
|
||||
wchar_t buffer[1024];
|
||||
if (msg) {
|
||||
wsprintf(buffer, L"Assertion failed: %s: %s, file %s, line %d\n", msg, expr, file, line);
|
||||
} else {
|
||||
wsprintf(buffer, L"Assertion failed: %s, file %s, line %d\n", expr, file, line);
|
||||
}
|
||||
OutputDebugStringW(buffer);
|
||||
DebugBreak();
|
||||
} else {
|
||||
_wassert(expr, file, line);
|
||||
}
|
||||
}
|
||||
void msvc_assert(const wchar_t* msg, const wchar_t* expr, const wchar_t* file, unsigned line) {
|
||||
if (IsDebuggerPresent()) {
|
||||
wchar_t buffer[1024];
|
||||
if (msg) {
|
||||
wsprintf(buffer, L"Assertion failed: %s: %s, file %s, line %d\n", msg, expr, file, line);
|
||||
} else {
|
||||
wsprintf(buffer, L"Assertion failed: %s, file %s, line %d\n", expr, file, line);
|
||||
}
|
||||
OutputDebugStringW(buffer);
|
||||
DebugBreak();
|
||||
} else {
|
||||
_wassert(expr, file, line);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : Error types
|
||||
|
||||
Error::Error(const String& message)
|
||||
: message(message)
|
||||
: message(message)
|
||||
{}
|
||||
|
||||
Error::~Error() {}
|
||||
|
||||
String Error::what() const {
|
||||
return message;
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,55 +106,55 @@ String get_stack_trace() {
|
||||
}
|
||||
#else
|
||||
String get_stack_trace() {
|
||||
return _(""); // not supported
|
||||
return _(""); // not supported
|
||||
}
|
||||
#endif // wxUSE_STACKWALKER
|
||||
|
||||
InternalError::InternalError(const String& str)
|
||||
: Error(
|
||||
_("An internal error occured:\n\n") +
|
||||
str + _("\n")
|
||||
_("Please save your work (use 'save as' to so you don't overwrite things)\n")
|
||||
_("and restart Magic Set Editor.\n\n")
|
||||
_("You should leave a bug report on http://magicseteditor.sourceforge.net/\n")
|
||||
_("Press Ctrl+C to copy this message to the clipboard.")
|
||||
)
|
||||
: Error(
|
||||
_("An internal error occured:\n\n") +
|
||||
str + _("\n")
|
||||
_("Please save your work (use 'save as' to so you don't overwrite things)\n")
|
||||
_("and restart Magic Set Editor.\n\n")
|
||||
_("You should leave a bug report on http://magicseteditor.sourceforge.net/\n")
|
||||
_("Press Ctrl+C to copy this message to the clipboard.")
|
||||
)
|
||||
{
|
||||
// add a stacktrace
|
||||
const String stack_trace = get_stack_trace();
|
||||
if (!stack_trace.empty()) {
|
||||
message << _("\n\nCall stack:\n") << stack_trace;
|
||||
}
|
||||
// add a stacktrace
|
||||
const String stack_trace = get_stack_trace();
|
||||
if (!stack_trace.empty()) {
|
||||
message << _("\n\nCall stack:\n") << stack_trace;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Parse errors
|
||||
|
||||
ScriptParseError::ScriptParseError(size_t pos, int line, const String& filename, const String& error)
|
||||
: ParseError(error)
|
||||
, start(pos), end(pos), line(line), filename(filename)
|
||||
: ParseError(error)
|
||||
, start(pos), end(pos), line(line), filename(filename)
|
||||
{}
|
||||
ScriptParseError::ScriptParseError(size_t pos, int line, const String& filename, const String& exp, const String& found)
|
||||
: ParseError(_("Expected '") + exp + _("' instead of '") + found + _("'"))
|
||||
, start(pos), end(pos + found.size()), line(line), filename(filename)
|
||||
: ParseError(_("Expected '") + exp + _("' instead of '") + found + _("'"))
|
||||
, start(pos), end(pos + found.size()), line(line), filename(filename)
|
||||
{}
|
||||
ScriptParseError::ScriptParseError(size_t pos1, size_t pos2, int line, const String& filename, const String& open, const String& close, const String& found)
|
||||
: ParseError(_("Expected closing '") + close + _("' for this '") + open + _("' instead of '") + found + _("'"))
|
||||
, start(pos1), end(pos2 + found.size()), line(line), filename(filename)
|
||||
: ParseError(_("Expected closing '") + close + _("' for this '") + open + _("' instead of '") + found + _("'"))
|
||||
, start(pos1), end(pos2 + found.size()), line(line), filename(filename)
|
||||
{}
|
||||
String ScriptParseError::what() const {
|
||||
return String(_("(")) << (int)start << _("): ") << Error::what();
|
||||
return String(_("(")) << (int)start << _("): ") << Error::what();
|
||||
}
|
||||
|
||||
String concat(const vector<ScriptParseError>& errors) {
|
||||
String total;
|
||||
FOR_EACH_CONST(e, errors) {
|
||||
if (!total.empty()) total += _("\n");
|
||||
total += e.what();
|
||||
}
|
||||
return total;
|
||||
String total;
|
||||
FOR_EACH_CONST(e, errors) {
|
||||
if (!total.empty()) total += _("\n");
|
||||
total += e.what();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
ScriptParseErrors::ScriptParseErrors(const vector<ScriptParseError>& errors)
|
||||
: ParseError(concat(errors))
|
||||
: ParseError(concat(errors))
|
||||
{}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Error handling
|
||||
@@ -167,37 +167,37 @@ bool show_message_box_for_fatal_errors = true;
|
||||
bool write_errors_to_cli = false;
|
||||
|
||||
void queue_message(MessageType type, String const& msg) {
|
||||
if (write_errors_to_cli && wxThread::IsMain()) {
|
||||
cli.show_message(type,msg);
|
||||
return; // TODO: is this the right thing to do?
|
||||
}
|
||||
if (show_message_box_for_fatal_errors && type == MESSAGE_FATAL_ERROR && wxThread::IsMain()) {
|
||||
// bring this to the user's attention right now!
|
||||
wxMessageBox(msg, _("Error"), wxOK | wxICON_ERROR);
|
||||
}
|
||||
// Thread safety
|
||||
wxMutexLocker lock(crit_error_handling);
|
||||
// Only show errors in the main thread
|
||||
message_queue.push_back(make_pair(type,msg));
|
||||
if (write_errors_to_cli && wxThread::IsMain()) {
|
||||
cli.show_message(type,msg);
|
||||
return; // TODO: is this the right thing to do?
|
||||
}
|
||||
if (show_message_box_for_fatal_errors && type == MESSAGE_FATAL_ERROR && wxThread::IsMain()) {
|
||||
// bring this to the user's attention right now!
|
||||
wxMessageBox(msg, _("Error"), wxOK | wxICON_ERROR);
|
||||
}
|
||||
// Thread safety
|
||||
wxMutexLocker lock(crit_error_handling);
|
||||
// Only show errors in the main thread
|
||||
message_queue.push_back(make_pair(type,msg));
|
||||
}
|
||||
|
||||
void handle_error(const Error& e) {
|
||||
queue_message(e.is_fatal() ? MESSAGE_FATAL_ERROR : MESSAGE_ERROR, e.what());
|
||||
queue_message(e.is_fatal() ? MESSAGE_FATAL_ERROR : MESSAGE_ERROR, e.what());
|
||||
}
|
||||
|
||||
bool have_queued_message() {
|
||||
wxMutexLocker lock(crit_error_handling);
|
||||
return !message_queue.empty();
|
||||
wxMutexLocker lock(crit_error_handling);
|
||||
return !message_queue.empty();
|
||||
}
|
||||
|
||||
bool get_queued_message(MessageType& type, String& msg) {
|
||||
wxMutexLocker lock(crit_error_handling);
|
||||
if (message_queue.empty()) {
|
||||
return false;
|
||||
} else {
|
||||
type = message_queue.back().first;
|
||||
msg = message_queue.back().second;
|
||||
message_queue.pop_back();
|
||||
return true;
|
||||
}
|
||||
wxMutexLocker lock(crit_error_handling);
|
||||
if (message_queue.empty()) {
|
||||
return false;
|
||||
} else {
|
||||
type = message_queue.back().first;
|
||||
msg = message_queue.back().second;
|
||||
message_queue.pop_back();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+57
-57
@@ -21,25 +21,25 @@
|
||||
/// Our own exception class
|
||||
class Error {
|
||||
public:
|
||||
Error(const String& message);
|
||||
virtual ~Error();
|
||||
|
||||
/// Return the error message
|
||||
virtual String what() const;
|
||||
/// Is the message (potentially) fatal?
|
||||
virtual bool is_fatal() const { return false; }
|
||||
|
||||
Error(const String& message);
|
||||
virtual ~Error();
|
||||
|
||||
/// Return the error message
|
||||
virtual String what() const;
|
||||
/// Is the message (potentially) fatal?
|
||||
virtual bool is_fatal() const { return false; }
|
||||
|
||||
protected:
|
||||
String message; ///< The error message
|
||||
String message; ///< The error message
|
||||
};
|
||||
|
||||
|
||||
/// Internal errors
|
||||
class InternalError : public Error {
|
||||
public:
|
||||
InternalError(const String& str);
|
||||
// not all internal errors are fatal, but we had still better warn the user about them.
|
||||
virtual bool is_fatal() const { return true; }
|
||||
InternalError(const String& str);
|
||||
// not all internal errors are fatal, but we had still better warn the user about them.
|
||||
virtual bool is_fatal() const { return true; }
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : File errors
|
||||
@@ -47,21 +47,21 @@ class InternalError : public Error {
|
||||
/// Errors related to packages
|
||||
class PackageError : public Error {
|
||||
public:
|
||||
inline PackageError(const String& str) : Error(str) {}
|
||||
inline PackageError(const String& str) : Error(str) {}
|
||||
};
|
||||
|
||||
/// A package is not found
|
||||
class PackageNotFoundError : public PackageError {
|
||||
public:
|
||||
inline PackageNotFoundError(const String& str) : PackageError(str) {}
|
||||
inline PackageNotFoundError(const String& str) : PackageError(str) {}
|
||||
};
|
||||
|
||||
/// A file is not found
|
||||
class FileNotFoundError : public PackageError {
|
||||
public:
|
||||
inline FileNotFoundError(const String& file, const String& package)
|
||||
: PackageError(_ERROR_2_("file not found", file, package))
|
||||
{}
|
||||
inline FileNotFoundError(const String& file, const String& package)
|
||||
: PackageError(_ERROR_2_("file not found", file, package))
|
||||
{}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Parse errors
|
||||
@@ -69,37 +69,37 @@ class FileNotFoundError : public PackageError {
|
||||
/// Parse errors
|
||||
class ParseError : public Error {
|
||||
public:
|
||||
inline ParseError(const String& str) : Error(str) {}
|
||||
inline ParseError(const String& str) : Error(str) {}
|
||||
};
|
||||
|
||||
/// Parse error in a particular file
|
||||
class FileParseError : public ParseError {
|
||||
public:
|
||||
inline FileParseError(const String& err, const String& file) :
|
||||
ParseError(_ERROR_2_("file parse error", file, err))
|
||||
{}
|
||||
inline FileParseError(const String& err, const String& file) :
|
||||
ParseError(_ERROR_2_("file parse error", file, err))
|
||||
{}
|
||||
};
|
||||
|
||||
/// Parse error in a script
|
||||
class ScriptParseError : public ParseError {
|
||||
public:
|
||||
ScriptParseError(size_t pos, int line, const String& filename, const String& str);
|
||||
ScriptParseError(size_t pos, int line, const String& filename, const String& expected, const String& found);
|
||||
ScriptParseError(size_t pos1, size_t pos2, int line, const String& filename, const String& open, const String& close, const String& found);
|
||||
/// Position of the error
|
||||
size_t start, end;
|
||||
/// Line number of the error (the first line is 1)
|
||||
int line;
|
||||
/// Filename the error was in, or an empty string
|
||||
String filename;
|
||||
/// Return the error message
|
||||
virtual String what() const;
|
||||
ScriptParseError(size_t pos, int line, const String& filename, const String& str);
|
||||
ScriptParseError(size_t pos, int line, const String& filename, const String& expected, const String& found);
|
||||
ScriptParseError(size_t pos1, size_t pos2, int line, const String& filename, const String& open, const String& close, const String& found);
|
||||
/// Position of the error
|
||||
size_t start, end;
|
||||
/// Line number of the error (the first line is 1)
|
||||
int line;
|
||||
/// Filename the error was in, or an empty string
|
||||
String filename;
|
||||
/// Return the error message
|
||||
virtual String what() const;
|
||||
};
|
||||
|
||||
/// Multiple parse errors in a script
|
||||
class ScriptParseErrors : public ParseError {
|
||||
public:
|
||||
ScriptParseErrors(const vector<ScriptParseError>& errors);
|
||||
ScriptParseErrors(const vector<ScriptParseError>& errors);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Script errors
|
||||
@@ -107,29 +107,29 @@ class ScriptParseErrors : public ParseError {
|
||||
/// A runtime error in a script
|
||||
class ScriptError : public Error {
|
||||
public:
|
||||
inline ScriptError(const String& str) : Error(str) {}
|
||||
inline ScriptError(const String& str) : Error(str) {}
|
||||
};
|
||||
|
||||
/// "Variable not set"
|
||||
class ScriptErrorNoVariable : public ScriptError {
|
||||
public:
|
||||
inline ScriptErrorNoVariable(const String& var) : ScriptError(_("Variable not set: ") + var) {}
|
||||
inline ScriptErrorNoVariable(const String& var) : ScriptError(_("Variable not set: ") + var) {}
|
||||
};
|
||||
|
||||
/// "Can't convert from A to B"
|
||||
class ScriptErrorConversion : public ScriptError {
|
||||
public:
|
||||
inline ScriptErrorConversion(const String& a, const String& b)
|
||||
: ScriptError(_ERROR_2_("can't convert", a, b)) {}
|
||||
inline ScriptErrorConversion(const String& value, const String& a, const String& b)
|
||||
: ScriptError(_ERROR_3_("can't convert value", value, a, b)) {}
|
||||
inline ScriptErrorConversion(const String& a, const String& b)
|
||||
: ScriptError(_ERROR_2_("can't convert", a, b)) {}
|
||||
inline ScriptErrorConversion(const String& value, const String& a, const String& b)
|
||||
: ScriptError(_ERROR_3_("can't convert value", value, a, b)) {}
|
||||
};
|
||||
|
||||
/// "A has no member B"
|
||||
class ScriptErrorNoMember : public ScriptError {
|
||||
public:
|
||||
inline ScriptErrorNoMember(const String& type, const String& member)
|
||||
: ScriptError(_ERROR_2_("has no member", type, member)) {}
|
||||
inline ScriptErrorNoMember(const String& type, const String& member)
|
||||
: ScriptError(_ERROR_2_("has no member", type, member)) {}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Error/message handling
|
||||
@@ -140,14 +140,14 @@ extern bool write_errors_to_cli;
|
||||
|
||||
/// Types/levels of (error)messages
|
||||
enum MessageType
|
||||
{ MESSAGE_INPUT
|
||||
, MESSAGE_OUTPUT
|
||||
, MESSAGE_INFO
|
||||
, MESSAGE_WARNING
|
||||
, MESSAGE_ERROR
|
||||
, MESSAGE_FATAL_ERROR
|
||||
{ MESSAGE_INPUT
|
||||
, MESSAGE_OUTPUT
|
||||
, MESSAGE_INFO
|
||||
, MESSAGE_WARNING
|
||||
, MESSAGE_ERROR
|
||||
, MESSAGE_FATAL_ERROR
|
||||
|
||||
, MESSAGE_TYPE_MAX
|
||||
, MESSAGE_TYPE_MAX
|
||||
};
|
||||
|
||||
/// Queue an (error) message, it can later be retrieved with get_queued_message
|
||||
@@ -169,15 +169,15 @@ String get_stack_trace();
|
||||
|
||||
/// Catch all types of errors, and pass then to handle_error
|
||||
#define CATCH_ALL_ERRORS(handle_now) \
|
||||
catch (const Error& e) { \
|
||||
handle_error(e); \
|
||||
} catch (const std::exception& e) { \
|
||||
/* we don't throw std::exception ourselfs, so this is probably something serious */ \
|
||||
String message(e.what(), IF_UNICODE(wxConvLocal, wxSTRING_MAXLEN) ); \
|
||||
handle_error(InternalError(message)); \
|
||||
} catch (...) { \
|
||||
handle_error(InternalError(_("An unexpected exception occurred!"))); \
|
||||
}
|
||||
catch (const Error& e) { \
|
||||
handle_error(e); \
|
||||
} catch (const std::exception& e) { \
|
||||
/* we don't throw std::exception ourselfs, so this is probably something serious */ \
|
||||
String message(e.what(), IF_UNICODE(wxConvLocal, wxSTRING_MAXLEN) ); \
|
||||
handle_error(InternalError(message)); \
|
||||
} catch (...) { \
|
||||
handle_error(InternalError(_("An unexpected exception occurred!"))); \
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
|
||||
+146
-146
@@ -18,200 +18,200 @@ DECLARE_TYPEOF_COLLECTION(String);
|
||||
// ----------------------------------------------------------------------------- : File names
|
||||
|
||||
String normalize_filename(const String& name) {
|
||||
wxFileName fn(name);
|
||||
fn.Normalize();
|
||||
return fn.GetFullPath();
|
||||
wxFileName fn(name);
|
||||
fn.Normalize();
|
||||
return fn.GetFullPath();
|
||||
}
|
||||
|
||||
String normalize_internal_filename(const String& name) {
|
||||
String ret;
|
||||
FOR_EACH_CONST(c, name) {
|
||||
if (c==_('\\')) ret += _('/');
|
||||
else ret += toLower(c);
|
||||
}
|
||||
return ret;
|
||||
String ret;
|
||||
FOR_EACH_CONST(c, name) {
|
||||
if (c==_('\\')) ret += _('/');
|
||||
else ret += toLower(c);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ignore_file(const String& name) {
|
||||
// Files that are never part of a package,
|
||||
// i.e. random stuff the OS file manager dumps without being asked
|
||||
return name == _("Thumbs.db"); // winXP explorer thumbnails
|
||||
// Files that are never part of a package,
|
||||
// i.e. random stuff the OS file manager dumps without being asked
|
||||
return name == _("Thumbs.db"); // winXP explorer thumbnails
|
||||
}
|
||||
|
||||
bool is_filename_char(Char c) {
|
||||
return isAlnum(c) || c == _(' ') || c == _('_') || c == _('-') || c == _('.');
|
||||
return isAlnum(c) || c == _(' ') || c == _('_') || c == _('-') || c == _('.');
|
||||
}
|
||||
|
||||
String clean_filename(const String& name) {
|
||||
String clean;
|
||||
// allow only valid characters, and remove leading whitespace
|
||||
bool start = true;
|
||||
FOR_EACH_CONST(c, name) {
|
||||
if (is_filename_char(c) && !(start && c == _(' '))) {
|
||||
start = false;
|
||||
clean += c;
|
||||
}
|
||||
}
|
||||
// remove trailing whitespace
|
||||
while (!clean.empty() && clean[clean.size()-1] == _(' ')) {
|
||||
clean.resize(clean.size()-1);
|
||||
}
|
||||
if (clean.empty() || starts_with(clean, _("."))) {
|
||||
clean = _("no-name") + clean;
|
||||
}
|
||||
return clean;
|
||||
String clean;
|
||||
// allow only valid characters, and remove leading whitespace
|
||||
bool start = true;
|
||||
FOR_EACH_CONST(c, name) {
|
||||
if (is_filename_char(c) && !(start && c == _(' '))) {
|
||||
start = false;
|
||||
clean += c;
|
||||
}
|
||||
}
|
||||
// remove trailing whitespace
|
||||
while (!clean.empty() && clean[clean.size()-1] == _(' ')) {
|
||||
clean.resize(clean.size()-1);
|
||||
}
|
||||
if (clean.empty() || starts_with(clean, _("."))) {
|
||||
clean = _("no-name") + clean;
|
||||
}
|
||||
return clean;
|
||||
}
|
||||
|
||||
bool resolve_filename_conflicts(wxFileName& fn, FilenameConflicts conflicts, set<String>& used) {
|
||||
switch (conflicts) {
|
||||
case CONFLICT_KEEP_OLD:
|
||||
return !fn.FileExists();
|
||||
case CONFLICT_OVERWRITE:
|
||||
return true;
|
||||
case CONFLICT_NUMBER: {
|
||||
int i = 0;
|
||||
String ext = fn.GetExt();
|
||||
while(fn.FileExists()) {
|
||||
fn.SetExt(String() << ++i << _(".") << ext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case CONFLICT_NUMBER_OVERWRITE: {
|
||||
int i = 0;
|
||||
String ext = fn.GetExt();
|
||||
while(used.find(fn.GetFullPath()) != used.end()) {
|
||||
fn.SetExt(String() << ++i << _(".") << ext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
throw InternalError(_("resolve_filename_conflicts: default case"));
|
||||
}
|
||||
}
|
||||
switch (conflicts) {
|
||||
case CONFLICT_KEEP_OLD:
|
||||
return !fn.FileExists();
|
||||
case CONFLICT_OVERWRITE:
|
||||
return true;
|
||||
case CONFLICT_NUMBER: {
|
||||
int i = 0;
|
||||
String ext = fn.GetExt();
|
||||
while(fn.FileExists()) {
|
||||
fn.SetExt(String() << ++i << _(".") << ext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case CONFLICT_NUMBER_OVERWRITE: {
|
||||
int i = 0;
|
||||
String ext = fn.GetExt();
|
||||
while(used.find(fn.GetFullPath()) != used.end()) {
|
||||
fn.SetExt(String() << ++i << _(".") << ext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
throw InternalError(_("resolve_filename_conflicts: default case"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : File info
|
||||
|
||||
time_t file_modified_time(const String& path) {
|
||||
// Note: wxFileName also provides a function for this, but that is very slow.
|
||||
struct stat statbuf;
|
||||
if (stat(path.mb_str(), &statbuf) != 0) {
|
||||
if (errno == ENOENT) {
|
||||
return 0;
|
||||
} else {
|
||||
throw InternalError(_("could not stat ") + path);
|
||||
}
|
||||
}
|
||||
return statbuf.st_mtime;
|
||||
// Note: wxFileName also provides a function for this, but that is very slow.
|
||||
struct stat statbuf;
|
||||
if (stat(path.mb_str(), &statbuf) != 0) {
|
||||
if (errno == ENOENT) {
|
||||
return 0;
|
||||
} else {
|
||||
throw InternalError(_("could not stat ") + path);
|
||||
}
|
||||
}
|
||||
return statbuf.st_mtime;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Directories
|
||||
|
||||
bool create_parent_dirs(const String& file) {
|
||||
for (size_t pos = file.find_first_of(_("\\/"), 1) ;
|
||||
pos != String::npos ;
|
||||
pos = file.find_first_of(_("\\/"),pos+1)) {
|
||||
String part = file.substr(0,pos);
|
||||
if (!wxDirExists(part)) {
|
||||
if (!wxMkdir(part)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
for (size_t pos = file.find_first_of(_("\\/"), 1) ;
|
||||
pos != String::npos ;
|
||||
pos = file.find_first_of(_("\\/"),pos+1)) {
|
||||
String part = file.substr(0,pos);
|
||||
if (!wxDirExists(part)) {
|
||||
if (!wxMkdir(part)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Removing
|
||||
|
||||
class RecursiveDeleter : public wxDirTraverser {
|
||||
public:
|
||||
RecursiveDeleter(const String& start) {
|
||||
to_delete.push_back(start);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
|
||||
void remove() {
|
||||
FOR_EACH_REVERSE(dir, to_delete) {
|
||||
if (!wxRmdir(dir)) {
|
||||
ok = false;
|
||||
handle_error(_("Cannot delete ") + dir + _("\n")
|
||||
_("The remainder of the package has still been removed, if possible.\n")
|
||||
_("Other packages may have been removed, including packages that this on is dependent on. Please remove manually."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxDirTraverseResult OnFile(const String& filename) {
|
||||
if (!wxRemoveFile(filename)) {
|
||||
ok = false;
|
||||
handle_error(_("Cannot delete ") + filename + _("\n")
|
||||
_("The remainder of the package has still been removed, if possible.\n")
|
||||
_("Other packages may have been removed, including packages that this on is dependent on. Please remove manually."));
|
||||
}
|
||||
return wxDIR_CONTINUE;
|
||||
}
|
||||
wxDirTraverseResult OnDir(const String& dirname) {
|
||||
to_delete.push_back(dirname);
|
||||
return wxDIR_CONTINUE;
|
||||
}
|
||||
RecursiveDeleter(const String& start) {
|
||||
to_delete.push_back(start);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
|
||||
void remove() {
|
||||
FOR_EACH_REVERSE(dir, to_delete) {
|
||||
if (!wxRmdir(dir)) {
|
||||
ok = false;
|
||||
handle_error(_("Cannot delete ") + dir + _("\n")
|
||||
_("The remainder of the package has still been removed, if possible.\n")
|
||||
_("Other packages may have been removed, including packages that this on is dependent on. Please remove manually."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxDirTraverseResult OnFile(const String& filename) {
|
||||
if (!wxRemoveFile(filename)) {
|
||||
ok = false;
|
||||
handle_error(_("Cannot delete ") + filename + _("\n")
|
||||
_("The remainder of the package has still been removed, if possible.\n")
|
||||
_("Other packages may have been removed, including packages that this on is dependent on. Please remove manually."));
|
||||
}
|
||||
return wxDIR_CONTINUE;
|
||||
}
|
||||
wxDirTraverseResult OnDir(const String& dirname) {
|
||||
to_delete.push_back(dirname);
|
||||
return wxDIR_CONTINUE;
|
||||
}
|
||||
private:
|
||||
vector<String> to_delete;
|
||||
vector<String> to_delete;
|
||||
};
|
||||
|
||||
bool remove_file_or_dir(const String& name) {
|
||||
if (wxFileExists(name)) {
|
||||
return wxRemoveFile(name);
|
||||
} else if (wxDirExists(name)) {
|
||||
RecursiveDeleter rd(name);
|
||||
{
|
||||
wxDir dir(name);
|
||||
dir.Traverse(rd);
|
||||
}
|
||||
rd.remove();
|
||||
return rd.ok;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
if (wxFileExists(name)) {
|
||||
return wxRemoveFile(name);
|
||||
} else if (wxDirExists(name)) {
|
||||
RecursiveDeleter rd(name);
|
||||
{
|
||||
wxDir dir(name);
|
||||
dir.Traverse(rd);
|
||||
}
|
||||
rd.remove();
|
||||
return rd.ok;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Renaming
|
||||
|
||||
bool rename_file_or_dir(const String& from, const String& to) {
|
||||
create_parent_dirs(to);
|
||||
return wxRenameFile(from, to);
|
||||
create_parent_dirs(to);
|
||||
return wxRenameFile(from, to);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Moving
|
||||
|
||||
class IgnoredMover : public wxDirTraverser {
|
||||
public:
|
||||
IgnoredMover(const String& from, const String& to)
|
||||
: from(from), to(to)
|
||||
{}
|
||||
wxDirTraverseResult OnFile(const String& filename) {
|
||||
tryMove(filename);
|
||||
return wxDIR_CONTINUE;
|
||||
}
|
||||
wxDirTraverseResult OnDir(const String& dirname) {
|
||||
return tryMove(dirname) ? wxDIR_IGNORE : wxDIR_CONTINUE;
|
||||
}
|
||||
IgnoredMover(const String& from, const String& to)
|
||||
: from(from), to(to)
|
||||
{}
|
||||
wxDirTraverseResult OnFile(const String& filename) {
|
||||
tryMove(filename);
|
||||
return wxDIR_CONTINUE;
|
||||
}
|
||||
wxDirTraverseResult OnDir(const String& dirname) {
|
||||
return tryMove(dirname) ? wxDIR_IGNORE : wxDIR_CONTINUE;
|
||||
}
|
||||
private:
|
||||
String from, to;
|
||||
bool tryMove(const String& from_path) {
|
||||
if (is_substr(from_path,0,from)) {
|
||||
String to_path = to + from_path.substr(from.size());
|
||||
return rename_file_or_dir(from_path, to_path);
|
||||
} else {
|
||||
// This shouldn't happen
|
||||
return false;
|
||||
}
|
||||
}
|
||||
String from, to;
|
||||
bool tryMove(const String& from_path) {
|
||||
if (is_substr(from_path,0,from)) {
|
||||
String to_path = to + from_path.substr(from.size());
|
||||
return rename_file_or_dir(from_path, to_path);
|
||||
} else {
|
||||
// This shouldn't happen
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void move_ignored_files(const String& from_dir, const String& to_dir) {
|
||||
if (wxDirExists(from_dir) && wxDirExists(to_dir)) {
|
||||
wxDir dir(from_dir);
|
||||
IgnoredMover im(from_dir, to_dir);
|
||||
dir.Traverse(im);
|
||||
}
|
||||
if (wxDirExists(from_dir) && wxDirExists(to_dir)) {
|
||||
wxDir dir(from_dir);
|
||||
IgnoredMover im(from_dir, to_dir);
|
||||
dir.Traverse(im);
|
||||
}
|
||||
}
|
||||
|
||||
+21
-21
@@ -20,28 +20,28 @@ DECLARE_POINTER_TYPE(TextValue);
|
||||
/// Information for search/replace
|
||||
class FindInfo {
|
||||
public:
|
||||
FindInfo(wxFindReplaceData& what) : what(what) {}
|
||||
virtual ~FindInfo() {}
|
||||
|
||||
/// Handle that a match was found.
|
||||
/** Returns true if we are done and searching should be ended. */
|
||||
virtual bool handle(const CardP& card, const TextValueP& value, size_t pos, bool was_selection) = 0;
|
||||
/// Should the found text be selected?
|
||||
virtual bool select() const { return true; }
|
||||
/// Should the current selection also be searched?
|
||||
virtual bool searchSelection() const { return false; }
|
||||
|
||||
/// Searching forward?
|
||||
inline bool forward() const { return what.GetFlags() & wxFR_DOWN; }
|
||||
/// Match whole words?
|
||||
inline bool wholeWord() const { return what.GetFlags() & wxFR_WHOLEWORD; }
|
||||
/// Case sensitive?
|
||||
inline bool caseSensitive() const { return what.GetFlags() & wxFR_MATCHCASE; }
|
||||
/// String to look for
|
||||
inline const String& findString() const { return what.GetFindString(); }
|
||||
|
||||
FindInfo(wxFindReplaceData& what) : what(what) {}
|
||||
virtual ~FindInfo() {}
|
||||
|
||||
/// Handle that a match was found.
|
||||
/** Returns true if we are done and searching should be ended. */
|
||||
virtual bool handle(const CardP& card, const TextValueP& value, size_t pos, bool was_selection) = 0;
|
||||
/// Should the found text be selected?
|
||||
virtual bool select() const { return true; }
|
||||
/// Should the current selection also be searched?
|
||||
virtual bool searchSelection() const { return false; }
|
||||
|
||||
/// Searching forward?
|
||||
inline bool forward() const { return what.GetFlags() & wxFR_DOWN; }
|
||||
/// Match whole words?
|
||||
inline bool wholeWord() const { return what.GetFlags() & wxFR_WHOLEWORD; }
|
||||
/// Case sensitive?
|
||||
inline bool caseSensitive() const { return what.GetFlags() & wxFR_MATCHCASE; }
|
||||
/// String to look for
|
||||
inline const String& findString() const { return what.GetFindString(); }
|
||||
|
||||
protected:
|
||||
wxFindReplaceData& what; ///< What to search for, the direction to search in
|
||||
wxFindReplaceData& what; ///< What to search for, the direction to search in
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+67
-67
@@ -19,75 +19,75 @@
|
||||
// ----------------------------------------------------------------------------- : Typeof magic
|
||||
|
||||
#ifdef __GNUC__
|
||||
// GCC has a buildin typeof function, so it doesn't need (as much) hacks
|
||||
#define DECLARE_TYPEOF(T)
|
||||
#define DECLARE_TYPEOF_NO_REV(T)
|
||||
#define DECLARE_TYPEOF_CONST(T)
|
||||
#define DECLARE_TYPEOF_COLLECTION(T)
|
||||
|
||||
#define TYPEOF(Value) __typeof(Value)
|
||||
#define TYPEOF_IT(Value) __typeof((Value).begin())
|
||||
#define TYPEOF_CIT(Value) __typeof((Value).begin())
|
||||
#define TYPEOF_RIT(Value) __typeof((Value).rbegin())
|
||||
#define TYPEOF_CRIT(Value) __typeof((Value).rbegin())
|
||||
#define TYPEOF_REF(Value) __typeof(*(Value).begin())&
|
||||
#define TYPEOF_CREF(Value) __typeof(*(Value).begin())&
|
||||
|
||||
// GCC has a buildin typeof function, so it doesn't need (as much) hacks
|
||||
#define DECLARE_TYPEOF(T)
|
||||
#define DECLARE_TYPEOF_NO_REV(T)
|
||||
#define DECLARE_TYPEOF_CONST(T)
|
||||
#define DECLARE_TYPEOF_COLLECTION(T)
|
||||
|
||||
#define TYPEOF(Value) __typeof(Value)
|
||||
#define TYPEOF_IT(Value) __typeof((Value).begin())
|
||||
#define TYPEOF_CIT(Value) __typeof((Value).begin())
|
||||
#define TYPEOF_RIT(Value) __typeof((Value).rbegin())
|
||||
#define TYPEOF_CRIT(Value) __typeof((Value).rbegin())
|
||||
#define TYPEOF_REF(Value) __typeof(*(Value).begin())&
|
||||
#define TYPEOF_CREF(Value) __typeof(*(Value).begin())&
|
||||
|
||||
#else
|
||||
/// Helper for typeof tricks
|
||||
template<const type_info &ref_type_info> struct TypeOf {};
|
||||
/// Helper for typeof tricks
|
||||
template<const type_info &ref_type_info> struct TypeOf {};
|
||||
|
||||
/// The type of a value
|
||||
#define TYPEOF(Value) TypeOf<typeid(Value)>::type
|
||||
/// The type of an iterator
|
||||
#define TYPEOF_IT(Value) TypeOf<typeid(Value)>::iterator
|
||||
/// The type of a const iterator
|
||||
#define TYPEOF_CIT(Value) TypeOf<typeid(Value)>::const_iterator
|
||||
/// The type of a reverse iterator
|
||||
#define TYPEOF_RIT(Value) TypeOf<typeid(Value)>::reverse_iterator
|
||||
/// The type of a const reverse iterator
|
||||
#define TYPEOF_CRIT(Value) TypeOf<typeid(Value)>::const_reverse_iterator
|
||||
/// The type of a reference
|
||||
#define TYPEOF_REF(Value) TypeOf<typeid(Value)>::reference
|
||||
/// The type of a const reference
|
||||
#define TYPEOF_CREF(Value) TypeOf<typeid(Value)>::const_reference
|
||||
|
||||
/// Declare typeof magic for a specific type
|
||||
#define DECLARE_TYPEOF(T) \
|
||||
template<> struct TypeOf<typeid(T)> { \
|
||||
typedef T type; \
|
||||
typedef T::iterator iterator; \
|
||||
typedef T::const_iterator const_iterator; \
|
||||
typedef T::reverse_iterator reverse_iterator; \
|
||||
typedef T::const_reverse_iterator const_reverse_iterator; \
|
||||
typedef T::reference reference; \
|
||||
typedef T::const_reference const_reference; \
|
||||
}
|
||||
/// Declare typeof magic for a specific type that doesn't support reverse iterators
|
||||
#define DECLARE_TYPEOF_NO_REV(T) \
|
||||
template<> struct TypeOf<typeid(T)> { \
|
||||
typedef T type; \
|
||||
typedef T::iterator iterator; \
|
||||
typedef T::const_iterator const_iterator; \
|
||||
typedef T::reference reference; \
|
||||
typedef T::const_reference const_reference; \
|
||||
}
|
||||
/// Declare typeof magic for a specific type, using const iterators
|
||||
#define DECLARE_TYPEOF_CONST(T) \
|
||||
template<> struct TypeOf<typeid(T)> { \
|
||||
typedef T type; \
|
||||
typedef T::const_iterator iterator; \
|
||||
typedef T::const_iterator const_iterator; \
|
||||
typedef T::const_reverse_iterator reverse_iterator; \
|
||||
typedef T::const_reverse_iterator const_reverse_iterator; \
|
||||
typedef T::const_reference reference; \
|
||||
typedef T::const_reference const_reference; \
|
||||
}
|
||||
|
||||
/// Declare typeof magic for a specific std::vector type
|
||||
#define DECLARE_TYPEOF_COLLECTION(T) DECLARE_TYPEOF(vector< T >); \
|
||||
DECLARE_TYPEOF_CONST(set< T >)
|
||||
|
||||
/// The type of a value
|
||||
#define TYPEOF(Value) TypeOf<typeid(Value)>::type
|
||||
/// The type of an iterator
|
||||
#define TYPEOF_IT(Value) TypeOf<typeid(Value)>::iterator
|
||||
/// The type of a const iterator
|
||||
#define TYPEOF_CIT(Value) TypeOf<typeid(Value)>::const_iterator
|
||||
/// The type of a reverse iterator
|
||||
#define TYPEOF_RIT(Value) TypeOf<typeid(Value)>::reverse_iterator
|
||||
/// The type of a const reverse iterator
|
||||
#define TYPEOF_CRIT(Value) TypeOf<typeid(Value)>::const_reverse_iterator
|
||||
/// The type of a reference
|
||||
#define TYPEOF_REF(Value) TypeOf<typeid(Value)>::reference
|
||||
/// The type of a const reference
|
||||
#define TYPEOF_CREF(Value) TypeOf<typeid(Value)>::const_reference
|
||||
|
||||
/// Declare typeof magic for a specific type
|
||||
#define DECLARE_TYPEOF(T) \
|
||||
template<> struct TypeOf<typeid(T)> { \
|
||||
typedef T type; \
|
||||
typedef T::iterator iterator; \
|
||||
typedef T::const_iterator const_iterator; \
|
||||
typedef T::reverse_iterator reverse_iterator; \
|
||||
typedef T::const_reverse_iterator const_reverse_iterator; \
|
||||
typedef T::reference reference; \
|
||||
typedef T::const_reference const_reference; \
|
||||
}
|
||||
/// Declare typeof magic for a specific type that doesn't support reverse iterators
|
||||
#define DECLARE_TYPEOF_NO_REV(T) \
|
||||
template<> struct TypeOf<typeid(T)> { \
|
||||
typedef T type; \
|
||||
typedef T::iterator iterator; \
|
||||
typedef T::const_iterator const_iterator; \
|
||||
typedef T::reference reference; \
|
||||
typedef T::const_reference const_reference; \
|
||||
}
|
||||
/// Declare typeof magic for a specific type, using const iterators
|
||||
#define DECLARE_TYPEOF_CONST(T) \
|
||||
template<> struct TypeOf<typeid(T)> { \
|
||||
typedef T type; \
|
||||
typedef T::const_iterator iterator; \
|
||||
typedef T::const_iterator const_iterator; \
|
||||
typedef T::const_reverse_iterator reverse_iterator; \
|
||||
typedef T::const_reverse_iterator const_reverse_iterator; \
|
||||
typedef T::const_reference reference; \
|
||||
typedef T::const_reference const_reference; \
|
||||
}
|
||||
|
||||
/// Declare typeof magic for a specific std::vector type
|
||||
#define DECLARE_TYPEOF_COLLECTION(T) DECLARE_TYPEOF(vector< T >); \
|
||||
DECLARE_TYPEOF_CONST(set< T >)
|
||||
|
||||
#endif
|
||||
|
||||
/// Use for template classes
|
||||
|
||||
+98
-98
@@ -33,98 +33,98 @@
|
||||
template <typename Key, typename Value>
|
||||
class IndexMap : private vector<Value> {
|
||||
public:
|
||||
using typename vector<Value>::iterator;
|
||||
using typename vector<Value>::const_iterator;
|
||||
using typename vector<Value>::reference;
|
||||
using typename vector<Value>::const_reference;
|
||||
using vector<Value>::empty;
|
||||
using vector<Value>::size;
|
||||
using vector<Value>::begin;
|
||||
using vector<Value>::end;
|
||||
using vector<Value>::clear;
|
||||
using vector<Value>::at; // for using numeric indices directly
|
||||
|
||||
/// Initialize this map with default values given a list of keys
|
||||
/** has no effect if already initialized with the given keys */
|
||||
bool init(const vector<Key>& keys) {
|
||||
if (!this->empty() && (keys.empty() || get_key(this->front()) != keys.front())) {
|
||||
// switch to different keys
|
||||
clear();
|
||||
}
|
||||
if (this->size() == keys.size()) return false;
|
||||
this->reserve(keys.size());
|
||||
for(typename vector<Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it) {
|
||||
const Key& key = *it;
|
||||
assert(key);
|
||||
if (key->index >= this->size()) this->resize(key->index + 1);
|
||||
init_object(key, (*this)[key->index]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/// Initialize this map with cloned values from another list
|
||||
void cloneFrom(const IndexMap<Key,Value>& values) {
|
||||
if (this->size() == values.size()) return;
|
||||
this->reserve(values.size());
|
||||
for(size_t index = size() ; index < values.size() ; ++index) {
|
||||
const Value& value = values[index];
|
||||
vector<Value>::push_back(value ? value->clone() : value);
|
||||
}
|
||||
}
|
||||
/// Change this map by adding an additional key and value
|
||||
void add(const Key& key, const Value& value) {
|
||||
assert(get_key(value) == key);
|
||||
if (key->index >= this->size()) this->resize(key->index + 1);
|
||||
(*this)[key->index] = value;
|
||||
}
|
||||
|
||||
/// Retrieve a value given its key
|
||||
inline Value operator [] (const Key& key) {
|
||||
assert(key);
|
||||
assert(this->size() > key->index);
|
||||
return at(key->index);
|
||||
}
|
||||
/// Retrieve a value given its key, if it matches
|
||||
inline Value tryGet (const Key& key) {
|
||||
assert(key);
|
||||
if (this->size() <= key->index) return Value();
|
||||
Value v = at(key->index);
|
||||
if (get_key(v) != key) return Value();
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Is a value contained in this index map?
|
||||
inline bool contains(const Value& value) const {
|
||||
assert(value);
|
||||
size_t index = get_key(value)->index;
|
||||
return index < this->size() && (*this)[index] == value;
|
||||
}
|
||||
|
||||
/// Is a key in the domain of this index map?
|
||||
inline bool containsKey(const Key& key) const {
|
||||
assert(key);
|
||||
return key->index < this->size() && get_key((*this)[key->index]) == key;
|
||||
}
|
||||
|
||||
/// Find a value given the key name, return an iterator
|
||||
template <typename Name>
|
||||
typename vector<Value>::const_iterator find(const Name& key) const {
|
||||
for(typename vector<Value>::const_iterator it = begin() ; it != end() ; ++it) {
|
||||
if (get_key_name(*it) == key) return it;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
inline void swap(IndexMap& b) {
|
||||
vector<Value>::swap(b);
|
||||
}
|
||||
|
||||
using typename vector<Value>::iterator;
|
||||
using typename vector<Value>::const_iterator;
|
||||
using typename vector<Value>::reference;
|
||||
using typename vector<Value>::const_reference;
|
||||
using vector<Value>::empty;
|
||||
using vector<Value>::size;
|
||||
using vector<Value>::begin;
|
||||
using vector<Value>::end;
|
||||
using vector<Value>::clear;
|
||||
using vector<Value>::at; // for using numeric indices directly
|
||||
|
||||
/// Initialize this map with default values given a list of keys
|
||||
/** has no effect if already initialized with the given keys */
|
||||
bool init(const vector<Key>& keys) {
|
||||
if (!this->empty() && (keys.empty() || get_key(this->front()) != keys.front())) {
|
||||
// switch to different keys
|
||||
clear();
|
||||
}
|
||||
if (this->size() == keys.size()) return false;
|
||||
this->reserve(keys.size());
|
||||
for(typename vector<Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it) {
|
||||
const Key& key = *it;
|
||||
assert(key);
|
||||
if (key->index >= this->size()) this->resize(key->index + 1);
|
||||
init_object(key, (*this)[key->index]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/// Initialize this map with cloned values from another list
|
||||
void cloneFrom(const IndexMap<Key,Value>& values) {
|
||||
if (this->size() == values.size()) return;
|
||||
this->reserve(values.size());
|
||||
for(size_t index = size() ; index < values.size() ; ++index) {
|
||||
const Value& value = values[index];
|
||||
vector<Value>::push_back(value ? value->clone() : value);
|
||||
}
|
||||
}
|
||||
/// Change this map by adding an additional key and value
|
||||
void add(const Key& key, const Value& value) {
|
||||
assert(get_key(value) == key);
|
||||
if (key->index >= this->size()) this->resize(key->index + 1);
|
||||
(*this)[key->index] = value;
|
||||
}
|
||||
|
||||
/// Retrieve a value given its key
|
||||
inline Value operator [] (const Key& key) {
|
||||
assert(key);
|
||||
assert(this->size() > key->index);
|
||||
return at(key->index);
|
||||
}
|
||||
/// Retrieve a value given its key, if it matches
|
||||
inline Value tryGet (const Key& key) {
|
||||
assert(key);
|
||||
if (this->size() <= key->index) return Value();
|
||||
Value v = at(key->index);
|
||||
if (get_key(v) != key) return Value();
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Is a value contained in this index map?
|
||||
inline bool contains(const Value& value) const {
|
||||
assert(value);
|
||||
size_t index = get_key(value)->index;
|
||||
return index < this->size() && (*this)[index] == value;
|
||||
}
|
||||
|
||||
/// Is a key in the domain of this index map?
|
||||
inline bool containsKey(const Key& key) const {
|
||||
assert(key);
|
||||
return key->index < this->size() && get_key((*this)[key->index]) == key;
|
||||
}
|
||||
|
||||
/// Find a value given the key name, return an iterator
|
||||
template <typename Name>
|
||||
typename vector<Value>::const_iterator find(const Name& key) const {
|
||||
for(typename vector<Value>::const_iterator it = begin() ; it != end() ; ++it) {
|
||||
if (get_key_name(*it) == key) return it;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
inline void swap(IndexMap& b) {
|
||||
vector<Value>::swap(b);
|
||||
}
|
||||
|
||||
private:
|
||||
using vector<Value>::operator [];
|
||||
using vector<Value>::operator [];
|
||||
};
|
||||
|
||||
template <typename Key, typename Value>
|
||||
inline void swap(IndexMap<Key,Value>& a, IndexMap<Key,Value>& b) {
|
||||
a.swap(b);
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
|
||||
@@ -135,8 +135,8 @@ inline void swap(IndexMap<Key,Value>& a, IndexMap<Key,Value>& b) {
|
||||
*/
|
||||
template <typename Key, typename Value>
|
||||
struct DelayedIndexMapsData : public IntrusivePtrBase<DelayedIndexMapsData<Key, Value> > {
|
||||
String unread_data;
|
||||
IndexMap<Key,Value> read_data;
|
||||
String unread_data;
|
||||
IndexMap<Key,Value> read_data;
|
||||
};
|
||||
|
||||
/// A map<String,IndexMap> where the reading of the index map depends on the name.
|
||||
@@ -146,16 +146,16 @@ struct DelayedIndexMapsData : public IntrusivePtrBase<DelayedIndexMapsData<Key,
|
||||
template <typename Key, typename Value>
|
||||
class DelayedIndexMaps {
|
||||
public:
|
||||
/// Get the data for a specific name. Initialize the map with init_with (if it is not alread initialized)
|
||||
IndexMap<Key,Value>& get(const String& name, const vector<Key>& init_with);
|
||||
/// Clear the delayed index map
|
||||
void clear();
|
||||
/// Get the data for a specific name. Initialize the map with init_with (if it is not alread initialized)
|
||||
IndexMap<Key,Value>& get(const String& name, const vector<Key>& init_with);
|
||||
/// Clear the delayed index map
|
||||
void clear();
|
||||
private:
|
||||
map<String, intrusive_ptr<DelayedIndexMapsData<Key,Value> > > data;
|
||||
friend class Reader;
|
||||
friend class Writer;
|
||||
friend class GetDefaultMember;
|
||||
friend class GetMember;
|
||||
map<String, intrusive_ptr<DelayedIndexMapsData<Key,Value> > > data;
|
||||
friend class Reader;
|
||||
friend class Writer;
|
||||
friend class GetDefaultMember;
|
||||
friend class GetMember;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -34,10 +34,10 @@ template <> void GetDefaultMember::handle(const wxDateTime& v) { value = to_sc
|
||||
// ----------------------------------------------------------------------------- : GetMember
|
||||
|
||||
GetMember::GetMember(const String& name)
|
||||
: target_name(name)
|
||||
: target_name(name)
|
||||
{}
|
||||
|
||||
// caused by the pattern: if (!tag.isComplex()) { REFLECT_NAMELESS(stuff) }
|
||||
template <> void GetMember::handle(const String& v) {
|
||||
throw InternalError(_("GetDefaultMember::handle"));
|
||||
throw InternalError(_("GetDefaultMember::handle"));
|
||||
}
|
||||
|
||||
+107
-107
@@ -24,42 +24,42 @@ template <typename T> class Scriptable;
|
||||
/** The member is wrapped in a ScriptValue */
|
||||
class GetDefaultMember {
|
||||
public:
|
||||
/// Tell the reflection code we are not reading
|
||||
inline bool reading() const { return false; }
|
||||
inline bool scripting() const { return true; }
|
||||
inline bool isComplex() const { return false; }
|
||||
inline void addAlias(int, const Char*, const Char*) {}
|
||||
inline void handleIgnore(int, const Char*) {}
|
||||
|
||||
/// The result, or script_nil if the member was not found
|
||||
inline ScriptValueP result() { return value; }
|
||||
|
||||
// --------------------------------------------------- : Handling objects
|
||||
|
||||
/// Handle an object: we don't match things with a name
|
||||
template <typename T>
|
||||
void handle(const Char* name, const T& object) {}
|
||||
/// Don't handle a value
|
||||
template <typename T>
|
||||
inline void handleNoScript(const Char* name, T& value) {}
|
||||
/// Tell the reflection code we are not reading
|
||||
inline bool reading() const { return false; }
|
||||
inline bool scripting() const { return true; }
|
||||
inline bool isComplex() const { return false; }
|
||||
inline void addAlias(int, const Char*, const Char*) {}
|
||||
inline void handleIgnore(int, const Char*) {}
|
||||
|
||||
/// The result, or script_nil if the member was not found
|
||||
inline ScriptValueP result() { return value; }
|
||||
|
||||
// --------------------------------------------------- : Handling objects
|
||||
|
||||
/// Handle an object: we don't match things with a name
|
||||
template <typename T>
|
||||
void handle(const Char* name, const T& object) {}
|
||||
/// Don't handle a value
|
||||
template <typename T>
|
||||
inline void handleNoScript(const Char* name, T& value) {}
|
||||
|
||||
/// Handle an object: investigate children, or store it if we know how
|
||||
void handle(const Char *);
|
||||
template <typename T> void handle(const T&);
|
||||
|
||||
/// Handle a Defaultable: investigate children
|
||||
template <typename T> void handle(const Defaultable<T>&);
|
||||
template <typename T> void handle(const Scriptable<T>& );
|
||||
template <typename T> void handle(const vector<T>& c) { value = to_script(&c); }
|
||||
template <typename K, typename V> void handle(const map<K,V>& c) { value = to_script(&c); }
|
||||
template <typename K, typename V> void handle(const IndexMap<K,V>& c) { value = to_script(&c); }
|
||||
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&) {}
|
||||
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>& c);
|
||||
template <typename T> void handle(const intrusive_ptr<T>& p) { value = to_script(p); }
|
||||
void handle(const ScriptValueP&);
|
||||
void handle(const ScriptP&);
|
||||
/// Handle an object: investigate children, or store it if we know how
|
||||
void handle(const Char *);
|
||||
template <typename T> void handle(const T&);
|
||||
|
||||
/// Handle a Defaultable: investigate children
|
||||
template <typename T> void handle(const Defaultable<T>&);
|
||||
template <typename T> void handle(const Scriptable<T>& );
|
||||
template <typename T> void handle(const vector<T>& c) { value = to_script(&c); }
|
||||
template <typename K, typename V> void handle(const map<K,V>& c) { value = to_script(&c); }
|
||||
template <typename K, typename V> void handle(const IndexMap<K,V>& c) { value = to_script(&c); }
|
||||
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&) {}
|
||||
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>& c);
|
||||
template <typename T> void handle(const intrusive_ptr<T>& p) { value = to_script(p); }
|
||||
void handle(const ScriptValueP&);
|
||||
void handle(const ScriptP&);
|
||||
private:
|
||||
ScriptValueP value; ///< The value we found (if any)
|
||||
ScriptValueP value; ///< The value we found (if any)
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : GetMember
|
||||
@@ -68,96 +68,96 @@ class GetDefaultMember {
|
||||
/** The member is wrapped in a ScriptValue */
|
||||
class GetMember : private GetDefaultMember {
|
||||
public:
|
||||
/// Construct a member getter that looks for the given name
|
||||
GetMember(const String& name);
|
||||
|
||||
/// Tell the reflection code we are not reading
|
||||
inline bool reading() const { return false; }
|
||||
inline bool scripting() const { return true; }
|
||||
inline bool isComplex() const { return true; }
|
||||
inline void addAlias(int, const Char*, const Char*) {}
|
||||
inline void handleIgnore(int, const Char*) {}
|
||||
|
||||
/// The result, or script_nil if the member was not found
|
||||
inline ScriptValueP result() { return gdm.result(); }
|
||||
|
||||
// --------------------------------------------------- : Handling objects
|
||||
|
||||
/// Handle an object: we are done if the name matches
|
||||
template <typename T>
|
||||
void handle(const Char* name, const T& object) {
|
||||
if (!gdm.result() && cannocial_name_compare(target_name, name)) {
|
||||
gdm.handle(object);
|
||||
}
|
||||
}
|
||||
/// Don't handle a value
|
||||
template <typename T>
|
||||
inline void handleNoScript(const Char* name, T& value) {}
|
||||
/// Handle an object: investigate children
|
||||
template <typename T> void handle(const T&);
|
||||
/// Handle an index map: invistigate keys
|
||||
template <typename K, typename V> void handle(const IndexMap<K,V>& m) {
|
||||
if (gdm.result()) return;
|
||||
for (typename IndexMap<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
|
||||
if (get_key_name(*it) == target_name) {
|
||||
gdm.handle(*it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&);
|
||||
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>&);
|
||||
|
||||
/// Construct a member getter that looks for the given name
|
||||
GetMember(const String& name);
|
||||
|
||||
/// Tell the reflection code we are not reading
|
||||
inline bool reading() const { return false; }
|
||||
inline bool scripting() const { return true; }
|
||||
inline bool isComplex() const { return true; }
|
||||
inline void addAlias(int, const Char*, const Char*) {}
|
||||
inline void handleIgnore(int, const Char*) {}
|
||||
|
||||
/// The result, or script_nil if the member was not found
|
||||
inline ScriptValueP result() { return gdm.result(); }
|
||||
|
||||
// --------------------------------------------------- : Handling objects
|
||||
|
||||
/// Handle an object: we are done if the name matches
|
||||
template <typename T>
|
||||
void handle(const Char* name, const T& object) {
|
||||
if (!gdm.result() && cannocial_name_compare(target_name, name)) {
|
||||
gdm.handle(object);
|
||||
}
|
||||
}
|
||||
/// Don't handle a value
|
||||
template <typename T>
|
||||
inline void handleNoScript(const Char* name, T& value) {}
|
||||
/// Handle an object: investigate children
|
||||
template <typename T> void handle(const T&);
|
||||
/// Handle an index map: invistigate keys
|
||||
template <typename K, typename V> void handle(const IndexMap<K,V>& m) {
|
||||
if (gdm.result()) return;
|
||||
for (typename IndexMap<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
|
||||
if (get_key_name(*it) == target_name) {
|
||||
gdm.handle(*it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&);
|
||||
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>&);
|
||||
|
||||
private:
|
||||
const String& target_name; ///< The name we are looking for
|
||||
GetDefaultMember gdm; ///< Object to store and retrieve the value
|
||||
const String& target_name; ///< The name we are looking for
|
||||
GetDefaultMember gdm; ///< Object to store and retrieve the value
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Reflection
|
||||
|
||||
/// Implement reflection as used by GetDefaultMember
|
||||
#define REFLECT_OBJECT_GET_DEFAULT_MEMBER(Cls) REFLECT_WRITE_YES(Cls,GetDefaultMember)
|
||||
#define REFLECT_OBJECT_GET_MEMBER(Cls) REFLECT_WRITE_YES(Cls,GetMember)
|
||||
#define REFLECT_OBJECT_GET_DEFAULT_MEMBER_NOT(Cls) REFLECT_WRITE_NO(Cls,GetDefaultMember)
|
||||
#define REFLECT_OBJECT_GET_MEMBER_NOT(Cls) REFLECT_WRITE_NO(Cls,GetMember)
|
||||
#define REFLECT_OBJECT_GET_DEFAULT_MEMBER(Cls) REFLECT_WRITE_YES(Cls,GetDefaultMember)
|
||||
#define REFLECT_OBJECT_GET_MEMBER(Cls) REFLECT_WRITE_YES(Cls,GetMember)
|
||||
#define REFLECT_OBJECT_GET_DEFAULT_MEMBER_NOT(Cls) REFLECT_WRITE_NO(Cls,GetDefaultMember)
|
||||
#define REFLECT_OBJECT_GET_MEMBER_NOT(Cls) REFLECT_WRITE_NO(Cls,GetMember)
|
||||
|
||||
#define REFLECT_WRITE_YES(Cls, Tag) \
|
||||
template<> void Tag::handle<Cls>(const Cls& object) { \
|
||||
const_cast<Cls&>(object).reflect(*this); \
|
||||
} \
|
||||
void Cls::reflect(Tag& tag) { \
|
||||
reflect_impl(tag); \
|
||||
}
|
||||
#define REFLECT_WRITE_YES(Cls, Tag) \
|
||||
template<> void Tag::handle<Cls>(const Cls& object) { \
|
||||
const_cast<Cls&>(object).reflect(*this); \
|
||||
} \
|
||||
void Cls::reflect(Tag& tag) { \
|
||||
reflect_impl(tag); \
|
||||
}
|
||||
|
||||
#define REFLECT_WRITE_NO(Cls, Tag) \
|
||||
template<> void Tag::handle<Cls>(const Cls& object) {} \
|
||||
void Cls::reflect(Tag& tag) {}
|
||||
#define REFLECT_WRITE_NO(Cls, Tag) \
|
||||
template<> void Tag::handle<Cls>(const Cls& object) {} \
|
||||
void Cls::reflect(Tag& tag) {}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Reflection for enumerations
|
||||
|
||||
/// Implement enum reflection as used by GetMember
|
||||
#define REFLECT_ENUM_GET_MEMBER(Enum) \
|
||||
template<> void GetDefaultMember::handle<Enum>(const Enum& enum_) { \
|
||||
EnumGetMember egm(*this); \
|
||||
reflect_ ## Enum(const_cast<Enum&>(enum_), egm); \
|
||||
}
|
||||
#define REFLECT_ENUM_GET_MEMBER(Enum) \
|
||||
template<> void GetDefaultMember::handle<Enum>(const Enum& enum_) { \
|
||||
EnumGetMember egm(*this); \
|
||||
reflect_ ## Enum(const_cast<Enum&>(enum_), egm); \
|
||||
}
|
||||
|
||||
/// 'Tag' to be used when reflecting enumerations for GetMember
|
||||
class EnumGetMember {
|
||||
public:
|
||||
inline EnumGetMember(GetDefaultMember& gdm)
|
||||
: gdm(gdm) {}
|
||||
|
||||
/// Handle a possible value for the enum, if the name matches the name in the input
|
||||
template <typename Enum>
|
||||
inline void handle(const Char* name, Enum value, Enum enum_) {
|
||||
if (enum_ == value) {
|
||||
gdm.handle(String(name));
|
||||
}
|
||||
}
|
||||
|
||||
inline EnumGetMember(GetDefaultMember& gdm)
|
||||
: gdm(gdm) {}
|
||||
|
||||
/// Handle a possible value for the enum, if the name matches the name in the input
|
||||
template <typename Enum>
|
||||
inline void handle(const Char* name, Enum value, Enum enum_) {
|
||||
if (enum_ == value) {
|
||||
gdm.handle(String(name));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
GetDefaultMember& gdm; ///< The object to store output in
|
||||
GetDefaultMember& gdm; ///< The object to store output in
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+421
-421
File diff suppressed because it is too large
Load Diff
+171
-171
@@ -49,154 +49,154 @@ DECLARE_DYNAMIC_ARG(Package*, clipboard_package);
|
||||
*/
|
||||
class Package : public IntrusivePtrVirtualBase {
|
||||
public:
|
||||
// --------------------------------------------------- : Managing the outside of the package
|
||||
// --------------------------------------------------- : Managing the outside of the package
|
||||
|
||||
/// Creates a new package
|
||||
Package();
|
||||
virtual ~Package();
|
||||
/// Creates a new package
|
||||
Package();
|
||||
virtual ~Package();
|
||||
|
||||
/// Is a file opened?
|
||||
bool isOpened() const;
|
||||
/// Must the package be saved with saveAs()?
|
||||
/// This is the case if the package has not been saved/opened before
|
||||
bool needSaveAs() const;
|
||||
/// Determines the short name of this package: the filename without path or extension
|
||||
String name() const;
|
||||
/// Return the relative filename of this file, the name and extension
|
||||
String relativeFilename() const;
|
||||
/// Return the absolute filename of this file
|
||||
const String& absoluteFilename() const;
|
||||
/// The time this package was last modified
|
||||
inline wxDateTime lastModified() const { return modified; }
|
||||
/// Is a file opened?
|
||||
bool isOpened() const;
|
||||
/// Must the package be saved with saveAs()?
|
||||
/// This is the case if the package has not been saved/opened before
|
||||
bool needSaveAs() const;
|
||||
/// Determines the short name of this package: the filename without path or extension
|
||||
String name() const;
|
||||
/// Return the relative filename of this file, the name and extension
|
||||
String relativeFilename() const;
|
||||
/// Return the absolute filename of this file
|
||||
const String& absoluteFilename() const;
|
||||
/// The time this package was last modified
|
||||
inline wxDateTime lastModified() const { return modified; }
|
||||
|
||||
/// Open a package
|
||||
/**
|
||||
* Should only be called when the package is constructed using the default constructor!
|
||||
*
|
||||
* If 'fast' is set, then for directories a full directory listing is not performed.
|
||||
* This means that the file_infos will not be fully initialized.
|
||||
*
|
||||
* @pre open not called before [TODO]
|
||||
*/
|
||||
void open(const String& package, bool fast = false);
|
||||
/// Open a package
|
||||
/**
|
||||
* Should only be called when the package is constructed using the default constructor!
|
||||
*
|
||||
* If 'fast' is set, then for directories a full directory listing is not performed.
|
||||
* This means that the file_infos will not be fully initialized.
|
||||
*
|
||||
* @pre open not called before [TODO]
|
||||
*/
|
||||
void open(const String& package, bool fast = false);
|
||||
|
||||
/// Saves the package
|
||||
/**
|
||||
* By default saves as a zip file, unless it was already a directory.
|
||||
*
|
||||
* If remove_unused=true all files that were in the file and
|
||||
* are not touched with referenceFile will be deleted from the new archive!
|
||||
* This is a form of garbage collection, to get rid of old picture files for example.
|
||||
*/
|
||||
void save(bool remove_unused = true);
|
||||
/// Saves the package
|
||||
/**
|
||||
* By default saves as a zip file, unless it was already a directory.
|
||||
*
|
||||
* If remove_unused=true all files that were in the file and
|
||||
* are not touched with referenceFile will be deleted from the new archive!
|
||||
* This is a form of garbage collection, to get rid of old picture files for example.
|
||||
*/
|
||||
void save(bool remove_unused = true);
|
||||
|
||||
/// Saves the package under a different filename
|
||||
void saveAs(const String& package, bool remove_unused = true);
|
||||
/// Saves the package under a different filename
|
||||
void saveAs(const String& package, bool remove_unused = true);
|
||||
|
||||
/// Saves the package under a different filename, but keep the old one open
|
||||
void saveCopy(const String& package);
|
||||
/// Saves the package under a different filename, but keep the old one open
|
||||
void saveCopy(const String& package);
|
||||
|
||||
|
||||
// --------------------------------------------------- : Managing the inside of the package
|
||||
// --------------------------------------------------- : Managing the inside of the package
|
||||
|
||||
/// Open an input stream for a file in the package.
|
||||
InputStreamP openIn(const String& file);
|
||||
/// Open an input stream for a file in the package.
|
||||
InputStreamP openIn(const String& file);
|
||||
|
||||
/// Open an output stream for a file in the package.
|
||||
/// (changes are only committed with save())
|
||||
OutputStreamP openOut(const String& file);
|
||||
/// Open an output stream for a file in the package.
|
||||
/// (changes are only committed with save())
|
||||
OutputStreamP openOut(const String& file);
|
||||
|
||||
/// Get a filename that can be written to to modfify a file in the package
|
||||
/// (changes are only committed with save())
|
||||
String nameOut(const String& file);
|
||||
/// Get a filename that can be written to to modfify a file in the package
|
||||
/// (changes are only committed with save())
|
||||
String nameOut(const String& file);
|
||||
|
||||
/// Creates a new, unique, filename with the specified prefix and suffix
|
||||
/// for example newFileName("image/",".jpg") -> "image/1.jpg"
|
||||
/// Returns the name of a temporary file that can be written to.
|
||||
FileName newFileName(const String& prefix, const String& suffix);
|
||||
/// Creates a new, unique, filename with the specified prefix and suffix
|
||||
/// for example newFileName("image/",".jpg") -> "image/1.jpg"
|
||||
/// Returns the name of a temporary file that can be written to.
|
||||
FileName newFileName(const String& prefix, const String& suffix);
|
||||
|
||||
/// Signal that a file is still used by this package.
|
||||
/// Must be called for files not opened using openOut/nameOut
|
||||
/// If they are to be kept in the package.
|
||||
void referenceFile(const String& file);
|
||||
/// Signal that a file is still used by this package.
|
||||
/// Must be called for files not opened using openOut/nameOut
|
||||
/// If they are to be kept in the package.
|
||||
void referenceFile(const String& file);
|
||||
|
||||
/// Get an 'absolute filename' for a file in the package.
|
||||
/// This file can later be opened from anywhere (other process) using openAbsoluteFile()
|
||||
String absoluteName(const String& file);
|
||||
/// Get an 'absolute filename' for a file in the package.
|
||||
/// This file can later be opened from anywhere (other process) using openAbsoluteFile()
|
||||
String absoluteName(const String& file);
|
||||
|
||||
/// Open a file given an absolute filename
|
||||
static InputStreamP openAbsoluteFile(const String& name);
|
||||
/// Open a file given an absolute filename
|
||||
static InputStreamP openAbsoluteFile(const String& name);
|
||||
|
||||
// --------------------------------------------------- : Managing the inside of the package : Reader/writer
|
||||
// --------------------------------------------------- : Managing the inside of the package : Reader/writer
|
||||
|
||||
template <typename T>
|
||||
void readFile(const String& file, T& obj);
|
||||
template <typename T>
|
||||
void readFile(const String& file, T& obj);
|
||||
|
||||
template <typename T>
|
||||
T readFile(const String& file) {
|
||||
T obj;
|
||||
readFile(file, obj);
|
||||
return obj;
|
||||
}
|
||||
template <typename T>
|
||||
T readFile(const String& file) {
|
||||
T obj;
|
||||
readFile(file, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void writeFile(const String& file, const T& obj, Version file_version) {
|
||||
Writer writer(openOut(file), file_version);
|
||||
writer.handle(obj);
|
||||
}
|
||||
template <typename T>
|
||||
void writeFile(const String& file, const T& obj, Version file_version) {
|
||||
Writer writer(openOut(file), file_version);
|
||||
writer.handle(obj);
|
||||
}
|
||||
|
||||
protected:
|
||||
// TODO: I dislike putting this here very much. There ought to be a better way.
|
||||
virtual VCSP getVCS() { return intrusive(new VCS()); }
|
||||
// TODO: I dislike putting this here very much. There ought to be a better way.
|
||||
virtual VCSP getVCS() { return intrusive(new VCS()); }
|
||||
|
||||
/// true if this is a zip file, false if a directory
|
||||
bool isZipfile() const { return !wxDirExists(filename); }
|
||||
/// true if this is a zip file, false if a directory
|
||||
bool isZipfile() const { return !wxDirExists(filename); }
|
||||
|
||||
// --------------------------------------------------- : Private stuff
|
||||
// --------------------------------------------------- : Private stuff
|
||||
private:
|
||||
|
||||
/// Information about a file in the package
|
||||
struct FileInfo {
|
||||
FileInfo();
|
||||
~FileInfo();
|
||||
bool keep; ///< Should this file be kept in the package? (as opposed to deleting it)
|
||||
bool created; ///< Was this file just created (e.g. should the VCS add it?)
|
||||
String tempName; ///< Name of the temporary file where new contents of this file are placed
|
||||
wxZipEntry* zipEntry; ///< Entry in the zip file for this file
|
||||
/// Is this file changed, and therefore written to a temporary file?
|
||||
inline bool wasWritten() const { return !tempName.empty(); }
|
||||
};
|
||||
/// Information about a file in the package
|
||||
struct FileInfo {
|
||||
FileInfo();
|
||||
~FileInfo();
|
||||
bool keep; ///< Should this file be kept in the package? (as opposed to deleting it)
|
||||
bool created; ///< Was this file just created (e.g. should the VCS add it?)
|
||||
String tempName; ///< Name of the temporary file where new contents of this file are placed
|
||||
wxZipEntry* zipEntry; ///< Entry in the zip file for this file
|
||||
/// Is this file changed, and therefore written to a temporary file?
|
||||
inline bool wasWritten() const { return !tempName.empty(); }
|
||||
};
|
||||
|
||||
/// Filename of the package
|
||||
String filename;
|
||||
/// Last modified time
|
||||
DateTime modified;
|
||||
/// Filename of the package
|
||||
String filename;
|
||||
/// Last modified time
|
||||
DateTime modified;
|
||||
|
||||
public:
|
||||
/// Information on files in the package
|
||||
/** Note: must be public for DECLARE_TYPEOF to work */
|
||||
typedef map<String, FileInfo> FileInfos;
|
||||
inline const FileInfos& getFileInfos() const { return files; }
|
||||
/// When was a file last modified?
|
||||
DateTime modificationTime(const pair<String, FileInfo>& fi) const;
|
||||
/// Information on files in the package
|
||||
/** Note: must be public for DECLARE_TYPEOF to work */
|
||||
typedef map<String, FileInfo> FileInfos;
|
||||
inline const FileInfos& getFileInfos() const { return files; }
|
||||
/// When was a file last modified?
|
||||
DateTime modificationTime(const pair<String, FileInfo>& fi) const;
|
||||
private:
|
||||
/// All files in the package
|
||||
FileInfos files;
|
||||
/// Filestream for reading zip files
|
||||
wxFileInputStream* fileStream;
|
||||
/// Filestream for reading zip files
|
||||
wxZipInputStream* zipStream;
|
||||
/// All files in the package
|
||||
FileInfos files;
|
||||
/// Filestream for reading zip files
|
||||
wxFileInputStream* fileStream;
|
||||
/// Filestream for reading zip files
|
||||
wxZipInputStream* zipStream;
|
||||
|
||||
void loadZipStream();
|
||||
void openDirectory(bool fast = false);
|
||||
void openSubdir(const String&);
|
||||
void openZipfile();
|
||||
void reopen();
|
||||
void removeTempFiles(bool remove_unused);
|
||||
void clearKeepFlag();
|
||||
void saveToZipfile(const String&, bool remove_unused, bool is_copy);
|
||||
void saveToDirectory(const String&, bool remove_unused, bool is_copy);
|
||||
FileInfos::iterator addFile(const String& file);
|
||||
void loadZipStream();
|
||||
void openDirectory(bool fast = false);
|
||||
void openSubdir(const String&);
|
||||
void openZipfile();
|
||||
void reopen();
|
||||
void removeTempFiles(bool remove_unused);
|
||||
void clearKeepFlag();
|
||||
void saveToZipfile(const String&, bool remove_unused, bool is_copy);
|
||||
void saveToDirectory(const String&, bool remove_unused, bool is_copy);
|
||||
FileInfos::iterator addFile(const String& file);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Packaged
|
||||
@@ -204,11 +204,11 @@ class Package : public IntrusivePtrVirtualBase {
|
||||
/// Dependencies of a package
|
||||
class PackageDependency : public IntrusivePtrBase<PackageDependency> {
|
||||
public:
|
||||
String package; ///< Name of the package someone depends on
|
||||
vector<String> suggests; ///< Packages suggested to fulfill this dependency
|
||||
Version version; ///< Minimal required version of that package
|
||||
String package; ///< Name of the package someone depends on
|
||||
vector<String> suggests; ///< Packages suggested to fulfill this dependency
|
||||
Version version; ///< Minimal required version of that package
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
/// Utility class for data types that are always stored in a package.
|
||||
@@ -216,53 +216,53 @@ class PackageDependency : public IntrusivePtrBase<PackageDependency> {
|
||||
*/
|
||||
class Packaged : public Package {
|
||||
public:
|
||||
Packaged();
|
||||
virtual ~Packaged() {}
|
||||
Packaged();
|
||||
virtual ~Packaged() {}
|
||||
|
||||
Version version; ///< Version number of this package
|
||||
Version compatible_version; ///< Earliest version number this package is compatible with
|
||||
String installer_group; ///< Group to place this package in in the installer
|
||||
String short_name; ///< Short name of this package
|
||||
String full_name; ///< Name of this package, for menus etc.
|
||||
String icon_filename; ///< Filename of icon to use in package lists
|
||||
vector<PackageDependencyP> dependencies; ///< Dependencies of this package
|
||||
int position_hint; ///< A hint for the package list
|
||||
Version version; ///< Version number of this package
|
||||
Version compatible_version; ///< Earliest version number this package is compatible with
|
||||
String installer_group; ///< Group to place this package in in the installer
|
||||
String short_name; ///< Short name of this package
|
||||
String full_name; ///< Name of this package, for menus etc.
|
||||
String icon_filename; ///< Filename of icon to use in package lists
|
||||
vector<PackageDependencyP> dependencies; ///< Dependencies of this package
|
||||
int position_hint; ///< A hint for the package list
|
||||
|
||||
/// Get an input stream for the package icon, if there is any
|
||||
InputStreamP openIconFile();
|
||||
/// Get an input stream for the package icon, if there is any
|
||||
InputStreamP openIconFile();
|
||||
|
||||
/// Open a package, and read the data
|
||||
/** if just_header is true, then the package is not fully parsed.
|
||||
*/
|
||||
void open(const String& package, bool just_header = false);
|
||||
/// Ensure the package is fully loaded.
|
||||
void loadFully();
|
||||
void save();
|
||||
void saveAs(const String& package, bool remove_unused = true);
|
||||
void saveCopy(const String& package);
|
||||
/// Open a package, and read the data
|
||||
/** if just_header is true, then the package is not fully parsed.
|
||||
*/
|
||||
void open(const String& package, bool just_header = false);
|
||||
/// Ensure the package is fully loaded.
|
||||
void loadFully();
|
||||
void save();
|
||||
void saveAs(const String& package, bool remove_unused = true);
|
||||
void saveCopy(const String& package);
|
||||
|
||||
/// Check if this package lists a dependency on the given package
|
||||
/** This is done to force people to fill in the dependencies */
|
||||
void requireDependency(Packaged* package);
|
||||
/// Check if this package lists a dependency on the given package
|
||||
/** This is done to force people to fill in the dependencies */
|
||||
void requireDependency(Packaged* package);
|
||||
|
||||
inline bool isFullyLoaded () const {
|
||||
return fully_loaded;
|
||||
}
|
||||
inline bool isFullyLoaded () const {
|
||||
return fully_loaded;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// filename of the data file, and extension of the package file
|
||||
virtual String typeName() const = 0;
|
||||
/// Can be overloaded to do validation after loading
|
||||
virtual void validate(Version file_app_version);
|
||||
/// What file version should be used for writing files?
|
||||
virtual Version fileVersion() const = 0;
|
||||
/// filename of the data file, and extension of the package file
|
||||
virtual String typeName() const = 0;
|
||||
/// Can be overloaded to do validation after loading
|
||||
virtual void validate(Version file_app_version);
|
||||
/// What file version should be used for writing files?
|
||||
virtual Version fileVersion() const = 0;
|
||||
|
||||
DECLARE_REFLECTION_VIRTUAL();
|
||||
DECLARE_REFLECTION_VIRTUAL();
|
||||
|
||||
private:
|
||||
bool fully_loaded; ///< Is the package fully loaded?
|
||||
friend struct JustAsPackageProxy;
|
||||
friend class Installer;
|
||||
bool fully_loaded; ///< Is the package fully loaded?
|
||||
friend struct JustAsPackageProxy;
|
||||
friend class Installer;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : IncludePackage
|
||||
@@ -270,9 +270,9 @@ class Packaged : public Package {
|
||||
/// A package that just contains a bunch of files that are used from other packages
|
||||
class IncludePackage : public Packaged {
|
||||
protected:
|
||||
String typeName() const;
|
||||
Version fileVersion() const;
|
||||
DECLARE_REFLECTION();
|
||||
String typeName() const;
|
||||
Version fileVersion() const;
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Utility
|
||||
@@ -280,9 +280,9 @@ class IncludePackage : public Packaged {
|
||||
/// Open a package with the given filename
|
||||
template <typename T>
|
||||
intrusive_ptr<T> open_package(const String& filename) {
|
||||
intrusive_ptr<T> package(new T);
|
||||
package->open(filename);
|
||||
return package;
|
||||
intrusive_ptr<T> package(new T);
|
||||
package->open(filename);
|
||||
return package;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : readFile definition
|
||||
@@ -291,12 +291,12 @@ intrusive_ptr<T> open_package(const String& filename) {
|
||||
template <typename T>
|
||||
inline void Package::readFile(const String& file, T& obj)
|
||||
{
|
||||
Reader reader(openIn(file), dynamic_cast<Packaged*>(this), absoluteFilename() + _("/") + file);
|
||||
try {
|
||||
reader.handle_greedy(obj);
|
||||
} catch (const ParseError& err) {
|
||||
throw FileParseError(err.what(), absoluteFilename() + _("/") + file); // more detailed message
|
||||
}
|
||||
Reader reader(openIn(file), dynamic_cast<Packaged*>(this), absoluteFilename() + _("/") + file);
|
||||
try {
|
||||
reader.handle_greedy(obj);
|
||||
} catch (const ParseError& err) {
|
||||
throw FileParseError(err.what(), absoluteFilename() + _("/") + file); // more detailed message
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+371
-371
@@ -29,471 +29,471 @@ PackageManager package_manager;
|
||||
|
||||
|
||||
void PackageManager::init() {
|
||||
local.init(true);
|
||||
global.init(false);
|
||||
if (!(local.valid() || global.valid()))
|
||||
throw Error(_("The MSE data files can not be found, there should be a directory called 'data' with these files. ")
|
||||
_("The expected place to find it in was either ") + wxStandardPaths::Get().GetDataDir() + _(" or ") +
|
||||
wxStandardPaths::Get().GetUserDataDir());
|
||||
local.init(true);
|
||||
global.init(false);
|
||||
if (!(local.valid() || global.valid()))
|
||||
throw Error(_("The MSE data files can not be found, there should be a directory called 'data' with these files. ")
|
||||
_("The expected place to find it in was either ") + wxStandardPaths::Get().GetDataDir() + _(" or ") +
|
||||
wxStandardPaths::Get().GetUserDataDir());
|
||||
}
|
||||
void PackageManager::destroy() {
|
||||
loaded_packages.clear();
|
||||
loaded_packages.clear();
|
||||
}
|
||||
void PackageManager::reset() {
|
||||
loaded_packages.clear();
|
||||
loaded_packages.clear();
|
||||
}
|
||||
|
||||
PackagedP PackageManager::openAny(const String& name_, bool just_header) {
|
||||
String name = trim(name_);
|
||||
if (starts_with(name,_("/"))) name = name.substr(1);
|
||||
if (starts_with(name,_(":NO-WARN-DEP:"))) name = name.substr(13);
|
||||
// Attempt to load local data first.
|
||||
String filename;
|
||||
if (wxFileName(name).IsRelative()) {
|
||||
// local data dir?
|
||||
filename = normalize_filename(local.name(name));
|
||||
if (!wxFileExists(filename) && !wxDirExists(filename)) {
|
||||
// global data dir
|
||||
filename = normalize_filename(global.name(name));
|
||||
}
|
||||
} else { // Absolute filename
|
||||
filename = normalize_filename(name);
|
||||
}
|
||||
String name = trim(name_);
|
||||
if (starts_with(name,_("/"))) name = name.substr(1);
|
||||
if (starts_with(name,_(":NO-WARN-DEP:"))) name = name.substr(13);
|
||||
// Attempt to load local data first.
|
||||
String filename;
|
||||
if (wxFileName(name).IsRelative()) {
|
||||
// local data dir?
|
||||
filename = normalize_filename(local.name(name));
|
||||
if (!wxFileExists(filename) && !wxDirExists(filename)) {
|
||||
// global data dir
|
||||
filename = normalize_filename(global.name(name));
|
||||
}
|
||||
} else { // Absolute filename
|
||||
filename = normalize_filename(name);
|
||||
}
|
||||
|
||||
// Is this package already loaded?
|
||||
PackagedP& p = loaded_packages[filename];
|
||||
if (!p) {
|
||||
// load with the right type, based on extension
|
||||
wxFileName fn(filename);
|
||||
if (fn.GetExt() == _("mse-game")) p = intrusive(new Game);
|
||||
else if (fn.GetExt() == _("mse-style")) p = intrusive(new StyleSheet);
|
||||
else if (fn.GetExt() == _("mse-locale")) p = intrusive(new Locale);
|
||||
else if (fn.GetExt() == _("mse-include")) p = intrusive(new IncludePackage);
|
||||
else if (fn.GetExt() == _("mse-symbol-font")) p = intrusive(new SymbolFont);
|
||||
else if (fn.GetExt() == _("mse-export-template")) p = intrusive(new ExportTemplate);
|
||||
else {
|
||||
throw PackageError(_("Unrecognized package type: '") + fn.GetExt() + _("'\nwhile trying to open: ") + name);
|
||||
}
|
||||
p->open(filename, just_header);
|
||||
} else if (!just_header) {
|
||||
p->loadFully();
|
||||
}
|
||||
return p;
|
||||
// Is this package already loaded?
|
||||
PackagedP& p = loaded_packages[filename];
|
||||
if (!p) {
|
||||
// load with the right type, based on extension
|
||||
wxFileName fn(filename);
|
||||
if (fn.GetExt() == _("mse-game")) p = intrusive(new Game);
|
||||
else if (fn.GetExt() == _("mse-style")) p = intrusive(new StyleSheet);
|
||||
else if (fn.GetExt() == _("mse-locale")) p = intrusive(new Locale);
|
||||
else if (fn.GetExt() == _("mse-include")) p = intrusive(new IncludePackage);
|
||||
else if (fn.GetExt() == _("mse-symbol-font")) p = intrusive(new SymbolFont);
|
||||
else if (fn.GetExt() == _("mse-export-template")) p = intrusive(new ExportTemplate);
|
||||
else {
|
||||
throw PackageError(_("Unrecognized package type: '") + fn.GetExt() + _("'\nwhile trying to open: ") + name);
|
||||
}
|
||||
p->open(filename, just_header);
|
||||
} else if (!just_header) {
|
||||
p->loadFully();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void PackageManager::findMatching(const String& pattern, vector<PackagedP>& out) {
|
||||
// first find local packages
|
||||
String file = local.findFirstMatching(pattern);
|
||||
while (!file.empty()) {
|
||||
out.push_back(openAny(file, true));
|
||||
file = wxFindNextFile();
|
||||
}
|
||||
// then global packages not already in the list
|
||||
file = global.findFirstMatching(pattern);
|
||||
while (!file.empty()) {
|
||||
PackagedP p = openAny(file, true);
|
||||
if (find(out.begin(), out.end(), p) == out.end()) {
|
||||
out.push_back(p);
|
||||
}
|
||||
file = wxFindNextFile();
|
||||
}
|
||||
// first find local packages
|
||||
String file = local.findFirstMatching(pattern);
|
||||
while (!file.empty()) {
|
||||
out.push_back(openAny(file, true));
|
||||
file = wxFindNextFile();
|
||||
}
|
||||
// then global packages not already in the list
|
||||
file = global.findFirstMatching(pattern);
|
||||
while (!file.empty()) {
|
||||
PackagedP p = openAny(file, true);
|
||||
if (find(out.begin(), out.end(), p) == out.end()) {
|
||||
out.push_back(p);
|
||||
}
|
||||
file = wxFindNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
InputStreamP PackageManager::openFileFromPackage(Packaged*& package, const String& name) {
|
||||
if (!name.empty() && name.GetChar(0) == _('/')) {
|
||||
// absolute name; break name
|
||||
size_t start = name.find_first_not_of(_("/\\"), 1); // allow "//package/name" from incorrect scripts
|
||||
size_t pos = name.find_first_of(_("/\\"), start);
|
||||
if (start < pos && pos != String::npos) {
|
||||
// open package
|
||||
PackagedP p = openAny(name.substr(start, pos-start));
|
||||
if (package && !is_substr(name,start,_(":NO-WARN-DEP:"))) {
|
||||
package->requireDependency(p.get());
|
||||
}
|
||||
package = p.get();
|
||||
return p->openIn(name.substr(pos + 1));
|
||||
}
|
||||
} else if (package) {
|
||||
// relative name
|
||||
return package->openIn(name);
|
||||
}
|
||||
throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
|
||||
if (!name.empty() && name.GetChar(0) == _('/')) {
|
||||
// absolute name; break name
|
||||
size_t start = name.find_first_not_of(_("/\\"), 1); // allow "//package/name" from incorrect scripts
|
||||
size_t pos = name.find_first_of(_("/\\"), start);
|
||||
if (start < pos && pos != String::npos) {
|
||||
// open package
|
||||
PackagedP p = openAny(name.substr(start, pos-start));
|
||||
if (package && !is_substr(name,start,_(":NO-WARN-DEP:"))) {
|
||||
package->requireDependency(p.get());
|
||||
}
|
||||
package = p.get();
|
||||
return p->openIn(name.substr(pos + 1));
|
||||
}
|
||||
} else if (package) {
|
||||
// relative name
|
||||
return package->openIn(name);
|
||||
}
|
||||
throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
|
||||
}
|
||||
|
||||
String PackageManager::openFilenameFromPackage(Packaged*& package, const String& name) {
|
||||
if (!name.empty() && name.GetChar(0) == _('/')) {
|
||||
// absolute name; break name
|
||||
size_t start = name.find_first_not_of(_("/\\"), 1); // allow "//package/name" from incorrect scripts
|
||||
size_t pos = name.find_first_of(_("/\\"), start);
|
||||
if (start < pos && pos != String::npos) {
|
||||
// open package
|
||||
PackagedP p = openAny(name.substr(start, pos-start));
|
||||
if (package && !is_substr(name,start,_(":NO-WARN-DEP:"))) {
|
||||
package->requireDependency(p.get());
|
||||
}
|
||||
package = p.get();
|
||||
return p->absoluteFilename() + _("/") + name.substr(pos + 1);
|
||||
}
|
||||
} else if (package) {
|
||||
// relative name
|
||||
return package->absoluteFilename() + _("/") + name;
|
||||
}
|
||||
throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
|
||||
if (!name.empty() && name.GetChar(0) == _('/')) {
|
||||
// absolute name; break name
|
||||
size_t start = name.find_first_not_of(_("/\\"), 1); // allow "//package/name" from incorrect scripts
|
||||
size_t pos = name.find_first_of(_("/\\"), start);
|
||||
if (start < pos && pos != String::npos) {
|
||||
// open package
|
||||
PackagedP p = openAny(name.substr(start, pos-start));
|
||||
if (package && !is_substr(name,start,_(":NO-WARN-DEP:"))) {
|
||||
package->requireDependency(p.get());
|
||||
}
|
||||
package = p.get();
|
||||
return p->absoluteFilename() + _("/") + name.substr(pos + 1);
|
||||
}
|
||||
} else if (package) {
|
||||
// relative name
|
||||
return package->absoluteFilename() + _("/") + name;
|
||||
}
|
||||
throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
|
||||
}
|
||||
|
||||
String PackageManager::getDictionaryDir(bool l) const {
|
||||
String dir = (l ? local : global).getDirectory();
|
||||
if (dir.empty()) return wxEmptyString;
|
||||
else return dir + _("/dictionaries/");
|
||||
String dir = (l ? local : global).getDirectory();
|
||||
if (dir.empty()) return wxEmptyString;
|
||||
else return dir + _("/dictionaries/");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : PackageManager : on disk
|
||||
|
||||
bool PackageManager::checkDependency(const PackageDependency& dep, bool report_errors) {
|
||||
// mse package?
|
||||
if (dep.package == mse_package) {
|
||||
if (app_version < dep.version) {
|
||||
queue_message(MESSAGE_WARNING, _ERROR_3_("package out of date", _("Magic Set Editor"), app_version.toString(), dep.version.toString()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// does the package exist?
|
||||
if (!local.exists(dep.package) && !global.exists(dep.package)) {
|
||||
if (report_errors)
|
||||
queue_message(MESSAGE_WARNING, _ERROR_1_("package not found", dep.package));
|
||||
return false;
|
||||
}
|
||||
PackagedP package = openAny(dep.package, true);
|
||||
if (package->version < dep.version) {
|
||||
if (report_errors)
|
||||
queue_message(MESSAGE_WARNING, _ERROR_3_("package out of date", dep.package, package->version.toString(), dep.version.toString()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
// mse package?
|
||||
if (dep.package == mse_package) {
|
||||
if (app_version < dep.version) {
|
||||
queue_message(MESSAGE_WARNING, _ERROR_3_("package out of date", _("Magic Set Editor"), app_version.toString(), dep.version.toString()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// does the package exist?
|
||||
if (!local.exists(dep.package) && !global.exists(dep.package)) {
|
||||
if (report_errors)
|
||||
queue_message(MESSAGE_WARNING, _ERROR_1_("package not found", dep.package));
|
||||
return false;
|
||||
}
|
||||
PackagedP package = openAny(dep.package, true);
|
||||
if (package->version < dep.version) {
|
||||
if (report_errors)
|
||||
queue_message(MESSAGE_WARNING, _ERROR_3_("package out of date", dep.package, package->version.toString(), dep.version.toString()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool PackageManager::installedVersion(const String& package_name, Version& version_out) {
|
||||
if (package_name == mse_package) {
|
||||
version_out = app_version;
|
||||
return true;
|
||||
} else {
|
||||
if (!local.exists(package_name) && !global.exists(package_name)) return false;
|
||||
PackagedP package = openAny(package_name, true);
|
||||
version_out = package->version;
|
||||
return true;
|
||||
}
|
||||
if (package_name == mse_package) {
|
||||
version_out = app_version;
|
||||
return true;
|
||||
} else {
|
||||
if (!local.exists(package_name) && !global.exists(package_name)) return false;
|
||||
PackagedP package = openAny(package_name, true);
|
||||
version_out = package->version;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void PackageManager::findAllInstalledPackages(vector<InstallablePackageP>& packages) {
|
||||
// from directories
|
||||
vector<InstallablePackageP> more_packages;
|
||||
global.installedPackages(packages);
|
||||
local.installedPackages(more_packages);
|
||||
merge(packages, more_packages);
|
||||
// the magic appliation package
|
||||
packages.push_back(mse_installable_package());
|
||||
// invariant: sorted:
|
||||
sort(packages);
|
||||
// from directories
|
||||
vector<InstallablePackageP> more_packages;
|
||||
global.installedPackages(packages);
|
||||
local.installedPackages(more_packages);
|
||||
merge(packages, more_packages);
|
||||
// the magic appliation package
|
||||
packages.push_back(mse_installable_package());
|
||||
// invariant: sorted:
|
||||
sort(packages);
|
||||
}
|
||||
|
||||
bool PackageManager::install(const InstallablePackage& package) {
|
||||
bool install_local = package.has(PACKAGE_ACT_LOCAL);
|
||||
return (install_local ? local : global).install(package);
|
||||
bool install_local = package.has(PACKAGE_ACT_LOCAL);
|
||||
return (install_local ? local : global).install(package);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : PackageDirectory
|
||||
|
||||
void PackageDirectory::init(bool local) {
|
||||
is_local = local;
|
||||
if (local) {
|
||||
init(wxStandardPaths::Get().GetUserDataDir() + _("/data"));
|
||||
} else {
|
||||
// determine data directory
|
||||
String dir = wxStandardPaths::Get().GetDataDir();
|
||||
// check if this is the actual data directory, especially during debugging,
|
||||
// the data may be higher up:
|
||||
// exe path = mse/build/debug/mse.exe
|
||||
// data path = mse/data
|
||||
while (!wxDirExists(dir + _("/data"))) {
|
||||
String d = dir;
|
||||
dir = wxPathOnly(dir);
|
||||
if (d == dir) {
|
||||
// we are at the root -> 'data' not found anywhere in the path
|
||||
dir = wxStandardPaths::Get().GetDataDir();
|
||||
break;
|
||||
}
|
||||
}
|
||||
init(dir + _("/data"));
|
||||
}
|
||||
is_local = local;
|
||||
if (local) {
|
||||
init(wxStandardPaths::Get().GetUserDataDir() + _("/data"));
|
||||
} else {
|
||||
// determine data directory
|
||||
String dir = wxStandardPaths::Get().GetDataDir();
|
||||
// check if this is the actual data directory, especially during debugging,
|
||||
// the data may be higher up:
|
||||
// exe path = mse/build/debug/mse.exe
|
||||
// data path = mse/data
|
||||
while (!wxDirExists(dir + _("/data"))) {
|
||||
String d = dir;
|
||||
dir = wxPathOnly(dir);
|
||||
if (d == dir) {
|
||||
// we are at the root -> 'data' not found anywhere in the path
|
||||
dir = wxStandardPaths::Get().GetDataDir();
|
||||
break;
|
||||
}
|
||||
}
|
||||
init(dir + _("/data"));
|
||||
}
|
||||
}
|
||||
void PackageDirectory::init(const String& dir) {
|
||||
if (wxDirExists(dir))
|
||||
directory = dir;
|
||||
else
|
||||
directory.clear();
|
||||
if (wxDirExists(dir))
|
||||
directory = dir;
|
||||
else
|
||||
directory.clear();
|
||||
}
|
||||
|
||||
String PackageDirectory::name(const String& name) const {
|
||||
return directory + _("/") + name;
|
||||
return directory + _("/") + name;
|
||||
}
|
||||
bool PackageDirectory::exists(const String& filename) const {
|
||||
String fn = name(filename);
|
||||
return wxFileExists(fn) || wxDirExists(fn);
|
||||
String fn = name(filename);
|
||||
return wxFileExists(fn) || wxDirExists(fn);
|
||||
}
|
||||
|
||||
String PackageDirectory::findFirstMatching(const String& pattern) const {
|
||||
if (!wxDirExists(directory)) return String();
|
||||
return wxFindFirstFile(directory + _("/") + pattern, 0);
|
||||
if (!wxDirExists(directory)) return String();
|
||||
return wxFindFirstFile(directory + _("/") + pattern, 0);
|
||||
}
|
||||
|
||||
bool compare_name(const PackageVersionP& a, const PackageVersionP& b) {
|
||||
return a->name < b->name;
|
||||
return a->name < b->name;
|
||||
}
|
||||
|
||||
void PackageDirectory::installedPackages(vector<InstallablePackageP>& packages_out) {
|
||||
loadDatabase();
|
||||
// find all package files
|
||||
vector<String> in_dir;
|
||||
for (String s = findFirstMatching(_("*.mse-*")) ; !s.empty() ; s = wxFindNextFile()) {
|
||||
size_t pos = s.find_last_of(_("/\\"));
|
||||
if (pos != String::npos) s = s.substr(pos+1);
|
||||
// TODO : check for valid package names
|
||||
in_dir.push_back(s);
|
||||
}
|
||||
sort(in_dir.begin(), in_dir.end());
|
||||
// merge with package database
|
||||
bool db_changed = false;
|
||||
vector<PackageVersionP>::const_iterator it1 = packages.begin();
|
||||
vector<String>::const_iterator it2 = in_dir.begin();
|
||||
while (it2 != in_dir.end()) {
|
||||
if (it1 == packages.end() || (*it1)->name > *it2) {
|
||||
// add new package to db
|
||||
try {
|
||||
PackagedP pack = package_manager.openAny(*it2, true);
|
||||
db_changed = true;
|
||||
PackageVersionP ver(new PackageVersion(
|
||||
is_local ? PackageVersion::STATUS_LOCAL : PackageVersion::STATUS_GLOBAL));
|
||||
ver->check_status(*pack);
|
||||
packages_out.push_back(intrusive(new InstallablePackage(intrusive(new PackageDescription(*pack)), ver)));
|
||||
} catch (const Error&) {}
|
||||
++it2;
|
||||
} else if ((*it1)->name < *it2) {
|
||||
// delete package from db
|
||||
db_changed = true;
|
||||
++it1;
|
||||
} else {
|
||||
// ok, a package already in the db
|
||||
try {
|
||||
PackagedP pack = package_manager.openAny(*it2, true);
|
||||
(*it1)->check_status(*pack);
|
||||
packages_out.push_back(intrusive(new InstallablePackage(intrusive(new PackageDescription(*pack)), *it1)));
|
||||
} catch (const Error&) { db_changed = true; }
|
||||
++it1, ++it2;
|
||||
}
|
||||
}
|
||||
if (it1 != packages.end()) db_changed = true;
|
||||
// has the database of installed packages changed?
|
||||
if (db_changed) {
|
||||
packages.clear();
|
||||
FOR_EACH(p, packages_out) {
|
||||
if (p->installed) packages.push_back(p->installed);
|
||||
}
|
||||
saveDatabase();
|
||||
}
|
||||
loadDatabase();
|
||||
// find all package files
|
||||
vector<String> in_dir;
|
||||
for (String s = findFirstMatching(_("*.mse-*")) ; !s.empty() ; s = wxFindNextFile()) {
|
||||
size_t pos = s.find_last_of(_("/\\"));
|
||||
if (pos != String::npos) s = s.substr(pos+1);
|
||||
// TODO : check for valid package names
|
||||
in_dir.push_back(s);
|
||||
}
|
||||
sort(in_dir.begin(), in_dir.end());
|
||||
// merge with package database
|
||||
bool db_changed = false;
|
||||
vector<PackageVersionP>::const_iterator it1 = packages.begin();
|
||||
vector<String>::const_iterator it2 = in_dir.begin();
|
||||
while (it2 != in_dir.end()) {
|
||||
if (it1 == packages.end() || (*it1)->name > *it2) {
|
||||
// add new package to db
|
||||
try {
|
||||
PackagedP pack = package_manager.openAny(*it2, true);
|
||||
db_changed = true;
|
||||
PackageVersionP ver(new PackageVersion(
|
||||
is_local ? PackageVersion::STATUS_LOCAL : PackageVersion::STATUS_GLOBAL));
|
||||
ver->check_status(*pack);
|
||||
packages_out.push_back(intrusive(new InstallablePackage(intrusive(new PackageDescription(*pack)), ver)));
|
||||
} catch (const Error&) {}
|
||||
++it2;
|
||||
} else if ((*it1)->name < *it2) {
|
||||
// delete package from db
|
||||
db_changed = true;
|
||||
++it1;
|
||||
} else {
|
||||
// ok, a package already in the db
|
||||
try {
|
||||
PackagedP pack = package_manager.openAny(*it2, true);
|
||||
(*it1)->check_status(*pack);
|
||||
packages_out.push_back(intrusive(new InstallablePackage(intrusive(new PackageDescription(*pack)), *it1)));
|
||||
} catch (const Error&) { db_changed = true; }
|
||||
++it1, ++it2;
|
||||
}
|
||||
}
|
||||
if (it1 != packages.end()) db_changed = true;
|
||||
// has the database of installed packages changed?
|
||||
if (db_changed) {
|
||||
packages.clear();
|
||||
FOR_EACH(p, packages_out) {
|
||||
if (p->installed) packages.push_back(p->installed);
|
||||
}
|
||||
saveDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
void PackageDirectory::bless(const String& package_name) {
|
||||
PackagedP pack = package_manager.openAny(package_name, true);
|
||||
// already have this package?
|
||||
FOR_EACH(ver, packages) {
|
||||
if (ver->name == package_name) {
|
||||
ver->check_status(*pack);
|
||||
ver->bless();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// a new package
|
||||
PackageVersionP ver(new PackageVersion(
|
||||
is_local ? PackageVersion::STATUS_LOCAL : PackageVersion::STATUS_GLOBAL));
|
||||
ver->check_status(*pack);
|
||||
ver->bless();
|
||||
packages.push_back(ver);
|
||||
sort(packages.begin(), packages.end(), compare_name);
|
||||
PackagedP pack = package_manager.openAny(package_name, true);
|
||||
// already have this package?
|
||||
FOR_EACH(ver, packages) {
|
||||
if (ver->name == package_name) {
|
||||
ver->check_status(*pack);
|
||||
ver->bless();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// a new package
|
||||
PackageVersionP ver(new PackageVersion(
|
||||
is_local ? PackageVersion::STATUS_LOCAL : PackageVersion::STATUS_GLOBAL));
|
||||
ver->check_status(*pack);
|
||||
ver->bless();
|
||||
packages.push_back(ver);
|
||||
sort(packages.begin(), packages.end(), compare_name);
|
||||
}
|
||||
|
||||
void PackageDirectory::removeFromDatabase(const String& package_name) {
|
||||
size_t i = 0, j = 0;
|
||||
for ( ; i < packages.size() ; ++i) {
|
||||
if (packages[i]->name != package_name) {
|
||||
packages[j++] = packages[i];
|
||||
}
|
||||
}
|
||||
packages.resize(j);
|
||||
size_t i = 0, j = 0;
|
||||
for ( ; i < packages.size() ; ++i) {
|
||||
if (packages[i]->name != package_name) {
|
||||
packages[j++] = packages[i];
|
||||
}
|
||||
}
|
||||
packages.resize(j);
|
||||
}
|
||||
|
||||
IMPLEMENT_REFLECTION(PackageDirectory) {
|
||||
REFLECT(packages);
|
||||
REFLECT(packages);
|
||||
}
|
||||
|
||||
void PackageDirectory::loadDatabase() {
|
||||
if (!packages.empty()) return;
|
||||
String filename = databaseFile();
|
||||
if (wxFileExists(filename)) {
|
||||
// packages file not existing is not an error
|
||||
shared_ptr<wxFileInputStream> file = shared(new wxFileInputStream(filename));
|
||||
if (!file->Ok()) return; // failure is not an error
|
||||
Reader reader(file, nullptr, filename);
|
||||
reader.handle_greedy(*this);
|
||||
sort(packages.begin(), packages.end(), compare_name);
|
||||
}
|
||||
if (!packages.empty()) return;
|
||||
String filename = databaseFile();
|
||||
if (wxFileExists(filename)) {
|
||||
// packages file not existing is not an error
|
||||
shared_ptr<wxFileInputStream> file = shared(new wxFileInputStream(filename));
|
||||
if (!file->Ok()) return; // failure is not an error
|
||||
Reader reader(file, nullptr, filename);
|
||||
reader.handle_greedy(*this);
|
||||
sort(packages.begin(), packages.end(), compare_name);
|
||||
}
|
||||
}
|
||||
|
||||
void PackageDirectory::saveDatabase() {
|
||||
Writer writer(shared(new wxFileOutputStream(databaseFile())), app_version);
|
||||
writer.handle(*this);
|
||||
Writer writer(shared(new wxFileOutputStream(databaseFile())), app_version);
|
||||
writer.handle(*this);
|
||||
}
|
||||
String PackageDirectory::databaseFile() {
|
||||
return name(_("packages"));
|
||||
return name(_("packages"));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : PackageDirectory : installing
|
||||
|
||||
bool PackageDirectory::install(const InstallablePackage& package) {
|
||||
String n = name(package.description->name);
|
||||
if (package.action & PACKAGE_ACT_REMOVE) {
|
||||
if (!remove_file_or_dir(n)) return false;
|
||||
removeFromDatabase(package.description->name);
|
||||
} else if (package.action & PACKAGE_ACT_INSTALL) {
|
||||
if (!remove_file_or_dir(n + _(".new"))) return false;
|
||||
bool ok = actual_install(package, n + _(".new"));
|
||||
if (!ok) return false;
|
||||
move_ignored_files(n, n + _(".new")); // copy over files from the old installed version to the new one
|
||||
if (!remove_file_or_dir(n)) return false;
|
||||
if (!rename_file_or_dir(n + _(".new"), n)) return false;
|
||||
bless(package.description->name);
|
||||
}
|
||||
saveDatabase();
|
||||
return true;
|
||||
String n = name(package.description->name);
|
||||
if (package.action & PACKAGE_ACT_REMOVE) {
|
||||
if (!remove_file_or_dir(n)) return false;
|
||||
removeFromDatabase(package.description->name);
|
||||
} else if (package.action & PACKAGE_ACT_INSTALL) {
|
||||
if (!remove_file_or_dir(n + _(".new"))) return false;
|
||||
bool ok = actual_install(package, n + _(".new"));
|
||||
if (!ok) return false;
|
||||
move_ignored_files(n, n + _(".new")); // copy over files from the old installed version to the new one
|
||||
if (!remove_file_or_dir(n)) return false;
|
||||
if (!rename_file_or_dir(n + _(".new"), n)) return false;
|
||||
bless(package.description->name);
|
||||
}
|
||||
saveDatabase();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PackageDirectory::actual_install(const InstallablePackage& package, const String& install_dir) {
|
||||
String name = package.description->name;
|
||||
if (!package.installer->installer) {
|
||||
queue_message(MESSAGE_ERROR, _("Installer not found for package: ") + name);
|
||||
return false;
|
||||
}
|
||||
Installer& installer = *package.installer->installer;
|
||||
// install files
|
||||
const Packaged::FileInfos& file_infos = installer.getFileInfos();
|
||||
for (Packaged::FileInfos::const_iterator it = file_infos.begin() ; it != file_infos.end() ; ++it) {
|
||||
String file = it->first;
|
||||
if (!is_substr_i(file,0,name)) continue; // not the right package
|
||||
// correct filename
|
||||
String local_file = install_dir + file.substr(name.length());
|
||||
create_parent_dirs(local_file);
|
||||
// copy file
|
||||
InputStreamP is = installer.openIn(file);
|
||||
wxFileOutputStream os (local_file);
|
||||
if (!os.IsOk()) {
|
||||
int act = wxMessageBox(_ERROR_1_("cannot create file", file), _TITLE_("cannot create file"), wxICON_ERROR | wxYES_NO);
|
||||
if (act == wxNO) return false;
|
||||
}
|
||||
os.Write(*is);
|
||||
}
|
||||
// update package database
|
||||
// TODO: bless the package?
|
||||
return true;
|
||||
String name = package.description->name;
|
||||
if (!package.installer->installer) {
|
||||
queue_message(MESSAGE_ERROR, _("Installer not found for package: ") + name);
|
||||
return false;
|
||||
}
|
||||
Installer& installer = *package.installer->installer;
|
||||
// install files
|
||||
const Packaged::FileInfos& file_infos = installer.getFileInfos();
|
||||
for (Packaged::FileInfos::const_iterator it = file_infos.begin() ; it != file_infos.end() ; ++it) {
|
||||
String file = it->first;
|
||||
if (!is_substr_i(file,0,name)) continue; // not the right package
|
||||
// correct filename
|
||||
String local_file = install_dir + file.substr(name.length());
|
||||
create_parent_dirs(local_file);
|
||||
// copy file
|
||||
InputStreamP is = installer.openIn(file);
|
||||
wxFileOutputStream os (local_file);
|
||||
if (!os.IsOk()) {
|
||||
int act = wxMessageBox(_ERROR_1_("cannot create file", file), _TITLE_("cannot create file"), wxICON_ERROR | wxYES_NO);
|
||||
if (act == wxNO) return false;
|
||||
}
|
||||
os.Write(*is);
|
||||
}
|
||||
// update package database
|
||||
// TODO: bless the package?
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : PackageVersion
|
||||
|
||||
template <> void Writer::handle(const PackageVersion::FileInfo& f) {
|
||||
if (f.status == PackageVersion::FILE_DELETED) {
|
||||
handle(_("D ") + f.file);
|
||||
} else {
|
||||
handle(format_string(_("%s%s %s"),
|
||||
f.status == PackageVersion::FILE_ADDED ? _("A")
|
||||
: f.status == PackageVersion::FILE_MODIFIED ? _("M") : _(""),
|
||||
f.time.Format(_("%Y%m%dT%H%M%S")),
|
||||
f.file));
|
||||
}
|
||||
if (f.status == PackageVersion::FILE_DELETED) {
|
||||
handle(_("D ") + f.file);
|
||||
} else {
|
||||
handle(format_string(_("%s%s %s"),
|
||||
f.status == PackageVersion::FILE_ADDED ? _("A")
|
||||
: f.status == PackageVersion::FILE_MODIFIED ? _("M") : _(""),
|
||||
f.time.Format(_("%Y%m%dT%H%M%S")),
|
||||
f.file));
|
||||
}
|
||||
}
|
||||
template <> void Reader::handle(PackageVersion::FileInfo& f) {
|
||||
String s; handle(s);
|
||||
// read status
|
||||
if (s.size() < 2) {f.status = PackageVersion::FILE_ADDED; return; }
|
||||
f.status = s.GetChar(0) == _('M') ? PackageVersion::FILE_MODIFIED
|
||||
: s.GetChar(0) == _('A') ? PackageVersion::FILE_ADDED
|
||||
: s.GetChar(0) == _('D') ? PackageVersion::FILE_DELETED
|
||||
: PackageVersion::FILE_UNCHANGED;
|
||||
if (f.status == PackageVersion::FILE_DELETED) {
|
||||
if (s.GetChar(1) != _(' ')) {f.status = PackageVersion::FILE_ADDED; return; }
|
||||
f.file = s.substr(2);
|
||||
return;
|
||||
} else if (f.status != PackageVersion::FILE_UNCHANGED) {
|
||||
s = s.substr(1);
|
||||
}
|
||||
if (s.size() < 8+1+6+1) {f.status = PackageVersion::FILE_ADDED; return; }
|
||||
if (s.GetChar(8+1+6) != _(' ')) {f.status = PackageVersion::FILE_ADDED; return; } // invalid format
|
||||
// read time, filename
|
||||
f.time.ParseFormat(s, _("%Y%m%dT%H%M%S"));
|
||||
f.file = s.substr(8+1+6+1);
|
||||
String s; handle(s);
|
||||
// read status
|
||||
if (s.size() < 2) {f.status = PackageVersion::FILE_ADDED; return; }
|
||||
f.status = s.GetChar(0) == _('M') ? PackageVersion::FILE_MODIFIED
|
||||
: s.GetChar(0) == _('A') ? PackageVersion::FILE_ADDED
|
||||
: s.GetChar(0) == _('D') ? PackageVersion::FILE_DELETED
|
||||
: PackageVersion::FILE_UNCHANGED;
|
||||
if (f.status == PackageVersion::FILE_DELETED) {
|
||||
if (s.GetChar(1) != _(' ')) {f.status = PackageVersion::FILE_ADDED; return; }
|
||||
f.file = s.substr(2);
|
||||
return;
|
||||
} else if (f.status != PackageVersion::FILE_UNCHANGED) {
|
||||
s = s.substr(1);
|
||||
}
|
||||
if (s.size() < 8+1+6+1) {f.status = PackageVersion::FILE_ADDED; return; }
|
||||
if (s.GetChar(8+1+6) != _(' ')) {f.status = PackageVersion::FILE_ADDED; return; } // invalid format
|
||||
// read time, filename
|
||||
f.time.ParseFormat(s, _("%Y%m%dT%H%M%S"));
|
||||
f.file = s.substr(8+1+6+1);
|
||||
}
|
||||
IMPLEMENT_REFLECTION_NO_SCRIPT(PackageVersion) {
|
||||
REFLECT_NO_SCRIPT(name);
|
||||
REFLECT_NO_SCRIPT(version);
|
||||
REFLECT_NO_SCRIPT(status);
|
||||
REFLECT_NO_SCRIPT(files);
|
||||
REFLECT_NO_SCRIPT(name);
|
||||
REFLECT_NO_SCRIPT(version);
|
||||
REFLECT_NO_SCRIPT(status);
|
||||
REFLECT_NO_SCRIPT(files);
|
||||
}
|
||||
|
||||
void PackageVersion::check_status(Packaged& package) {
|
||||
status &= ~STATUS_MODIFIED;
|
||||
if (!(status & STATUS_BLESSED)) status |= STATUS_MODIFIED;
|
||||
name = package.relativeFilename();
|
||||
version = package.version;
|
||||
// Merge our files list with the list from the package
|
||||
vector<FileInfo> new_files;
|
||||
Package::FileInfos fis = package.getFileInfos();
|
||||
Package::FileInfos::const_iterator it1 = fis.begin();
|
||||
vector<FileInfo>::iterator it2 = files.begin();
|
||||
//% size_t it2 = 0, size = files.size();
|
||||
//% bool need_sort = false;
|
||||
while(it1 != fis.end() || it2 != files.end()) {
|
||||
if (it1 != fis.end() && it2 != files.end() && it1->first == it2->file) {
|
||||
DateTime mtime = package.modificationTime(*it1);
|
||||
if (mtime != it2->time) {
|
||||
it2->time = mtime;
|
||||
it2->status = FILE_MODIFIED;
|
||||
new_files.push_back(*it2);
|
||||
status |= STATUS_MODIFIED;
|
||||
}
|
||||
++it1; ++it2;
|
||||
} else if (it1 != fis.end() && (it2 == files.end() || it1->first < it2->file)) {
|
||||
// this is a new file
|
||||
DateTime mtime = package.modificationTime(*it1);
|
||||
new_files.push_back(FileInfo(it1->first, mtime, FILE_ADDED));
|
||||
status |= STATUS_MODIFIED;
|
||||
++it1;
|
||||
} else {
|
||||
// this file is no longer in the package, it was deleted
|
||||
if (it2->status != FILE_ADDED) {
|
||||
it2->status = FILE_DELETED;
|
||||
new_files.push_back(*it2);
|
||||
status |= STATUS_MODIFIED;
|
||||
}
|
||||
++it2;
|
||||
}
|
||||
}
|
||||
swap(files,new_files);
|
||||
status &= ~STATUS_MODIFIED;
|
||||
if (!(status & STATUS_BLESSED)) status |= STATUS_MODIFIED;
|
||||
name = package.relativeFilename();
|
||||
version = package.version;
|
||||
// Merge our files list with the list from the package
|
||||
vector<FileInfo> new_files;
|
||||
Package::FileInfos fis = package.getFileInfos();
|
||||
Package::FileInfos::const_iterator it1 = fis.begin();
|
||||
vector<FileInfo>::iterator it2 = files.begin();
|
||||
//% size_t it2 = 0, size = files.size();
|
||||
//% bool need_sort = false;
|
||||
while(it1 != fis.end() || it2 != files.end()) {
|
||||
if (it1 != fis.end() && it2 != files.end() && it1->first == it2->file) {
|
||||
DateTime mtime = package.modificationTime(*it1);
|
||||
if (mtime != it2->time) {
|
||||
it2->time = mtime;
|
||||
it2->status = FILE_MODIFIED;
|
||||
new_files.push_back(*it2);
|
||||
status |= STATUS_MODIFIED;
|
||||
}
|
||||
++it1; ++it2;
|
||||
} else if (it1 != fis.end() && (it2 == files.end() || it1->first < it2->file)) {
|
||||
// this is a new file
|
||||
DateTime mtime = package.modificationTime(*it1);
|
||||
new_files.push_back(FileInfo(it1->first, mtime, FILE_ADDED));
|
||||
status |= STATUS_MODIFIED;
|
||||
++it1;
|
||||
} else {
|
||||
// this file is no longer in the package, it was deleted
|
||||
if (it2->status != FILE_ADDED) {
|
||||
it2->status = FILE_DELETED;
|
||||
new_files.push_back(*it2);
|
||||
status |= STATUS_MODIFIED;
|
||||
}
|
||||
++it2;
|
||||
}
|
||||
}
|
||||
swap(files,new_files);
|
||||
}
|
||||
|
||||
inline bool is_deleted(PackageVersion::FileInfo f) {
|
||||
return f.status == PackageVersion::FILE_DELETED;
|
||||
return f.status == PackageVersion::FILE_DELETED;
|
||||
}
|
||||
void PackageVersion::bless() {
|
||||
files.erase(remove_if(files.begin(),files.end(),is_deleted),files.end());
|
||||
FOR_EACH(f,files) {
|
||||
f.status = FILE_UNCHANGED;
|
||||
}
|
||||
status &= ~STATUS_MODIFIED;
|
||||
status |= STATUS_BLESSED;
|
||||
files.erase(remove_if(files.begin(),files.end(),is_deleted),files.end());
|
||||
FOR_EACH(f,files) {
|
||||
f.status = FILE_UNCHANGED;
|
||||
}
|
||||
status &= ~STATUS_MODIFIED;
|
||||
status |= STATUS_BLESSED;
|
||||
}
|
||||
|
||||
+169
-169
@@ -25,33 +25,33 @@ class PackageDependency;
|
||||
/// Information on a package in a repository
|
||||
class PackageVersionData : public IntrusivePtrVirtualBase {
|
||||
public:
|
||||
PackageVersionData() {}
|
||||
|
||||
String name; ///< Name of the package
|
||||
String short_name; ///< Name to show on package list.
|
||||
String description; ///< html description
|
||||
bool hidden; ///< Not shown in the package list (installed automatically as a dependency)
|
||||
|
||||
PackageVersionP base_version; ///< The locally installed version (if installed)
|
||||
PackageVersionP local_version; ///< Modifications made to the locally installed version?
|
||||
PackageVersionP remote_version; ///< The version available from the server (if available)
|
||||
bool global; ///< The installed package is in the global location, not the user-local one
|
||||
bool new_global; ///<
|
||||
|
||||
bool source_local; ///< Is the source
|
||||
String source; ///< Where can the package be downloaded/found?
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
PackageVersionData() {}
|
||||
|
||||
String name; ///< Name of the package
|
||||
String short_name; ///< Name to show on package list.
|
||||
String description; ///< html description
|
||||
bool hidden; ///< Not shown in the package list (installed automatically as a dependency)
|
||||
|
||||
PackageVersionP base_version; ///< The locally installed version (if installed)
|
||||
PackageVersionP local_version; ///< Modifications made to the locally installed version?
|
||||
PackageVersionP remote_version; ///< The version available from the server (if available)
|
||||
bool global; ///< The installed package is in the global location, not the user-local one
|
||||
bool new_global; ///<
|
||||
|
||||
bool source_local; ///< Is the source
|
||||
String source; ///< Where can the package be downloaded/found?
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
class UpdateData {
|
||||
vector<PackageDependencyP> packages; ///< package/latest-version-number pairs
|
||||
String new_updates_url; ///< updates url has changed?
|
||||
vector<PackageDependencyP> packages; ///< package/latest-version-number pairs
|
||||
String new_updates_url; ///< updates url has changed?
|
||||
};
|
||||
|
||||
IMPLEMENT_REFLECTION_NO_SCRIPT(UpdateData) {
|
||||
REFLECT_NO_SCRIPT(packages);
|
||||
REFLECT_NO_SCRIPT(new_updates_url);
|
||||
REFLECT_NO_SCRIPT(packages);
|
||||
REFLECT_NO_SCRIPT(new_updates_url);
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -60,45 +60,45 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(UpdateData) {
|
||||
/// A directory for packages
|
||||
class PackageDirectory {
|
||||
public:
|
||||
void init(bool local);
|
||||
void init(const String& dir);
|
||||
|
||||
bool valid() const { return !directory.empty(); }
|
||||
|
||||
/// Name of a package in this directory
|
||||
String name(const String& name) const;
|
||||
/// Does a package with the given name exist?
|
||||
bool exists(const String& name) const;
|
||||
|
||||
/// Get the package directory
|
||||
inline String getDirectory() const { return directory; }
|
||||
|
||||
/// Find all packages that match a filename pattern (using wxFindFirst)
|
||||
String findFirstMatching(const String& pattern) const;
|
||||
|
||||
/// Get all installed packages
|
||||
void installedPackages(vector<InstallablePackageP>& packages);
|
||||
|
||||
/// Install/uninstall a package
|
||||
bool install(const InstallablePackage& package);
|
||||
|
||||
/// Bless a package
|
||||
void bless(const String& package_name);
|
||||
/// Remove a package from the database
|
||||
void removeFromDatabase(const String& package_name);
|
||||
|
||||
void loadDatabase();
|
||||
void saveDatabase();
|
||||
void init(bool local);
|
||||
void init(const String& dir);
|
||||
|
||||
bool valid() const { return !directory.empty(); }
|
||||
|
||||
/// Name of a package in this directory
|
||||
String name(const String& name) const;
|
||||
/// Does a package with the given name exist?
|
||||
bool exists(const String& name) const;
|
||||
|
||||
/// Get the package directory
|
||||
inline String getDirectory() const { return directory; }
|
||||
|
||||
/// Find all packages that match a filename pattern (using wxFindFirst)
|
||||
String findFirstMatching(const String& pattern) const;
|
||||
|
||||
/// Get all installed packages
|
||||
void installedPackages(vector<InstallablePackageP>& packages);
|
||||
|
||||
/// Install/uninstall a package
|
||||
bool install(const InstallablePackage& package);
|
||||
|
||||
/// Bless a package
|
||||
void bless(const String& package_name);
|
||||
/// Remove a package from the database
|
||||
void removeFromDatabase(const String& package_name);
|
||||
|
||||
void loadDatabase();
|
||||
void saveDatabase();
|
||||
private:
|
||||
bool is_local;
|
||||
String directory;
|
||||
vector<PackageVersionP> packages; // sorted by name
|
||||
|
||||
String databaseFile();
|
||||
// Do the actual installation of a package
|
||||
bool actual_install(const InstallablePackage& package, const String& install_dir);
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
bool is_local;
|
||||
String directory;
|
||||
vector<PackageVersionP> packages; // sorted by name
|
||||
|
||||
String databaseFile();
|
||||
// Do the actual installation of a package
|
||||
bool actual_install(const InstallablePackage& package, const String& install_dir);
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : PackageManager
|
||||
@@ -109,77 +109,77 @@ class PackageDirectory {
|
||||
*/
|
||||
class PackageManager {
|
||||
public:
|
||||
/// Initialize the package manager
|
||||
void init();
|
||||
/// Empty the list of packages.
|
||||
/** This function MUST be called before the program terminates, otherwise
|
||||
* we could get into fights with pool allocators used by ScriptValues */
|
||||
void destroy();
|
||||
/// Empty the list of packages, they will all be reloaded
|
||||
void reset();
|
||||
|
||||
// --------------------------------------------------- : Packages in memory
|
||||
|
||||
/// Open a package with the specified name (including extension)
|
||||
template <typename T>
|
||||
intrusive_ptr<T> open(const String& name) {
|
||||
PackagedP p = openAny(name);
|
||||
intrusive_ptr<T> typedP = dynamic_pointer_cast<T>(p);
|
||||
if (typedP) {
|
||||
return typedP;
|
||||
} else {
|
||||
throw InternalError(format_string(_("Package %s loaded as wrong type"),name));
|
||||
}
|
||||
}
|
||||
|
||||
/// Open a package with the specified name, the type of package is determined by its extension!
|
||||
/** @param if just_header is true, then the package is not fully parsed.
|
||||
*/
|
||||
PackagedP openAny(const String& name, bool just_header = false);
|
||||
|
||||
/// Find all packages that match a filename pattern, store them in out
|
||||
/** Only reads the package headers */
|
||||
void findMatching(const String& pattern, vector<PackagedP>& out);
|
||||
|
||||
/// Open a file from a package, with a name encoded as "/package/file"
|
||||
/** If 'package' is set then:
|
||||
* - tries to open a relative file from the package if the name is "file"
|
||||
* - verifies a dependency from that package if an absolute filename is used
|
||||
* this is to force people to fill in the dependencies
|
||||
* Afterwards, package will be set to the package the file is opened from
|
||||
*/
|
||||
InputStreamP openFileFromPackage(Packaged*& package, const String& name);
|
||||
|
||||
/// Get a filename to open from a package
|
||||
/** WARNING: this is a bit of a hack, since not all package types support names in this way.
|
||||
* It is needed for third party libraries (i.e. hunspell) that load stuff from files.
|
||||
*/
|
||||
String openFilenameFromPackage(Packaged*& package, const String& name);
|
||||
|
||||
// --------------------------------------------------- : Packages on disk
|
||||
|
||||
/// Check if the given dependency is currently installed
|
||||
bool checkDependency(const PackageDependency& dep, bool report_errors = true);
|
||||
/// Determine the latest version of the given package
|
||||
/** If it is not installed, returns false */
|
||||
bool installedVersion(const String& pkg, Version& version_out);
|
||||
|
||||
/// Get all installed packages
|
||||
void findAllInstalledPackages(vector<InstallablePackageP>& packages);
|
||||
|
||||
/// Install/uninstall a package, returns success
|
||||
bool install(const InstallablePackage& package);
|
||||
|
||||
// --------------------------------------------------- : Other package like things
|
||||
|
||||
/// Get the directory for dictionary files
|
||||
String getDictionaryDir(bool local) const;
|
||||
|
||||
// --------------------------------------------------- : Packages on a server
|
||||
|
||||
/// Initialize the package manager
|
||||
void init();
|
||||
/// Empty the list of packages.
|
||||
/** This function MUST be called before the program terminates, otherwise
|
||||
* we could get into fights with pool allocators used by ScriptValues */
|
||||
void destroy();
|
||||
/// Empty the list of packages, they will all be reloaded
|
||||
void reset();
|
||||
|
||||
// --------------------------------------------------- : Packages in memory
|
||||
|
||||
/// Open a package with the specified name (including extension)
|
||||
template <typename T>
|
||||
intrusive_ptr<T> open(const String& name) {
|
||||
PackagedP p = openAny(name);
|
||||
intrusive_ptr<T> typedP = dynamic_pointer_cast<T>(p);
|
||||
if (typedP) {
|
||||
return typedP;
|
||||
} else {
|
||||
throw InternalError(format_string(_("Package %s loaded as wrong type"),name));
|
||||
}
|
||||
}
|
||||
|
||||
/// Open a package with the specified name, the type of package is determined by its extension!
|
||||
/** @param if just_header is true, then the package is not fully parsed.
|
||||
*/
|
||||
PackagedP openAny(const String& name, bool just_header = false);
|
||||
|
||||
/// Find all packages that match a filename pattern, store them in out
|
||||
/** Only reads the package headers */
|
||||
void findMatching(const String& pattern, vector<PackagedP>& out);
|
||||
|
||||
/// Open a file from a package, with a name encoded as "/package/file"
|
||||
/** If 'package' is set then:
|
||||
* - tries to open a relative file from the package if the name is "file"
|
||||
* - verifies a dependency from that package if an absolute filename is used
|
||||
* this is to force people to fill in the dependencies
|
||||
* Afterwards, package will be set to the package the file is opened from
|
||||
*/
|
||||
InputStreamP openFileFromPackage(Packaged*& package, const String& name);
|
||||
|
||||
/// Get a filename to open from a package
|
||||
/** WARNING: this is a bit of a hack, since not all package types support names in this way.
|
||||
* It is needed for third party libraries (i.e. hunspell) that load stuff from files.
|
||||
*/
|
||||
String openFilenameFromPackage(Packaged*& package, const String& name);
|
||||
|
||||
// --------------------------------------------------- : Packages on disk
|
||||
|
||||
/// Check if the given dependency is currently installed
|
||||
bool checkDependency(const PackageDependency& dep, bool report_errors = true);
|
||||
/// Determine the latest version of the given package
|
||||
/** If it is not installed, returns false */
|
||||
bool installedVersion(const String& pkg, Version& version_out);
|
||||
|
||||
/// Get all installed packages
|
||||
void findAllInstalledPackages(vector<InstallablePackageP>& packages);
|
||||
|
||||
/// Install/uninstall a package, returns success
|
||||
bool install(const InstallablePackage& package);
|
||||
|
||||
// --------------------------------------------------- : Other package like things
|
||||
|
||||
/// Get the directory for dictionary files
|
||||
String getDictionaryDir(bool local) const;
|
||||
|
||||
// --------------------------------------------------- : Packages on a server
|
||||
|
||||
private:
|
||||
map<String, PackagedP> loaded_packages;
|
||||
PackageDirectory local, global;
|
||||
map<String, PackagedP> loaded_packages;
|
||||
PackageDirectory local, global;
|
||||
};
|
||||
|
||||
/// The global PackageManager instance
|
||||
@@ -190,48 +190,48 @@ extern PackageManager package_manager;
|
||||
/// Version information for an installed package
|
||||
class PackageVersion : public IntrusivePtrBase<PackageVersion> {
|
||||
public:
|
||||
PackageVersion() : status(0) {}
|
||||
PackageVersion(int status) : status(status) {}
|
||||
|
||||
String name;
|
||||
Version version;
|
||||
vector<PackageDependencyP> dependencies;
|
||||
enum Status
|
||||
{ STATUS_LOCAL = 0x01 ///< Package is installed in the local package dir
|
||||
, STATUS_GLOBAL = 0x02 ///< Package is installed in the global package dir
|
||||
, STATUS_BLESSED = 0x10 ///< Package is an official version, installed with the installer
|
||||
, STATUS_MODIFIED = 0x20 ///< Package has been modified after installing
|
||||
, STATUS_FIXED = 0x40 ///< The package can not be uninstalled
|
||||
};
|
||||
int status;
|
||||
|
||||
/// Check the status of the files in this package
|
||||
void check_status(Packaged& package);
|
||||
/// Set blessed status to true
|
||||
void bless();
|
||||
|
||||
PackageVersion() : status(0) {}
|
||||
PackageVersion(int status) : status(status) {}
|
||||
|
||||
String name;
|
||||
Version version;
|
||||
vector<PackageDependencyP> dependencies;
|
||||
enum Status
|
||||
{ STATUS_LOCAL = 0x01 ///< Package is installed in the local package dir
|
||||
, STATUS_GLOBAL = 0x02 ///< Package is installed in the global package dir
|
||||
, STATUS_BLESSED = 0x10 ///< Package is an official version, installed with the installer
|
||||
, STATUS_MODIFIED = 0x20 ///< Package has been modified after installing
|
||||
, STATUS_FIXED = 0x40 ///< The package can not be uninstalled
|
||||
};
|
||||
int status;
|
||||
|
||||
/// Check the status of the files in this package
|
||||
void check_status(Packaged& package);
|
||||
/// Set blessed status to true
|
||||
void bless();
|
||||
|
||||
public:
|
||||
/// Status of a single file
|
||||
enum FileStatus
|
||||
{ FILE_UNCHANGED
|
||||
, FILE_MODIFIED
|
||||
, FILE_ADDED
|
||||
, FILE_DELETED
|
||||
};
|
||||
/// Information on files in this package
|
||||
struct FileInfo {
|
||||
inline FileInfo() {}
|
||||
inline FileInfo(const String& file, const DateTime& time, FileStatus status)
|
||||
: file(file), time(time), status(status)
|
||||
{}
|
||||
String file;
|
||||
DateTime time;
|
||||
FileStatus status;
|
||||
inline bool operator < (const FileInfo& f) const { return file < f.file; }
|
||||
};
|
||||
/// Status of a single file
|
||||
enum FileStatus
|
||||
{ FILE_UNCHANGED
|
||||
, FILE_MODIFIED
|
||||
, FILE_ADDED
|
||||
, FILE_DELETED
|
||||
};
|
||||
/// Information on files in this package
|
||||
struct FileInfo {
|
||||
inline FileInfo() {}
|
||||
inline FileInfo(const String& file, const DateTime& time, FileStatus status)
|
||||
: file(file), time(time), status(status)
|
||||
{}
|
||||
String file;
|
||||
DateTime time;
|
||||
FileStatus status;
|
||||
inline bool operator < (const FileInfo& f) const { return file < f.file; }
|
||||
};
|
||||
private:
|
||||
vector<FileInfo> files; // sorted by filename
|
||||
DECLARE_REFLECTION();
|
||||
vector<FileInfo> files; // sorted by filename
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+332
-332
@@ -22,112 +22,112 @@ IMPLEMENT_DYNAMIC_ARG(ReaderPragmaHandler,reader_pragma_handler,nullptr);
|
||||
// ----------------------------------------------------------------------------- : Reader
|
||||
|
||||
Reader::Reader(const InputStreamP& input, Packaged* package, const String& filename, bool ignore_invalid)
|
||||
: indent(0), expected_indent(0), state(OUTSIDE)
|
||||
, ignore_invalid(ignore_invalid)
|
||||
, filename(filename), package(package), line_number(0), previous_line_number(0)
|
||||
, input(input)
|
||||
: indent(0), expected_indent(0), state(OUTSIDE)
|
||||
, ignore_invalid(ignore_invalid)
|
||||
, filename(filename), package(package), line_number(0), previous_line_number(0)
|
||||
, input(input)
|
||||
{
|
||||
moveNext();
|
||||
handleAppVersion();
|
||||
moveNext();
|
||||
handleAppVersion();
|
||||
}
|
||||
|
||||
Reader::Reader(Reader* parent, Packaged* pkg, const String& filename, bool ignore_invalid)
|
||||
: indent(0), expected_indent(0), state(OUTSIDE)
|
||||
, ignore_invalid(ignore_invalid)
|
||||
, filename(filename), package(pkg), line_number(0), previous_line_number(0)
|
||||
, input(package_manager.openFileFromPackage(package, filename))
|
||||
: indent(0), expected_indent(0), state(OUTSIDE)
|
||||
, ignore_invalid(ignore_invalid)
|
||||
, filename(filename), package(pkg), line_number(0), previous_line_number(0)
|
||||
, input(package_manager.openFileFromPackage(package, filename))
|
||||
{
|
||||
moveNext();
|
||||
// in an included file, use the app version of the parent if we have none
|
||||
handleAppVersion();
|
||||
if (file_app_version == 0) {
|
||||
file_app_version = parent->file_app_version;
|
||||
}
|
||||
moveNext();
|
||||
// in an included file, use the app version of the parent if we have none
|
||||
handleAppVersion();
|
||||
if (file_app_version == 0) {
|
||||
file_app_version = parent->file_app_version;
|
||||
}
|
||||
}
|
||||
|
||||
void Reader::addAlias(Version end_version, const Char* a, const Char* b) {
|
||||
Alias& alias = aliasses[a];
|
||||
alias.new_key = b;
|
||||
alias.end_version = end_version;
|
||||
Alias& alias = aliasses[a];
|
||||
alias.new_key = b;
|
||||
alias.end_version = end_version;
|
||||
}
|
||||
|
||||
void Reader::handleIgnore(int end_version, const Char* a) {
|
||||
if (file_app_version < end_version) {
|
||||
if (enterBlock(a)) exitBlock();
|
||||
}
|
||||
if (file_app_version < end_version) {
|
||||
if (enterBlock(a)) exitBlock();
|
||||
}
|
||||
}
|
||||
|
||||
void Reader::handleAppVersion() {
|
||||
if (enterBlock(_("mse_version"))) {
|
||||
handle(file_app_version);
|
||||
if (app_version < file_app_version) {
|
||||
queue_message(MESSAGE_WARNING, _ERROR_2_("newer version", filename, file_app_version.toString()));
|
||||
}
|
||||
exitBlock();
|
||||
}
|
||||
if (enterBlock(_("mse_version"))) {
|
||||
handle(file_app_version);
|
||||
if (app_version < file_app_version) {
|
||||
queue_message(MESSAGE_WARNING, _ERROR_2_("newer version", filename, file_app_version.toString()));
|
||||
}
|
||||
exitBlock();
|
||||
}
|
||||
}
|
||||
|
||||
void Reader::warning(const String& msg, int line_number_delta, bool warn_on_previous_line) {
|
||||
warnings += String(_("\nOn line "))
|
||||
<< ((warn_on_previous_line ? previous_line_number : line_number) + line_number_delta)
|
||||
<< _(": \t") << msg;
|
||||
warnings += String(_("\nOn line "))
|
||||
<< ((warn_on_previous_line ? previous_line_number : line_number) + line_number_delta)
|
||||
<< _(": \t") << msg;
|
||||
}
|
||||
|
||||
void Reader::showWarnings() {
|
||||
if (!warnings.empty()) {
|
||||
queue_message(MESSAGE_WARNING, _("Warnings while reading file:\n") + filename + _("\n") + warnings);
|
||||
warnings.clear();
|
||||
}
|
||||
if (!warnings.empty()) {
|
||||
queue_message(MESSAGE_WARNING, _("Warnings while reading file:\n") + filename + _("\n") + warnings);
|
||||
warnings.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool Reader::enterAnyBlock() {
|
||||
if (state == ENTERED) moveNext(); // on the key of the parent block, first move inside it
|
||||
if (indent != expected_indent) return false; // not enough indentation
|
||||
state = ENTERED;
|
||||
expected_indent += 1; // the indent inside the block must be at least this much
|
||||
return true;
|
||||
if (state == ENTERED) moveNext(); // on the key of the parent block, first move inside it
|
||||
if (indent != expected_indent) return false; // not enough indentation
|
||||
state = ENTERED;
|
||||
expected_indent += 1; // the indent inside the block must be at least this much
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Reader::enterBlock(const Char* name) {
|
||||
if (state == ENTERED) moveNext(); // on the key of the parent block, first move inside it
|
||||
if (indent != expected_indent) return false; // not enough indentation
|
||||
if (cannocial_name_compare(key, name)) {
|
||||
state = ENTERED;
|
||||
expected_indent += 1; // the indent inside the block must be at least this much
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (state == ENTERED) moveNext(); // on the key of the parent block, first move inside it
|
||||
if (indent != expected_indent) return false; // not enough indentation
|
||||
if (cannocial_name_compare(key, name)) {
|
||||
state = ENTERED;
|
||||
expected_indent += 1; // the indent inside the block must be at least this much
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Reader::exitBlock() {
|
||||
assert(expected_indent > 0);
|
||||
expected_indent -= 1;
|
||||
assert(state != UNHANDLED);
|
||||
previous_value.clear();
|
||||
if (state == ENTERED) moveNext(); // leave this key
|
||||
// Dump the remainder of the block
|
||||
// TODO: issue warnings?
|
||||
while (indent > expected_indent) {
|
||||
moveNext();
|
||||
}
|
||||
state = HANDLED;
|
||||
assert(expected_indent > 0);
|
||||
expected_indent -= 1;
|
||||
assert(state != UNHANDLED);
|
||||
previous_value.clear();
|
||||
if (state == ENTERED) moveNext(); // leave this key
|
||||
// Dump the remainder of the block
|
||||
// TODO: issue warnings?
|
||||
while (indent > expected_indent) {
|
||||
moveNext();
|
||||
}
|
||||
state = HANDLED;
|
||||
}
|
||||
|
||||
void Reader::moveNext() {
|
||||
previous_line_number = line_number;
|
||||
state = HANDLED;
|
||||
key.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()) {
|
||||
readLine();
|
||||
}
|
||||
// did we reach the end of the file?
|
||||
if (key.empty() && input->Eof()) {
|
||||
line_number += 1;
|
||||
indent = -1;
|
||||
}
|
||||
previous_line_number = line_number;
|
||||
state = HANDLED;
|
||||
key.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()) {
|
||||
readLine();
|
||||
}
|
||||
// did we reach the end of the file?
|
||||
if (key.empty() && input->Eof()) {
|
||||
line_number += 1;
|
||||
indent = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Faster vector, uses large local storage
|
||||
@@ -138,28 +138,28 @@ void Reader::moveNext() {
|
||||
*/
|
||||
template <typename T> class LocalVector {
|
||||
public:
|
||||
LocalVector() : the_size(0), alloced(SMALL_SIZE), buffer(small) {}
|
||||
~LocalVector() { if (buffer != small) free(buffer); }
|
||||
void push_back(T t) {
|
||||
if (the_size >= alloced) {
|
||||
// double buffer size
|
||||
if (buffer != small) {
|
||||
buffer = (T*)realloc(buffer, sizeof(T) * alloced * 2);
|
||||
} else {
|
||||
buffer = (T*)malloc(sizeof(T) * alloced * 2);
|
||||
memcpy(buffer, small, alloced * sizeof(T));
|
||||
}
|
||||
alloced *= 2;
|
||||
}
|
||||
buffer[the_size++] = t;
|
||||
}
|
||||
inline const T* get() const { return buffer; }
|
||||
inline size_t size() const { return the_size; }
|
||||
LocalVector() : the_size(0), alloced(SMALL_SIZE), buffer(small) {}
|
||||
~LocalVector() { if (buffer != small) free(buffer); }
|
||||
void push_back(T t) {
|
||||
if (the_size >= alloced) {
|
||||
// double buffer size
|
||||
if (buffer != small) {
|
||||
buffer = (T*)realloc(buffer, sizeof(T) * alloced * 2);
|
||||
} else {
|
||||
buffer = (T*)malloc(sizeof(T) * alloced * 2);
|
||||
memcpy(buffer, small, alloced * sizeof(T));
|
||||
}
|
||||
alloced *= 2;
|
||||
}
|
||||
buffer[the_size++] = t;
|
||||
}
|
||||
inline const T* get() const { return buffer; }
|
||||
inline size_t size() const { return the_size; }
|
||||
private:
|
||||
static const int SMALL_SIZE = 1024;
|
||||
size_t the_size, alloced;
|
||||
T* buffer;
|
||||
T small[SMALL_SIZE];
|
||||
static const int SMALL_SIZE = 1024;
|
||||
size_t the_size, alloced;
|
||||
T* buffer;
|
||||
T small[SMALL_SIZE];
|
||||
};
|
||||
|
||||
/// Read an UTF-8 encoded line from an input stream
|
||||
@@ -167,290 +167,290 @@ template <typename T> class LocalVector {
|
||||
*/
|
||||
String read_utf8_line(wxInputStream& input, bool eat_bom = true, bool until_eof = false);
|
||||
String read_utf8_line(wxInputStream& input, bool eat_bom, bool until_eof) {
|
||||
LocalVector<char> buffer;
|
||||
while (!input.Eof()) {
|
||||
Byte c = input.GetC(); if (input.LastRead() <= 0) break;
|
||||
if (!until_eof) {
|
||||
if (c == '\n') break;
|
||||
if (c == '\r') {
|
||||
if (input.Eof()) break;
|
||||
c = input.GetC(); if (input.LastRead() <= 0) break;
|
||||
if (c != '\n') {
|
||||
input.Ungetch(c); // \r but not \r\n
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffer.push_back(c);
|
||||
}
|
||||
// convert to string
|
||||
buffer.push_back('\0');
|
||||
size_t size = wxConvUTF8.MB2WC(nullptr, buffer.get(), 0);
|
||||
if (size == size_t(-1)) {
|
||||
throw ParseError(_("Invalid UTF-8 sequence"));
|
||||
} else if (size == 0) {
|
||||
return _("");
|
||||
}
|
||||
#ifdef UNICODE
|
||||
#if wxVERSION_NUMBER >= 2900
|
||||
String result = wxString::FromUTF8(buffer.get(), buffer.size());
|
||||
return eat_bom ? decodeUTF8BOM(result) : result;
|
||||
#else
|
||||
// NOTE: wx doc is wrong, parameter to GetWritableChar is numer of characters, not bytes
|
||||
String result;
|
||||
Char* result_buf = result.GetWriteBuf(size + 1);
|
||||
wxConvUTF8.MB2WC(result_buf, buffer.get(), size + 1);
|
||||
result.UngetWriteBuf(size);
|
||||
return eat_bom ? decodeUTF8BOM(result) : result;
|
||||
#endif
|
||||
#else
|
||||
String result;
|
||||
// first to wchar, then back to local
|
||||
vector<wchar_t> buf2; buf2.resize(size+1);
|
||||
wxConvUTF8.MB2WC(&buf2[0], buffer.get(), size + 1);
|
||||
// eat BOM?
|
||||
if (eat_bom && buf2[0]==0xFEFF ) {
|
||||
buf2.erase(buf2.begin()); // remove BOM
|
||||
}
|
||||
// convert
|
||||
#ifdef __WXMSW__
|
||||
// size includes null terminator
|
||||
size = ::WideCharToMultiByte(CP_ACP, 0, &buf2[0], -1, nullptr, 0, nullptr, nullptr);
|
||||
Char* result_buf = result.GetWriteBuf(size);
|
||||
::WideCharToMultiByte(CP_ACP, 0, &buf2[0], -1, result_buf, (int)size, nullptr, nullptr);
|
||||
result.UngetWriteBuf(size - 1);
|
||||
#else
|
||||
for (size_t i = 0 ; i < size ; ++i) {
|
||||
wchar_t wc = buf2[i];
|
||||
if (wc < 0xFF) {
|
||||
result += (Char)wc;
|
||||
} else {
|
||||
// not valid in Latin1
|
||||
result += '?';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
#endif
|
||||
LocalVector<char> buffer;
|
||||
while (!input.Eof()) {
|
||||
Byte c = input.GetC(); if (input.LastRead() <= 0) break;
|
||||
if (!until_eof) {
|
||||
if (c == '\n') break;
|
||||
if (c == '\r') {
|
||||
if (input.Eof()) break;
|
||||
c = input.GetC(); if (input.LastRead() <= 0) break;
|
||||
if (c != '\n') {
|
||||
input.Ungetch(c); // \r but not \r\n
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffer.push_back(c);
|
||||
}
|
||||
// convert to string
|
||||
buffer.push_back('\0');
|
||||
size_t size = wxConvUTF8.MB2WC(nullptr, buffer.get(), 0);
|
||||
if (size == size_t(-1)) {
|
||||
throw ParseError(_("Invalid UTF-8 sequence"));
|
||||
} else if (size == 0) {
|
||||
return _("");
|
||||
}
|
||||
#ifdef UNICODE
|
||||
#if wxVERSION_NUMBER >= 2900
|
||||
String result = wxString::FromUTF8(buffer.get(), buffer.size());
|
||||
return eat_bom ? decodeUTF8BOM(result) : result;
|
||||
#else
|
||||
// NOTE: wx doc is wrong, parameter to GetWritableChar is numer of characters, not bytes
|
||||
String result;
|
||||
Char* result_buf = result.GetWriteBuf(size + 1);
|
||||
wxConvUTF8.MB2WC(result_buf, buffer.get(), size + 1);
|
||||
result.UngetWriteBuf(size);
|
||||
return eat_bom ? decodeUTF8BOM(result) : result;
|
||||
#endif
|
||||
#else
|
||||
String result;
|
||||
// first to wchar, then back to local
|
||||
vector<wchar_t> buf2; buf2.resize(size+1);
|
||||
wxConvUTF8.MB2WC(&buf2[0], buffer.get(), size + 1);
|
||||
// eat BOM?
|
||||
if (eat_bom && buf2[0]==0xFEFF ) {
|
||||
buf2.erase(buf2.begin()); // remove BOM
|
||||
}
|
||||
// convert
|
||||
#ifdef __WXMSW__
|
||||
// size includes null terminator
|
||||
size = ::WideCharToMultiByte(CP_ACP, 0, &buf2[0], -1, nullptr, 0, nullptr, nullptr);
|
||||
Char* result_buf = result.GetWriteBuf(size);
|
||||
::WideCharToMultiByte(CP_ACP, 0, &buf2[0], -1, result_buf, (int)size, nullptr, nullptr);
|
||||
result.UngetWriteBuf(size - 1);
|
||||
#else
|
||||
for (size_t i = 0 ; i < size ; ++i) {
|
||||
wchar_t wc = buf2[i];
|
||||
if (wc < 0xFF) {
|
||||
result += (Char)wc;
|
||||
} else {
|
||||
// not valid in Latin1
|
||||
result += '?';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Reader::readLine(bool in_string) {
|
||||
line_number += 1;
|
||||
// We have to do our own line reading, because wxTextInputStream is insane
|
||||
try {
|
||||
line = read_utf8_line(*input, line_number == 1);
|
||||
} catch (const ParseError& e) {
|
||||
throw ParseError(e.what() + String(_(" on line ")) << line_number);
|
||||
}
|
||||
// pragma handler
|
||||
if (reader_pragma_handler()) reader_pragma_handler()(line);
|
||||
// read indentation
|
||||
indent = 0;
|
||||
while ((UInt)indent < line.size() && line.GetChar(indent) == _('\t')) {
|
||||
indent += 1;
|
||||
}
|
||||
// read key / value
|
||||
if (line.find_first_not_of(_(" \t")) == String::npos || line.GetChar(indent) == _('#')) {
|
||||
// empty line or comment
|
||||
key.clear();
|
||||
return;
|
||||
}
|
||||
size_t pos = line.find_first_of(_(':'), indent);
|
||||
key = line.substr(indent, pos - indent);
|
||||
if (!ignore_invalid && !in_string && starts_with(key, _(" "))) {
|
||||
warning(_("key: '") + key + _("' starts with a space; only use TABs for indentation!"), 0, false);
|
||||
// try to fix up: 8 spaces is a tab
|
||||
while (starts_with(key, _(" "))) {
|
||||
key = key.substr(8);
|
||||
indent += 1;
|
||||
}
|
||||
}
|
||||
key = canonical_name_form(trim(key));
|
||||
value = pos == String::npos ? _("") : trim_left(line.substr(pos+1));
|
||||
if (key.empty() && pos!=String::npos) key = _(" "); // we don't want an empty key if there was a colon
|
||||
line_number += 1;
|
||||
// We have to do our own line reading, because wxTextInputStream is insane
|
||||
try {
|
||||
line = read_utf8_line(*input, line_number == 1);
|
||||
} catch (const ParseError& e) {
|
||||
throw ParseError(e.what() + String(_(" on line ")) << line_number);
|
||||
}
|
||||
// pragma handler
|
||||
if (reader_pragma_handler()) reader_pragma_handler()(line);
|
||||
// read indentation
|
||||
indent = 0;
|
||||
while ((UInt)indent < line.size() && line.GetChar(indent) == _('\t')) {
|
||||
indent += 1;
|
||||
}
|
||||
// read key / value
|
||||
if (line.find_first_not_of(_(" \t")) == String::npos || line.GetChar(indent) == _('#')) {
|
||||
// empty line or comment
|
||||
key.clear();
|
||||
return;
|
||||
}
|
||||
size_t pos = line.find_first_of(_(':'), indent);
|
||||
key = line.substr(indent, pos - indent);
|
||||
if (!ignore_invalid && !in_string && starts_with(key, _(" "))) {
|
||||
warning(_("key: '") + key + _("' starts with a space; only use TABs for indentation!"), 0, false);
|
||||
// try to fix up: 8 spaces is a tab
|
||||
while (starts_with(key, _(" "))) {
|
||||
key = key.substr(8);
|
||||
indent += 1;
|
||||
}
|
||||
}
|
||||
key = canonical_name_form(trim(key));
|
||||
value = pos == String::npos ? _("") : trim_left(line.substr(pos+1));
|
||||
if (key.empty() && pos!=String::npos) key = _(" "); // we don't want an empty key if there was a colon
|
||||
}
|
||||
|
||||
void Reader::unknownKey() {
|
||||
// ignore?
|
||||
if (ignore_invalid) {
|
||||
do {
|
||||
moveNext();
|
||||
} while (indent > expected_indent);
|
||||
return;
|
||||
}
|
||||
// aliasses?
|
||||
map<String,Alias>::const_iterator it = aliasses.find(key);
|
||||
if (it != aliasses.end()) {
|
||||
if (aliasses.find(it->second.new_key) != aliasses.end()) {
|
||||
// alias points to another alias, don't follow it, there is the risk of infinite loops
|
||||
} else if (it->second.end_version <= file_app_version) {
|
||||
// alias not used for this version, use in warning
|
||||
if (indent == expected_indent) {
|
||||
warning(_("Unexpected key: '") + key + _("' use '") + it->second.new_key + _("'"), 0, false);
|
||||
do {
|
||||
moveNext();
|
||||
} while (indent > expected_indent);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// try this key instead
|
||||
key = it->second.new_key;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (indent >= expected_indent) {
|
||||
warning(_("Unexpected key: '") + key + _("'"), 0, false);
|
||||
do {
|
||||
moveNext();
|
||||
} while (indent > expected_indent);
|
||||
}
|
||||
// else: could be a nameless value, which doesn't call exitBlock to move past its own key
|
||||
// ignore?
|
||||
if (ignore_invalid) {
|
||||
do {
|
||||
moveNext();
|
||||
} while (indent > expected_indent);
|
||||
return;
|
||||
}
|
||||
// aliasses?
|
||||
map<String,Alias>::const_iterator it = aliasses.find(key);
|
||||
if (it != aliasses.end()) {
|
||||
if (aliasses.find(it->second.new_key) != aliasses.end()) {
|
||||
// alias points to another alias, don't follow it, there is the risk of infinite loops
|
||||
} else if (it->second.end_version <= file_app_version) {
|
||||
// alias not used for this version, use in warning
|
||||
if (indent == expected_indent) {
|
||||
warning(_("Unexpected key: '") + key + _("' use '") + it->second.new_key + _("'"), 0, false);
|
||||
do {
|
||||
moveNext();
|
||||
} while (indent > expected_indent);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// try this key instead
|
||||
key = it->second.new_key;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (indent >= expected_indent) {
|
||||
warning(_("Unexpected key: '") + key + _("'"), 0, false);
|
||||
do {
|
||||
moveNext();
|
||||
} while (indent > expected_indent);
|
||||
}
|
||||
// else: could be a nameless value, which doesn't call exitBlock to move past its own key
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Handling basic types
|
||||
|
||||
void Reader::unhandle() {
|
||||
assert(state == HANDLED);
|
||||
state = UNHANDLED;
|
||||
assert(state == HANDLED);
|
||||
state = UNHANDLED;
|
||||
}
|
||||
|
||||
const String& Reader::getValue() {
|
||||
assert(state != HANDLED); // don't try to handle things twice
|
||||
if (state == UNHANDLED) {
|
||||
state = HANDLED;
|
||||
return previous_value;
|
||||
} else if (value.empty()) {
|
||||
// a multiline string
|
||||
previous_value.clear();
|
||||
int pending_newlines = 0;
|
||||
// read all lines that are indented enough
|
||||
readLine(true);
|
||||
previous_line_number = line_number;
|
||||
while (indent >= expected_indent && !input->Eof()) {
|
||||
previous_value.resize(previous_value.size() + pending_newlines, _('\n'));
|
||||
pending_newlines = 0;
|
||||
previous_value += line.substr(expected_indent); // strip expected indent
|
||||
do {
|
||||
readLine(true);
|
||||
pending_newlines++;
|
||||
// skip empty lines that are not indented enough
|
||||
} while(trim(line).empty() && indent < expected_indent && !input->Eof());
|
||||
}
|
||||
// moveNext(), but without the initial readLine()
|
||||
state = HANDLED;
|
||||
while (key.empty() && !input->Eof()) {
|
||||
readLine();
|
||||
}
|
||||
// did we reach the end of the file?
|
||||
if (key.empty() && input->Eof()) {
|
||||
line_number += 1;
|
||||
indent = -1;
|
||||
}
|
||||
if (indent >= expected_indent) {
|
||||
warning(_("Blank line or comment in text block, that is insufficiently indented.\n")
|
||||
_("\t\tEither indent the comment/blank line, or add a 'key:' after it.\n")
|
||||
_("\t\tThis could cause more more error messages.\n"), -1, false);
|
||||
}
|
||||
return previous_value;
|
||||
} else {
|
||||
previous_value = value;
|
||||
moveNext();
|
||||
return previous_value;
|
||||
}
|
||||
assert(state != HANDLED); // don't try to handle things twice
|
||||
if (state == UNHANDLED) {
|
||||
state = HANDLED;
|
||||
return previous_value;
|
||||
} else if (value.empty()) {
|
||||
// a multiline string
|
||||
previous_value.clear();
|
||||
int pending_newlines = 0;
|
||||
// read all lines that are indented enough
|
||||
readLine(true);
|
||||
previous_line_number = line_number;
|
||||
while (indent >= expected_indent && !input->Eof()) {
|
||||
previous_value.resize(previous_value.size() + pending_newlines, _('\n'));
|
||||
pending_newlines = 0;
|
||||
previous_value += line.substr(expected_indent); // strip expected indent
|
||||
do {
|
||||
readLine(true);
|
||||
pending_newlines++;
|
||||
// skip empty lines that are not indented enough
|
||||
} while(trim(line).empty() && indent < expected_indent && !input->Eof());
|
||||
}
|
||||
// moveNext(), but without the initial readLine()
|
||||
state = HANDLED;
|
||||
while (key.empty() && !input->Eof()) {
|
||||
readLine();
|
||||
}
|
||||
// did we reach the end of the file?
|
||||
if (key.empty() && input->Eof()) {
|
||||
line_number += 1;
|
||||
indent = -1;
|
||||
}
|
||||
if (indent >= expected_indent) {
|
||||
warning(_("Blank line or comment in text block, that is insufficiently indented.\n")
|
||||
_("\t\tEither indent the comment/blank line, or add a 'key:' after it.\n")
|
||||
_("\t\tThis could cause more more error messages.\n"), -1, false);
|
||||
}
|
||||
return previous_value;
|
||||
} else {
|
||||
previous_value = value;
|
||||
moveNext();
|
||||
return previous_value;
|
||||
}
|
||||
}
|
||||
|
||||
template <> void Reader::handle(String& s) {
|
||||
s = getValue();
|
||||
s = getValue();
|
||||
}
|
||||
template <> void Reader::handle(int& i) {
|
||||
long l = 0;
|
||||
if (!getValue().ToLong(&l)) {
|
||||
warning(_("Expected integer instead of '") + previous_value + _("'"));
|
||||
}
|
||||
i = l;
|
||||
long l = 0;
|
||||
if (!getValue().ToLong(&l)) {
|
||||
warning(_("Expected integer instead of '") + previous_value + _("'"));
|
||||
}
|
||||
i = l;
|
||||
}
|
||||
template <> void Reader::handle(unsigned int& i) {
|
||||
long l = 0;
|
||||
if (!getValue().ToLong(&l)) {
|
||||
warning(_("Expected non-negative integer instead of '") + previous_value + _("'"));
|
||||
} else if (l < 0) {
|
||||
warning(wxString::Format(_("Expected non-negative integer instead of %d"),(int)l));
|
||||
}
|
||||
i = abs(l); // abs, because it will seem strange if -1 comes out as MAX_INT
|
||||
long l = 0;
|
||||
if (!getValue().ToLong(&l)) {
|
||||
warning(_("Expected non-negative integer instead of '") + previous_value + _("'"));
|
||||
} else if (l < 0) {
|
||||
warning(wxString::Format(_("Expected non-negative integer instead of %d"),(int)l));
|
||||
}
|
||||
i = abs(l); // abs, because it will seem strange if -1 comes out as MAX_INT
|
||||
}
|
||||
template <> void Reader::handle(double& d) {
|
||||
if (!getValue().ToDouble(&d)) {
|
||||
warning(_("Expected floating point number instead of '") + previous_value + _("'"));
|
||||
}
|
||||
if (!getValue().ToDouble(&d)) {
|
||||
warning(_("Expected floating point number instead of '") + previous_value + _("'"));
|
||||
}
|
||||
}
|
||||
template <> void Reader::handle(bool& b) {
|
||||
const String& v = getValue();
|
||||
if (v==_("true") || v==_("1") || v==_("yes")) {
|
||||
b = true;
|
||||
} else if (v==_("false") || v==_("0") || v==_("no")) {
|
||||
b = false;
|
||||
} else {
|
||||
warning(_("Expected boolean ('true' or 'false') instead of '") + v + _("'"));
|
||||
}
|
||||
const String& v = getValue();
|
||||
if (v==_("true") || v==_("1") || v==_("yes")) {
|
||||
b = true;
|
||||
} else if (v==_("false") || v==_("0") || v==_("no")) {
|
||||
b = false;
|
||||
} else {
|
||||
warning(_("Expected boolean ('true' or 'false') instead of '") + v + _("'"));
|
||||
}
|
||||
}
|
||||
template <> void Reader::handle(tribool& tb) {
|
||||
bool b;
|
||||
handle(b);
|
||||
tb = b;
|
||||
bool b;
|
||||
handle(b);
|
||||
tb = b;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Handling less basic util types
|
||||
|
||||
template <> void Reader::handle(wxDateTime& date) {
|
||||
if (!date.ParseDateTime(getValue().c_str())) {
|
||||
throw ParseError(_("Expected a date and time"));
|
||||
}
|
||||
if (!date.ParseDateTime(getValue().c_str())) {
|
||||
throw ParseError(_("Expected a date and time"));
|
||||
}
|
||||
}
|
||||
|
||||
template <> void Reader::handle(Vector2D& vec) {
|
||||
if (!wxSscanf(getValue().c_str(), _("(%lf,%lf)"), &vec.x, &vec.y)) {
|
||||
throw ParseError(_("Expected (x,y)"));
|
||||
}
|
||||
if (!wxSscanf(getValue().c_str(), _("(%lf,%lf)"), &vec.x, &vec.y)) {
|
||||
throw ParseError(_("Expected (x,y)"));
|
||||
}
|
||||
}
|
||||
|
||||
template <> void Reader::handle(FileName& f) {
|
||||
if (clipboard_package()) {
|
||||
String str = getValue();
|
||||
if (!str.empty()) {
|
||||
// copy file into current package
|
||||
try {
|
||||
String packaged_name = clipboard_package()->newFileName(_("image"),_("")); // a new unique name in the package, assume it's an image
|
||||
OutputStreamP out = clipboard_package()->openOut(packaged_name);
|
||||
InputStreamP in = Package::openAbsoluteFile(str);
|
||||
out->Write(*in); // copy
|
||||
f.assign(packaged_name);
|
||||
} catch (Error) {
|
||||
// ignore errors
|
||||
}
|
||||
} else {
|
||||
f.assign(str);
|
||||
}
|
||||
} else {
|
||||
handle(static_cast<String&>(f));
|
||||
}
|
||||
if (clipboard_package()) {
|
||||
String str = getValue();
|
||||
if (!str.empty()) {
|
||||
// copy file into current package
|
||||
try {
|
||||
String packaged_name = clipboard_package()->newFileName(_("image"),_("")); // a new unique name in the package, assume it's an image
|
||||
OutputStreamP out = clipboard_package()->openOut(packaged_name);
|
||||
InputStreamP in = Package::openAbsoluteFile(str);
|
||||
out->Write(*in); // copy
|
||||
f.assign(packaged_name);
|
||||
} catch (Error) {
|
||||
// ignore errors
|
||||
}
|
||||
} else {
|
||||
f.assign(str);
|
||||
}
|
||||
} else {
|
||||
handle(static_cast<String&>(f));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EnumReader
|
||||
|
||||
String EnumReader::notDoneErrorMessage() const {
|
||||
if (!first) throw InternalError(_("No first value in EnumReader"));
|
||||
return _ERROR_2_("unrecognized value", read, first);
|
||||
if (!first) throw InternalError(_("No first value in EnumReader"));
|
||||
return _ERROR_2_("unrecognized value", read, first);
|
||||
}
|
||||
|
||||
void EnumReader::warnIfNotDone(Reader* errors_to) {
|
||||
if (!done) {
|
||||
// warning: unknown value
|
||||
errors_to->warning(notDoneErrorMessage());
|
||||
}
|
||||
if (!done) {
|
||||
// warning: unknown value
|
||||
errors_to->warning(notDoneErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
void EnumReader::errorIfNotDone() {
|
||||
if (!done) {
|
||||
throw ParseError(notDoneErrorMessage());
|
||||
}
|
||||
if (!done) {
|
||||
throw ParseError(notDoneErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
+216
-216
@@ -33,163 +33,163 @@ typedef shared_ptr<wxInputStream> InputStreamP;
|
||||
*/
|
||||
class Reader {
|
||||
private:
|
||||
/// Construct a reader that reads a file in a package
|
||||
/** Used for "include file" keys.
|
||||
* package can be nullptr
|
||||
*/
|
||||
Reader(Reader* parent, Packaged* package, const String& filename, bool ignore_invalid = false);
|
||||
/// Construct a reader that reads a file in a package
|
||||
/** Used for "include file" keys.
|
||||
* package can be nullptr
|
||||
*/
|
||||
Reader(Reader* parent, Packaged* package, const String& filename, bool ignore_invalid = false);
|
||||
public:
|
||||
/// Construct a reader that reads from the given input stream
|
||||
/** filename is used only for error messages
|
||||
*/
|
||||
Reader(const InputStreamP& input, Packaged* package = nullptr, const String& filename = wxEmptyString, bool ignore_invalid = false);
|
||||
|
||||
~Reader() { showWarnings(); }
|
||||
|
||||
/// Tell the reflection code we are reading
|
||||
inline bool reading() const { return true; }
|
||||
/// Tell the reflection code we are not related to scripting
|
||||
inline bool scripting() const { return false; }
|
||||
/// Is the thing currently being read 'complex', i.e. does it have children
|
||||
inline bool isComplex() const { return indent != expected_indent - 1 || value.empty(); }
|
||||
/// Add a as an alias for b, all keys a will be replaced with b, only if file_app_version < end_version
|
||||
void addAlias(Version end_version, const Char* a, const Char* b);
|
||||
/// Ignore old keys
|
||||
void handleIgnore(int, const Char*);
|
||||
|
||||
/// Read and check the application version
|
||||
void handleAppVersion();
|
||||
|
||||
/// Add a warning message, but continue reading
|
||||
void warning(const String& msg, int line_number_delta = 0, bool warn_on_previous_line = true);
|
||||
/// Show all warning messages, but continue reading
|
||||
void showWarnings();
|
||||
|
||||
// --------------------------------------------------- : Handling objects
|
||||
/// Handle an object that can read as much as it can eat
|
||||
template <typename T>
|
||||
void handle_greedy(T& object) {
|
||||
do {
|
||||
handle(object);
|
||||
if (state != HANDLED) unknownKey(object);
|
||||
state = OUTSIDE;
|
||||
} while (indent >= expected_indent);
|
||||
}
|
||||
|
||||
/// Handle an object: read it if it's name matches
|
||||
template <typename T>
|
||||
void handle(const Char* name, T& object) {
|
||||
if (enterBlock(name)) {
|
||||
handle_greedy(object);
|
||||
exitBlock();
|
||||
}
|
||||
}
|
||||
/// Handle a value
|
||||
template <typename T>
|
||||
inline void handleNoScript(const Char* name, T& value) { handle(name,value); }
|
||||
|
||||
/// Reads a vector from the input stream
|
||||
template <typename T>
|
||||
void handle(const Char* name, vector<T>& vector);
|
||||
|
||||
/// Reads an object of type T from the input stream
|
||||
template <typename T> void handle(T&);
|
||||
/// Reads a intrusive_ptr from the input stream
|
||||
template <typename T> void handle(intrusive_ptr<T>&);
|
||||
/// Reads a map from the input stream
|
||||
template <typename V> void handle(map<String,V>&);
|
||||
/// Reads an IndexMap from the input stream, reads only keys that already exist in the map
|
||||
template <typename K, typename V> void handle(IndexMap<K,V>&);
|
||||
template <typename K, typename V> void handle(DelayedIndexMaps<K,V>&);
|
||||
template <typename K, typename V> void handle(DelayedIndexMapsData<K,V>&);
|
||||
/// Reads a Defaultable from the input stream
|
||||
template <typename T> void handle(Defaultable<T>&);
|
||||
/// Reads a Scriptable from the input stream
|
||||
template <typename T> void handle(Scriptable<T>&);
|
||||
// special behaviour
|
||||
void handle(GameP&);
|
||||
void handle(StyleSheetP&);
|
||||
|
||||
/// Indicate that the last value from getValue() was not handled, allowing it to be handled again
|
||||
void unhandle();
|
||||
|
||||
/// The package being read from
|
||||
inline Packaged* getPackage() const { return package; }
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
/// App version this file was made with
|
||||
Version file_app_version;
|
||||
/// Construct a reader that reads from the given input stream
|
||||
/** filename is used only for error messages
|
||||
*/
|
||||
Reader(const InputStreamP& input, Packaged* package = nullptr, const String& filename = wxEmptyString, bool ignore_invalid = false);
|
||||
|
||||
~Reader() { showWarnings(); }
|
||||
|
||||
/// Tell the reflection code we are reading
|
||||
inline bool reading() const { return true; }
|
||||
/// Tell the reflection code we are not related to scripting
|
||||
inline bool scripting() const { return false; }
|
||||
/// Is the thing currently being read 'complex', i.e. does it have children
|
||||
inline bool isComplex() const { return indent != expected_indent - 1 || value.empty(); }
|
||||
/// Add a as an alias for b, all keys a will be replaced with b, only if file_app_version < end_version
|
||||
void addAlias(Version end_version, const Char* a, const Char* b);
|
||||
/// Ignore old keys
|
||||
void handleIgnore(int, const Char*);
|
||||
|
||||
/// Read and check the application version
|
||||
void handleAppVersion();
|
||||
|
||||
/// Add a warning message, but continue reading
|
||||
void warning(const String& msg, int line_number_delta = 0, bool warn_on_previous_line = true);
|
||||
/// Show all warning messages, but continue reading
|
||||
void showWarnings();
|
||||
|
||||
// --------------------------------------------------- : Handling objects
|
||||
/// Handle an object that can read as much as it can eat
|
||||
template <typename T>
|
||||
void handle_greedy(T& object) {
|
||||
do {
|
||||
handle(object);
|
||||
if (state != HANDLED) unknownKey(object);
|
||||
state = OUTSIDE;
|
||||
} while (indent >= expected_indent);
|
||||
}
|
||||
|
||||
/// Handle an object: read it if it's name matches
|
||||
template <typename T>
|
||||
void handle(const Char* name, T& object) {
|
||||
if (enterBlock(name)) {
|
||||
handle_greedy(object);
|
||||
exitBlock();
|
||||
}
|
||||
}
|
||||
/// Handle a value
|
||||
template <typename T>
|
||||
inline void handleNoScript(const Char* name, T& value) { handle(name,value); }
|
||||
|
||||
/// Reads a vector from the input stream
|
||||
template <typename T>
|
||||
void handle(const Char* name, vector<T>& vector);
|
||||
|
||||
/// Reads an object of type T from the input stream
|
||||
template <typename T> void handle(T&);
|
||||
/// Reads a intrusive_ptr from the input stream
|
||||
template <typename T> void handle(intrusive_ptr<T>&);
|
||||
/// Reads a map from the input stream
|
||||
template <typename V> void handle(map<String,V>&);
|
||||
/// Reads an IndexMap from the input stream, reads only keys that already exist in the map
|
||||
template <typename K, typename V> void handle(IndexMap<K,V>&);
|
||||
template <typename K, typename V> void handle(DelayedIndexMaps<K,V>&);
|
||||
template <typename K, typename V> void handle(DelayedIndexMapsData<K,V>&);
|
||||
/// Reads a Defaultable from the input stream
|
||||
template <typename T> void handle(Defaultable<T>&);
|
||||
/// Reads a Scriptable from the input stream
|
||||
template <typename T> void handle(Scriptable<T>&);
|
||||
// special behaviour
|
||||
void handle(GameP&);
|
||||
void handle(StyleSheetP&);
|
||||
|
||||
/// Indicate that the last value from getValue() was not handled, allowing it to be handled again
|
||||
void unhandle();
|
||||
|
||||
/// The package being read from
|
||||
inline Packaged* getPackage() const { return package; }
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
/// App version this file was made with
|
||||
Version file_app_version;
|
||||
private:
|
||||
/// The line we read
|
||||
String line;
|
||||
/// The key and value of the last line we read
|
||||
String key, value;
|
||||
/// Value of the *previous* line, only valid in state==HANDLED
|
||||
String previous_value;
|
||||
/// Indentation of the last line we read
|
||||
int indent;
|
||||
/// Indentation of the block we are in
|
||||
int expected_indent;
|
||||
/// State of the reader
|
||||
enum State {
|
||||
OUTSIDE, ///< We have not entered the block of the current key
|
||||
ENTERED, ///< We just entered the block of the current key
|
||||
HANDLED, ///< We have handled a value, and moved to the next line, previous_value is the value we just handled
|
||||
UNHANDLED, ///< Something has been 'unhandled()'
|
||||
} state;
|
||||
/// Aliasses for compatability
|
||||
struct Alias {
|
||||
String new_key;
|
||||
Version end_version;
|
||||
};
|
||||
/// Aliasses for compatability
|
||||
map<String, Alias> aliasses;
|
||||
/// Should all invalid keys be ignored?
|
||||
bool ignore_invalid;
|
||||
|
||||
/// Filename for error messages
|
||||
String filename;
|
||||
/// Package this file is from, if any
|
||||
Packaged* package;
|
||||
/// Line number of the current line for error messages
|
||||
int line_number;
|
||||
/// Line number of the previous_line
|
||||
int previous_line_number;
|
||||
/// Input stream we are reading from
|
||||
InputStreamP input;
|
||||
/// Accumulated warning messages
|
||||
String warnings;
|
||||
|
||||
// --------------------------------------------------- : Reading the stream
|
||||
|
||||
/// Is there a block with the given key under the current cursor? if so, enter it
|
||||
bool enterBlock(const Char* name);
|
||||
/// Enter any block, no matter what the key
|
||||
bool enterAnyBlock();
|
||||
/// Leave the block we are in
|
||||
void exitBlock();
|
||||
|
||||
/// Move to the next non empty line
|
||||
void moveNext();
|
||||
/// Reads the next line from the input, and stores it in line/key/value/indent
|
||||
void readLine(bool in_string = false);
|
||||
|
||||
/// Return the value on the current line
|
||||
const String& getValue();
|
||||
|
||||
/// No line was read, because nothing mathes the current key
|
||||
/** Maybe the key is "include file" */
|
||||
template <typename T>
|
||||
void unknownKey(T& v) {
|
||||
if (key == _("include file")) {
|
||||
Reader reader(this, package, value, ignore_invalid);
|
||||
reader.handle_greedy(v);
|
||||
moveNext();
|
||||
} else {
|
||||
unknownKey();
|
||||
}
|
||||
}
|
||||
void unknownKey();
|
||||
/// The line we read
|
||||
String line;
|
||||
/// The key and value of the last line we read
|
||||
String key, value;
|
||||
/// Value of the *previous* line, only valid in state==HANDLED
|
||||
String previous_value;
|
||||
/// Indentation of the last line we read
|
||||
int indent;
|
||||
/// Indentation of the block we are in
|
||||
int expected_indent;
|
||||
/// State of the reader
|
||||
enum State {
|
||||
OUTSIDE, ///< We have not entered the block of the current key
|
||||
ENTERED, ///< We just entered the block of the current key
|
||||
HANDLED, ///< We have handled a value, and moved to the next line, previous_value is the value we just handled
|
||||
UNHANDLED, ///< Something has been 'unhandled()'
|
||||
} state;
|
||||
/// Aliasses for compatability
|
||||
struct Alias {
|
||||
String new_key;
|
||||
Version end_version;
|
||||
};
|
||||
/// Aliasses for compatability
|
||||
map<String, Alias> aliasses;
|
||||
/// Should all invalid keys be ignored?
|
||||
bool ignore_invalid;
|
||||
|
||||
/// Filename for error messages
|
||||
String filename;
|
||||
/// Package this file is from, if any
|
||||
Packaged* package;
|
||||
/// Line number of the current line for error messages
|
||||
int line_number;
|
||||
/// Line number of the previous_line
|
||||
int previous_line_number;
|
||||
/// Input stream we are reading from
|
||||
InputStreamP input;
|
||||
/// Accumulated warning messages
|
||||
String warnings;
|
||||
|
||||
// --------------------------------------------------- : Reading the stream
|
||||
|
||||
/// Is there a block with the given key under the current cursor? if so, enter it
|
||||
bool enterBlock(const Char* name);
|
||||
/// Enter any block, no matter what the key
|
||||
bool enterAnyBlock();
|
||||
/// Leave the block we are in
|
||||
void exitBlock();
|
||||
|
||||
/// Move to the next non empty line
|
||||
void moveNext();
|
||||
/// Reads the next line from the input, and stores it in line/key/value/indent
|
||||
void readLine(bool in_string = false);
|
||||
|
||||
/// Return the value on the current line
|
||||
const String& getValue();
|
||||
|
||||
/// No line was read, because nothing mathes the current key
|
||||
/** Maybe the key is "include file" */
|
||||
template <typename T>
|
||||
void unknownKey(T& v) {
|
||||
if (key == _("include file")) {
|
||||
Reader reader(this, package, value, ignore_invalid);
|
||||
reader.handle_greedy(v);
|
||||
moveNext();
|
||||
} else {
|
||||
unknownKey();
|
||||
}
|
||||
}
|
||||
void unknownKey();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Container types
|
||||
@@ -200,7 +200,7 @@ class Reader {
|
||||
*/
|
||||
template <typename T>
|
||||
intrusive_ptr<T> read_new(Reader& reader) {
|
||||
return intrusive(new T());
|
||||
return intrusive(new T());
|
||||
}
|
||||
|
||||
/// Update the 'index' member of a value for use by IndexMap
|
||||
@@ -208,92 +208,92 @@ template <typename T> void update_index(T&, size_t index) {}
|
||||
|
||||
template <typename T>
|
||||
void Reader::handle(const Char* name, vector<T>& vector) {
|
||||
String vectorKey = singular_form(name);
|
||||
while (enterBlock(vectorKey)) {
|
||||
vector.resize(vector.size() + 1);
|
||||
handle_greedy(vector.back());
|
||||
update_index(vector.back(), vector.size() - 1); // update index for IndexMap
|
||||
exitBlock();
|
||||
}
|
||||
String vectorKey = singular_form(name);
|
||||
while (enterBlock(vectorKey)) {
|
||||
vector.resize(vector.size() + 1);
|
||||
handle_greedy(vector.back());
|
||||
update_index(vector.back(), vector.size() - 1); // update index for IndexMap
|
||||
exitBlock();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Reader::handle(intrusive_ptr<T>& pointer) {
|
||||
if (!pointer) pointer = read_new<T>(*this);
|
||||
handle(*pointer);
|
||||
if (!pointer) pointer = read_new<T>(*this);
|
||||
handle(*pointer);
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
void Reader::handle(map<String, V>& m) {
|
||||
while (enterAnyBlock()) {
|
||||
handle_greedy(m[key]);
|
||||
exitBlock();
|
||||
}
|
||||
while (enterAnyBlock()) {
|
||||
handle_greedy(m[key]);
|
||||
exitBlock();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
void Reader::handle(IndexMap<K,V>& m) {
|
||||
for (typename IndexMap<K,V>::iterator it = m.begin() ; it != m.end() ; ++it) {
|
||||
handle(get_key_name(*it).c_str(), *it);
|
||||
}
|
||||
for (typename IndexMap<K,V>::iterator it = m.begin() ; it != m.end() ; ++it) {
|
||||
handle(get_key_name(*it).c_str(), *it);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Reflection
|
||||
|
||||
/// Implement reflection as used by Reader
|
||||
#define REFLECT_OBJECT_READER(Cls) \
|
||||
template<> void Reader::handle<Cls>(Cls& object) { \
|
||||
object.reflect(*this); \
|
||||
} \
|
||||
void Cls::reflect(Reader& reader) { \
|
||||
reflect_impl(reader); \
|
||||
}
|
||||
#define REFLECT_OBJECT_READER(Cls) \
|
||||
template<> void Reader::handle<Cls>(Cls& object) { \
|
||||
object.reflect(*this); \
|
||||
} \
|
||||
void Cls::reflect(Reader& reader) { \
|
||||
reflect_impl(reader); \
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Reflection for enumerations
|
||||
|
||||
/// Implement enum reflection as used by Reader
|
||||
#define REFLECT_ENUM_READER(Enum) \
|
||||
template<> void Reader::handle<Enum>(Enum& enum_) { \
|
||||
EnumReader reader(getValue()); \
|
||||
reflect_ ## Enum(enum_, reader); \
|
||||
reader.warnIfNotDone(this); \
|
||||
} \
|
||||
void parse_enum(const String& value, Enum& out) { \
|
||||
EnumReader reader(value); \
|
||||
reflect_ ## Enum(out, reader); \
|
||||
reader.errorIfNotDone(); \
|
||||
}
|
||||
#define REFLECT_ENUM_READER(Enum) \
|
||||
template<> void Reader::handle<Enum>(Enum& enum_) { \
|
||||
EnumReader reader(getValue()); \
|
||||
reflect_ ## Enum(enum_, reader); \
|
||||
reader.warnIfNotDone(this); \
|
||||
} \
|
||||
void parse_enum(const String& value, Enum& out) { \
|
||||
EnumReader reader(value); \
|
||||
reflect_ ## Enum(out, reader); \
|
||||
reader.errorIfNotDone(); \
|
||||
}
|
||||
|
||||
/// 'Tag' to be used when reflecting enumerations for Reader
|
||||
class EnumReader {
|
||||
public:
|
||||
inline EnumReader(String read)
|
||||
: read(read), first(nullptr), done(false) {}
|
||||
|
||||
/// Handle a possible value for the enum, if the name matches the name in the input
|
||||
template <typename Enum>
|
||||
inline void handle(const Char* name, Enum value, Enum& enum_) {
|
||||
if (!done) {
|
||||
if (read == name) {
|
||||
done = true;
|
||||
enum_ = value;
|
||||
} else if (!first) {
|
||||
first = name;
|
||||
enum_ = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool isDone() const { return done; }
|
||||
void warnIfNotDone(Reader* errors_to);
|
||||
void errorIfNotDone();
|
||||
|
||||
inline EnumReader(String read)
|
||||
: read(read), first(nullptr), done(false) {}
|
||||
|
||||
/// Handle a possible value for the enum, if the name matches the name in the input
|
||||
template <typename Enum>
|
||||
inline void handle(const Char* name, Enum value, Enum& enum_) {
|
||||
if (!done) {
|
||||
if (read == name) {
|
||||
done = true;
|
||||
enum_ = value;
|
||||
} else if (!first) {
|
||||
first = name;
|
||||
enum_ = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool isDone() const { return done; }
|
||||
void warnIfNotDone(Reader* errors_to);
|
||||
void errorIfNotDone();
|
||||
|
||||
private:
|
||||
String read; ///< The string to match to a value name
|
||||
Char const* first; ///< Has the first (default) value been handled? If so, what is its name.
|
||||
bool done; ///< Was anything matched?
|
||||
|
||||
String notDoneErrorMessage() const;
|
||||
String read; ///< The string to match to a value name
|
||||
Char const* first; ///< Has the first (default) value been handled? If so, what is its name.
|
||||
bool done; ///< Was anything matched?
|
||||
|
||||
String notDoneErrorMessage() const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+86
-86
@@ -18,133 +18,133 @@ using boost::tribool;
|
||||
// ----------------------------------------------------------------------------- : Writer
|
||||
|
||||
Writer::Writer(const OutputStreamP& output, Version file_app_version)
|
||||
: indentation(0)
|
||||
, output(output), stream(*output)
|
||||
: indentation(0)
|
||||
, output(output), stream(*output)
|
||||
{
|
||||
stream.WriteString(BYTE_ORDER_MARK);
|
||||
handle(_("mse_version"), file_app_version);
|
||||
stream.WriteString(BYTE_ORDER_MARK);
|
||||
handle(_("mse_version"), file_app_version);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Writer::enterBlock(const Char* name) {
|
||||
// don't write the key yet
|
||||
pending_opened.push_back(name);
|
||||
// don't write the key yet
|
||||
pending_opened.push_back(name);
|
||||
}
|
||||
|
||||
void Writer::exitBlock() {
|
||||
if (pending_opened.empty()) {
|
||||
assert(indentation > 0);
|
||||
indentation -= 1;
|
||||
} else {
|
||||
// this block was apparently empty, ignore it
|
||||
pending_opened.pop_back();
|
||||
}
|
||||
if (pending_opened.empty()) {
|
||||
assert(indentation > 0);
|
||||
indentation -= 1;
|
||||
} else {
|
||||
// this block was apparently empty, ignore it
|
||||
pending_opened.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void Writer::writePending() {
|
||||
// In enterBlock we have delayed the actual writing of the keys until this point
|
||||
// here we write all the pending keys, and increase indentation along the way.
|
||||
for (size_t i = 0 ; i < pending_opened.size() ; ++i) {
|
||||
if (i > 0) {
|
||||
// before entering a sub-block, write a colon after the parent's name
|
||||
stream.WriteString(_(":\n"));
|
||||
}
|
||||
indentation += 1;
|
||||
writeIndentation();
|
||||
writeUTF8(stream, canonical_name_form(pending_opened[i]));
|
||||
}
|
||||
pending_opened.clear();
|
||||
// In enterBlock we have delayed the actual writing of the keys until this point
|
||||
// here we write all the pending keys, and increase indentation along the way.
|
||||
for (size_t i = 0 ; i < pending_opened.size() ; ++i) {
|
||||
if (i > 0) {
|
||||
// before entering a sub-block, write a colon after the parent's name
|
||||
stream.WriteString(_(":\n"));
|
||||
}
|
||||
indentation += 1;
|
||||
writeIndentation();
|
||||
writeUTF8(stream, canonical_name_form(pending_opened[i]));
|
||||
}
|
||||
pending_opened.clear();
|
||||
}
|
||||
|
||||
void Writer::writeIndentation() {
|
||||
for(int i = 1 ; i < indentation ; ++i) {
|
||||
stream.PutChar(_('\t'));
|
||||
}
|
||||
for(int i = 1 ; i < indentation ; ++i) {
|
||||
stream.PutChar(_('\t'));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Handling basic types
|
||||
|
||||
void Writer::handle(const String& value) {
|
||||
if (pending_opened.empty()) {
|
||||
throw InternalError(_("Can only write a value in a key that was just opened"));
|
||||
}
|
||||
writePending();
|
||||
// write indentation and key
|
||||
if (value.find_first_of(_('\n')) != String::npos || (!value.empty() && isSpace(value.GetChar(0)))) {
|
||||
// multiline string, or contains leading whitespace
|
||||
stream.WriteString(_(":\n"));
|
||||
indentation += 1;
|
||||
// split lines, and write each line
|
||||
size_t start = 0, end, size = value.size();
|
||||
while (start < size) {
|
||||
end = value.find_first_of(_("\n\r"), start); // until end of line
|
||||
// write the line
|
||||
writeIndentation();
|
||||
writeUTF8(stream, value.substr(start, end - start));
|
||||
// Skip \r and \n
|
||||
if (end == String::npos) break;
|
||||
stream.PutChar(_('\n'));
|
||||
start = end + 1;
|
||||
if (start < size) {
|
||||
Char c1 = value.GetChar(start - 1);
|
||||
Char c2 = value.GetChar(start);
|
||||
// skip second character of \r\n or \n\r
|
||||
if (c1 != c2 && (c2 == _('\r') || c2 == _('\n'))) start += 1;
|
||||
}
|
||||
}
|
||||
indentation -= 1;
|
||||
} else {
|
||||
stream.WriteString(_(": "));
|
||||
writeUTF8(stream, value);
|
||||
}
|
||||
stream.PutChar(_('\n'));
|
||||
if (pending_opened.empty()) {
|
||||
throw InternalError(_("Can only write a value in a key that was just opened"));
|
||||
}
|
||||
writePending();
|
||||
// write indentation and key
|
||||
if (value.find_first_of(_('\n')) != String::npos || (!value.empty() && isSpace(value.GetChar(0)))) {
|
||||
// multiline string, or contains leading whitespace
|
||||
stream.WriteString(_(":\n"));
|
||||
indentation += 1;
|
||||
// split lines, and write each line
|
||||
size_t start = 0, end, size = value.size();
|
||||
while (start < size) {
|
||||
end = value.find_first_of(_("\n\r"), start); // until end of line
|
||||
// write the line
|
||||
writeIndentation();
|
||||
writeUTF8(stream, value.substr(start, end - start));
|
||||
// Skip \r and \n
|
||||
if (end == String::npos) break;
|
||||
stream.PutChar(_('\n'));
|
||||
start = end + 1;
|
||||
if (start < size) {
|
||||
Char c1 = value.GetChar(start - 1);
|
||||
Char c2 = value.GetChar(start);
|
||||
// skip second character of \r\n or \n\r
|
||||
if (c1 != c2 && (c2 == _('\r') || c2 == _('\n'))) start += 1;
|
||||
}
|
||||
}
|
||||
indentation -= 1;
|
||||
} else {
|
||||
stream.WriteString(_(": "));
|
||||
writeUTF8(stream, value);
|
||||
}
|
||||
stream.PutChar(_('\n'));
|
||||
}
|
||||
|
||||
template <> void Writer::handle(const int& value) {
|
||||
handle(String() << value);
|
||||
handle(String() << value);
|
||||
}
|
||||
template <> void Writer::handle(const unsigned int& value) {
|
||||
handle(String() << value);
|
||||
handle(String() << value);
|
||||
}
|
||||
template <> void Writer::handle(const double& value) {
|
||||
handle(String() << value);
|
||||
handle(String() << value);
|
||||
}
|
||||
template <> void Writer::handle(const bool& value) {
|
||||
handle(value ? _("true") : _("false"));
|
||||
handle(value ? _("true") : _("false"));
|
||||
}
|
||||
template <> void Writer::handle(const tribool& value) {
|
||||
if (!indeterminate(value)) {
|
||||
handle(value ? _("true") : _("false"));
|
||||
}
|
||||
if (!indeterminate(value)) {
|
||||
handle(value ? _("true") : _("false"));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Handling less basic util types
|
||||
|
||||
template <> void Writer::handle(const wxDateTime& date) {
|
||||
if (date.IsValid()) {
|
||||
handle(date.Format(_("%Y-%m-%d %H:%M:%S")));
|
||||
}
|
||||
if (date.IsValid()) {
|
||||
handle(date.Format(_("%Y-%m-%d %H:%M:%S")));
|
||||
}
|
||||
}
|
||||
template <> void Writer::handle(const Vector2D& vec) {
|
||||
handle(String::Format(_("(%.10lf,%.10lf)"), vec.x, vec.y));
|
||||
handle(String::Format(_("(%.10lf,%.10lf)"), vec.x, vec.y));
|
||||
}
|
||||
template <> void Writer::handle(const Color& col) {
|
||||
handle(String::Format(_("rgb(%u,%u,%u)"), col.Red(), col.Green(), col.Blue()));
|
||||
handle(String::Format(_("rgb(%u,%u,%u)"), col.Red(), col.Green(), col.Blue()));
|
||||
}
|
||||
|
||||
template <> void Writer::handle(const FileName& value) {
|
||||
if (clipboard_package() && !value.empty()) {
|
||||
// use absolute names on clipboard
|
||||
try {
|
||||
handle(clipboard_package()->absoluteName(value));
|
||||
} catch (const Error&) {
|
||||
// ignore errors
|
||||
}
|
||||
} else {
|
||||
handle(static_cast<const String&>(value));
|
||||
if (writing_package()) {
|
||||
writing_package()->referenceFile(value);
|
||||
}
|
||||
}
|
||||
if (clipboard_package() && !value.empty()) {
|
||||
// use absolute names on clipboard
|
||||
try {
|
||||
handle(clipboard_package()->absoluteName(value));
|
||||
} catch (const Error&) {
|
||||
// ignore errors
|
||||
}
|
||||
} else {
|
||||
handle(static_cast<const String&>(value));
|
||||
if (writing_package()) {
|
||||
writing_package()->referenceFile(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+105
-105
@@ -25,145 +25,145 @@ typedef shared_ptr<wxOutputStream> OutputStreamP;
|
||||
/// The Writer can be used for writing (serializing) objects
|
||||
class Writer {
|
||||
public:
|
||||
/// Construct a writer that writes to the given output stream
|
||||
Writer(const OutputStreamP& output, Version file_app_version);
|
||||
|
||||
/// Tell the reflection code we are not reading
|
||||
inline bool reading() const { return false; }
|
||||
inline bool scripting() const { return false; }
|
||||
inline bool isComplex() const { return true; }
|
||||
inline void addAlias(int, const Char*, const Char*) {}
|
||||
inline void handleIgnore(int, const Char*) {}
|
||||
|
||||
// --------------------------------------------------- : Handling objects
|
||||
/// Handle an object: write it under the given name
|
||||
template <typename T>
|
||||
void handle(const Char* name, const T& object) {
|
||||
enterBlock(name);
|
||||
handle(object);
|
||||
exitBlock();
|
||||
}
|
||||
/// Handle a value
|
||||
template <typename T>
|
||||
inline void handleNoScript(const Char* name, T& value) { handle(name,value); }
|
||||
|
||||
/// Write a vector to the output stream
|
||||
template <typename T>
|
||||
void handle(const Char* name, const vector<T>& vector);
|
||||
|
||||
/// Write a string to the output stream
|
||||
void handle(const String& str);
|
||||
void handle(const Char* str) { handle(String(str)); }
|
||||
|
||||
/// Write an object of type T to the output stream
|
||||
template <typename T> void handle(const T&);
|
||||
/// Write a intrusive_ptr to the output stream
|
||||
template <typename T> void handle(const intrusive_ptr<T>&);
|
||||
/// Write a map to the output stream
|
||||
template <typename K, typename V> void handle(const map<K,V>&);
|
||||
/// Write an IndexMap to the output stream
|
||||
template <typename K, typename V> void handle(const IndexMap<K,V>&);
|
||||
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&);
|
||||
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>&);
|
||||
/// Write an object of type Defaultable<T> to the output stream
|
||||
template <typename T> void handle(const Defaultable<T>&);
|
||||
/// Write an object of type Scriptable<T> to the output stream
|
||||
template <typename T> void handle(const Scriptable<T>&);
|
||||
// special behaviour
|
||||
void handle(const GameP&);
|
||||
void handle(const StyleSheetP&);
|
||||
|
||||
/// Construct a writer that writes to the given output stream
|
||||
Writer(const OutputStreamP& output, Version file_app_version);
|
||||
|
||||
/// Tell the reflection code we are not reading
|
||||
inline bool reading() const { return false; }
|
||||
inline bool scripting() const { return false; }
|
||||
inline bool isComplex() const { return true; }
|
||||
inline void addAlias(int, const Char*, const Char*) {}
|
||||
inline void handleIgnore(int, const Char*) {}
|
||||
|
||||
// --------------------------------------------------- : Handling objects
|
||||
/// Handle an object: write it under the given name
|
||||
template <typename T>
|
||||
void handle(const Char* name, const T& object) {
|
||||
enterBlock(name);
|
||||
handle(object);
|
||||
exitBlock();
|
||||
}
|
||||
/// Handle a value
|
||||
template <typename T>
|
||||
inline void handleNoScript(const Char* name, T& value) { handle(name,value); }
|
||||
|
||||
/// Write a vector to the output stream
|
||||
template <typename T>
|
||||
void handle(const Char* name, const vector<T>& vector);
|
||||
|
||||
/// Write a string to the output stream
|
||||
void handle(const String& str);
|
||||
void handle(const Char* str) { handle(String(str)); }
|
||||
|
||||
/// Write an object of type T to the output stream
|
||||
template <typename T> void handle(const T&);
|
||||
/// Write a intrusive_ptr to the output stream
|
||||
template <typename T> void handle(const intrusive_ptr<T>&);
|
||||
/// Write a map to the output stream
|
||||
template <typename K, typename V> void handle(const map<K,V>&);
|
||||
/// Write an IndexMap to the output stream
|
||||
template <typename K, typename V> void handle(const IndexMap<K,V>&);
|
||||
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&);
|
||||
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>&);
|
||||
/// Write an object of type Defaultable<T> to the output stream
|
||||
template <typename T> void handle(const Defaultable<T>&);
|
||||
/// Write an object of type Scriptable<T> to the output stream
|
||||
template <typename T> void handle(const Scriptable<T>&);
|
||||
// special behaviour
|
||||
void handle(const GameP&);
|
||||
void handle(const StyleSheetP&);
|
||||
|
||||
private:
|
||||
// --------------------------------------------------- : Data
|
||||
/// Indentation of the current block
|
||||
int indentation;
|
||||
/// Blocks opened to which nothing has been written
|
||||
vector<const Char*> pending_opened;
|
||||
|
||||
/// Output stream we are writing to
|
||||
OutputStreamP output;
|
||||
/// Text stream wrapping the output stream
|
||||
wxTextOutputStream stream;
|
||||
|
||||
// --------------------------------------------------- : Writing to the stream
|
||||
|
||||
/// Start a new block with the given name
|
||||
void enterBlock(const Char* name);
|
||||
/// Leave the block we are in
|
||||
void exitBlock();
|
||||
|
||||
/// Write the pending_opened with the required indentation
|
||||
void writePending();
|
||||
/// Output some taps to represent the indentation level
|
||||
void writeIndentation();
|
||||
// --------------------------------------------------- : Data
|
||||
/// Indentation of the current block
|
||||
int indentation;
|
||||
/// Blocks opened to which nothing has been written
|
||||
vector<const Char*> pending_opened;
|
||||
|
||||
/// Output stream we are writing to
|
||||
OutputStreamP output;
|
||||
/// Text stream wrapping the output stream
|
||||
wxTextOutputStream stream;
|
||||
|
||||
// --------------------------------------------------- : Writing to the stream
|
||||
|
||||
/// Start a new block with the given name
|
||||
void enterBlock(const Char* name);
|
||||
/// Leave the block we are in
|
||||
void exitBlock();
|
||||
|
||||
/// Write the pending_opened with the required indentation
|
||||
void writePending();
|
||||
/// Output some taps to represent the indentation level
|
||||
void writeIndentation();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Container types
|
||||
|
||||
template <typename T>
|
||||
void Writer::handle(const Char* name, const vector<T>& vec) {
|
||||
String vectorKey = singular_form(name);
|
||||
for (typename vector<T>::const_iterator it = vec.begin() ; it != vec.end() ; ++it) {
|
||||
handle(vectorKey, *it);
|
||||
}
|
||||
String vectorKey = singular_form(name);
|
||||
for (typename vector<T>::const_iterator it = vec.begin() ; it != vec.end() ; ++it) {
|
||||
handle(vectorKey, *it);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Writer::handle(const intrusive_ptr<T>& pointer) {
|
||||
if (pointer) handle(*pointer);
|
||||
if (pointer) handle(*pointer);
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
void Writer::handle(const map<K,V>& m) {
|
||||
for (typename map<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
|
||||
handle(it->first.c_str(), it->second);
|
||||
}
|
||||
for (typename map<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
|
||||
handle(it->first.c_str(), it->second);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
void Writer::handle(const IndexMap<K,V>& m) {
|
||||
for (typename IndexMap<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
|
||||
handle(get_key_name(*it).c_str(), *it);
|
||||
}
|
||||
for (typename IndexMap<K,V>::const_iterator it = m.begin() ; it != m.end() ; ++it) {
|
||||
handle(get_key_name(*it).c_str(), *it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Reflection
|
||||
|
||||
/// Implement reflection as used by Writer
|
||||
#define REFLECT_OBJECT_WRITER(Cls) \
|
||||
template<> void Writer::handle<Cls>(const Cls& object) { \
|
||||
const_cast<Cls&>(object).reflect(*this); \
|
||||
} \
|
||||
void Cls::reflect(Writer& writer) { \
|
||||
reflect_impl(writer); \
|
||||
}
|
||||
#define REFLECT_OBJECT_WRITER(Cls) \
|
||||
template<> void Writer::handle<Cls>(const Cls& object) { \
|
||||
const_cast<Cls&>(object).reflect(*this); \
|
||||
} \
|
||||
void Cls::reflect(Writer& writer) { \
|
||||
reflect_impl(writer); \
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Reflection for enumerations
|
||||
|
||||
/// Implement enum reflection as used by Writer
|
||||
#define REFLECT_ENUM_WRITER(Enum) \
|
||||
template<> void Writer::handle<Enum>(const Enum& enum_) { \
|
||||
EnumWriter writer(*this); \
|
||||
reflect_ ## Enum(const_cast<Enum&>(enum_), writer); \
|
||||
}
|
||||
#define REFLECT_ENUM_WRITER(Enum) \
|
||||
template<> void Writer::handle<Enum>(const Enum& enum_) { \
|
||||
EnumWriter writer(*this); \
|
||||
reflect_ ## Enum(const_cast<Enum&>(enum_), writer); \
|
||||
}
|
||||
|
||||
/// 'Tag' to be used when reflecting enumerations for Writer
|
||||
class EnumWriter {
|
||||
public:
|
||||
inline EnumWriter(Writer& writer)
|
||||
: writer(writer) {}
|
||||
|
||||
/// Handle a possible value for the enum, if the name matches the name in the input
|
||||
template <typename Enum>
|
||||
inline void handle(const Char* name, Enum value, Enum enum_) {
|
||||
if (enum_ == value) {
|
||||
writer.handle(name);
|
||||
}
|
||||
}
|
||||
|
||||
inline EnumWriter(Writer& writer)
|
||||
: writer(writer) {}
|
||||
|
||||
/// Handle a possible value for the enum, if the name matches the name in the input
|
||||
template <typename Enum>
|
||||
inline void handle(const Char* name, Enum value, Enum enum_) {
|
||||
if (enum_ == value) {
|
||||
writer.handle(name);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Writer& writer; ///< The writer to write output to
|
||||
Writer& writer; ///< The writer to write output to
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+31
-31
@@ -27,17 +27,17 @@ class SymbolFont;
|
||||
// ----------------------------------------------------------------------------- : Localisation macros
|
||||
|
||||
enum LocaleCategory
|
||||
{ LOCALE_CAT_MENU
|
||||
, LOCALE_CAT_HELP
|
||||
, LOCALE_CAT_TOOL
|
||||
, LOCALE_CAT_TOOLTIP
|
||||
, LOCALE_CAT_LABEL
|
||||
, LOCALE_CAT_BUTTON
|
||||
, LOCALE_CAT_TITLE
|
||||
, LOCALE_CAT_TYPE
|
||||
, LOCALE_CAT_ACTION
|
||||
, LOCALE_CAT_ERROR
|
||||
, LOCALE_CAT_MAX
|
||||
{ LOCALE_CAT_MENU
|
||||
, LOCALE_CAT_HELP
|
||||
, LOCALE_CAT_TOOL
|
||||
, LOCALE_CAT_TOOLTIP
|
||||
, LOCALE_CAT_LABEL
|
||||
, LOCALE_CAT_BUTTON
|
||||
, LOCALE_CAT_TITLE
|
||||
, LOCALE_CAT_TYPE
|
||||
, LOCALE_CAT_ACTION
|
||||
, LOCALE_CAT_ERROR
|
||||
, LOCALE_CAT_MAX
|
||||
};
|
||||
|
||||
typedef String (*DefaultLocaleFun)(const String&);
|
||||
@@ -75,37 +75,37 @@ String tr(const Package&, const String& subcat, const String& key, DefaultLocale
|
||||
#define _ERROR_(s) tr(LOCALE_CAT_ERROR, _(s))
|
||||
|
||||
/// A localized string for menus, with 1 argument (printf style)
|
||||
#define _MENU_1_(s,a) format_string(_MENU_(s), a)
|
||||
#define _MENU_1_(s,a) format_string(_MENU_(s), a)
|
||||
/// A localized string for context menus, contains no "\tshortcut"
|
||||
#define _CONTEXT_MENU_(s) remove_shortcut(_MENU_(s))
|
||||
|
||||
/// A localized string for tooltip text, with 1 argument (printf style)
|
||||
#define _HELP_1_(s,a) format_string(_HELP_(s), a)
|
||||
#define _HELP_1_(s,a) format_string(_HELP_(s), a)
|
||||
|
||||
/// A localized string for tooltip text, with 1 argument (printf style)
|
||||
#define _TOOLTIP_1_(s,a) format_string(_TOOLTIP_(s), a)
|
||||
#define _TOOLTIP_1_(s,a) format_string(_TOOLTIP_(s), a)
|
||||
|
||||
/// A localized string for tooltip labels, with 1 argument (printf style)
|
||||
#define _LABEL_1_(s,a) format_string(_LABEL_(s), a)
|
||||
#define _LABEL_1_(s,a) format_string(_LABEL_(s), a)
|
||||
|
||||
/// A localized string for button text, with 1 argument (printf style)
|
||||
#define _BUTTON_1_(s,a) format_string(_BUTTON_(s), a)
|
||||
#define _BUTTON_1_(s,a) format_string(_BUTTON_(s), a)
|
||||
|
||||
/// A localized string for window titles, with 1 argument (printf style)
|
||||
#define _TITLE_1_(s,a) format_string(_TITLE_(s), a)
|
||||
#define _TITLE_1_(s,a) format_string(_TITLE_(s), a)
|
||||
|
||||
/// A localized string for type names in scripts, with 1 argument (printf style)
|
||||
#define _TYPE_1_(s,a) format_string(_TYPE_(s), a)
|
||||
#define _TYPE_1_(s,a) format_string(_TYPE_(s), a)
|
||||
|
||||
/// A localized string for action names, with 1 argument (printf style)
|
||||
#define _ACTION_1_(s,a) format_string(_ACTION_(s), a)
|
||||
#define _ACTION_1_(s,a) format_string(_ACTION_(s), a)
|
||||
|
||||
/// A localized string for error messages, with 1 argument (printf style)
|
||||
#define _ERROR_1_(s,a) format_string(_ERROR_(s), a)
|
||||
#define _ERROR_1_(s,a) format_string(_ERROR_(s), a)
|
||||
/// A localized string for error messages, with 2 argument (printf style)
|
||||
#define _ERROR_2_(s,a,b) format_string(_ERROR_(s), a, b)
|
||||
#define _ERROR_2_(s,a,b) format_string(_ERROR_(s), a, b)
|
||||
/// A localized string for error messages, with 3 argument (printf style)
|
||||
#define _ERROR_3_(s,a,b,c) format_string(_ERROR_(s), a, b, c)
|
||||
#define _ERROR_3_(s,a,b,c) format_string(_ERROR_(s), a, b, c)
|
||||
/// A localized string for error messages, with 4 argument (printf style)
|
||||
#define _ERROR_4_(s,a,b,c,d) format_string(_ERROR_(s), a, b, c, d)
|
||||
|
||||
@@ -113,23 +113,23 @@ String tr(const Package&, const String& subcat, const String& key, DefaultLocale
|
||||
/** Equivalent to sprintf / String::Format, but allows strings to be passed as arguments (gcc)
|
||||
*/
|
||||
inline String format_string(const String& format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
String res = String::Format(format, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
String res = String::Format(format, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
inline String format_string(const String& format, const String& a0) {
|
||||
return String::Format(format, a0.c_str());
|
||||
return String::Format(format, a0.c_str());
|
||||
}
|
||||
inline String format_string(const String& format, const String& a0, const String& a1) {
|
||||
return String::Format(format, a0.c_str(), a1.c_str());
|
||||
return String::Format(format, a0.c_str(), a1.c_str());
|
||||
}
|
||||
inline String format_string(const String& format, const String& a0, const String& a1, const String& a2) {
|
||||
return String::Format(format, a0.c_str(), a1.c_str(), a2.c_str());
|
||||
return String::Format(format, a0.c_str(), a1.c_str(), a2.c_str());
|
||||
}
|
||||
inline String format_string(const String& format, const String& a0, const String& a1, const String& a2, const String& a3) {
|
||||
return String::Format(format, a0.c_str(), a1.c_str(), a2.c_str(), a3.c_str());
|
||||
return String::Format(format, a0.c_str(), a1.c_str(), a2.c_str(), a3.c_str());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+44
-44
@@ -18,69 +18,69 @@
|
||||
template <typename T>
|
||||
class OrderCache : public IntrusivePtrBase<OrderCache<T> > {
|
||||
public:
|
||||
/// Initialize the order cache, ordering the keys by their string values from the other vector
|
||||
/** Optionally filter the list using a vector of booleans of items to keep (note: vector<bool> is evil)
|
||||
* @pre keys.size() == values.size()
|
||||
*/
|
||||
OrderCache(const vector<T>& keys, const vector<String>& values, vector<int>* keep = nullptr);
|
||||
|
||||
/// Find the position of the given key in the cache, returns -1 if not found
|
||||
int find(const T& key) const;
|
||||
|
||||
/// Initialize the order cache, ordering the keys by their string values from the other vector
|
||||
/** Optionally filter the list using a vector of booleans of items to keep (note: vector<bool> is evil)
|
||||
* @pre keys.size() == values.size()
|
||||
*/
|
||||
OrderCache(const vector<T>& keys, const vector<String>& values, vector<int>* keep = nullptr);
|
||||
|
||||
/// Find the position of the given key in the cache, returns -1 if not found
|
||||
int find(const T& key) const;
|
||||
|
||||
private:
|
||||
struct CompareKeys;
|
||||
struct CompareValues;
|
||||
typedef pair<void*,int> KV;
|
||||
vector<KV> positions;
|
||||
struct CompareKeys;
|
||||
struct CompareValues;
|
||||
typedef pair<void*,int> KV;
|
||||
vector<KV> positions;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Implementation
|
||||
|
||||
template <typename T>
|
||||
struct OrderCache<T>::CompareKeys {
|
||||
inline bool operator () (const KV& a, void* b) { return a.first < b; }
|
||||
inline bool operator () (const KV& a, const KV& b) { return a.first < b.first; }
|
||||
inline bool operator () (void* a, const KV& b) { return a < b.first; }
|
||||
inline bool operator () (const KV& a, void* b) { return a.first < b; }
|
||||
inline bool operator () (const KV& a, const KV& b) { return a.first < b.first; }
|
||||
inline bool operator () (void* a, const KV& b) { return a < b.first; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct OrderCache<T>::CompareValues {
|
||||
const vector<String>& values;
|
||||
CompareValues(const vector<String>& values) : values(values) {}
|
||||
|
||||
inline bool operator () (const KV& a, const KV& b) {
|
||||
return smart_less(values[a.second], values[b.second]);
|
||||
}
|
||||
const vector<String>& values;
|
||||
CompareValues(const vector<String>& values) : values(values) {}
|
||||
|
||||
inline bool operator () (const KV& a, const KV& b) {
|
||||
return smart_less(values[a.second], values[b.second]);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
OrderCache<T>::OrderCache(const vector<T>& keys, const vector<String>& values, vector<int>* keep) {
|
||||
assert(keys.size() == values.size());
|
||||
assert(!keep || keep->size() == keys.size());
|
||||
// initialize positions, use pos to point back to the values vector
|
||||
positions.reserve(keys.size());
|
||||
int i = 0;
|
||||
for (typename vector<T>::const_iterator it = keys.begin() ; it != keys.end() ; ++it, ++i) {
|
||||
if (!keep || (*keep)[i]) {
|
||||
positions.push_back(KV(&**it, i));
|
||||
}
|
||||
}
|
||||
// sort the KVs by the values
|
||||
sort(positions.begin(), positions.end(), CompareValues(values));
|
||||
// update positions, to point to sorted list
|
||||
i = 0;
|
||||
for (typename vector<KV>::iterator it = positions.begin() ; it != positions.end() ; ++it, ++i) {
|
||||
it->second = i;
|
||||
}
|
||||
// sort the KVs by the keys
|
||||
sort(positions.begin(), positions.end(), CompareKeys());
|
||||
assert(keys.size() == values.size());
|
||||
assert(!keep || keep->size() == keys.size());
|
||||
// initialize positions, use pos to point back to the values vector
|
||||
positions.reserve(keys.size());
|
||||
int i = 0;
|
||||
for (typename vector<T>::const_iterator it = keys.begin() ; it != keys.end() ; ++it, ++i) {
|
||||
if (!keep || (*keep)[i]) {
|
||||
positions.push_back(KV(&**it, i));
|
||||
}
|
||||
}
|
||||
// sort the KVs by the values
|
||||
sort(positions.begin(), positions.end(), CompareValues(values));
|
||||
// update positions, to point to sorted list
|
||||
i = 0;
|
||||
for (typename vector<KV>::iterator it = positions.begin() ; it != positions.end() ; ++it, ++i) {
|
||||
it->second = i;
|
||||
}
|
||||
// sort the KVs by the keys
|
||||
sort(positions.begin(), positions.end(), CompareKeys());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int OrderCache<T>::find(const T& key) const {
|
||||
vector<KV>::const_iterator it = lower_bound(positions.begin(), positions.end(), &*key, CompareKeys());
|
||||
if (it == positions.end() || it->first != &*key) return -1;
|
||||
return it->second;
|
||||
vector<KV>::const_iterator it = lower_bound(positions.begin(), positions.end(), &*key, CompareKeys());
|
||||
if (it == positions.end() || it->first != &*key) return -1;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
// ----------------------------------------------------------------------------- : GCC
|
||||
|
||||
#ifdef __GNUC__
|
||||
|
||||
/// Absolute value of integers
|
||||
template <typename T>
|
||||
inline T abs(T a) { return a < 0 ? -a : a; }
|
||||
|
||||
|
||||
/// Absolute value of integers
|
||||
template <typename T>
|
||||
inline T abs(T a) { return a < 0 ? -a : a; }
|
||||
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+30
-30
@@ -15,10 +15,10 @@
|
||||
// ----------------------------------------------------------------------------- : Compiler specific
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning (disable: 4100) // unreferenced formal parameter
|
||||
# pragma warning (disable: 4355) // 'this' : used in base member initializer list
|
||||
# pragma warning (disable: 4800) // 'int' : forcing value to bool 'true' or 'false' (performance warning)
|
||||
# pragma warning (disable: 4675) // resolved overload was found by argument-dependent lookup (occurs in some boost header)
|
||||
# pragma warning (disable: 4100) // unreferenced formal parameter
|
||||
# pragma warning (disable: 4355) // 'this' : used in base member initializer list
|
||||
# pragma warning (disable: 4800) // 'int' : forcing value to bool 'true' or 'false' (performance warning)
|
||||
# pragma warning (disable: 4675) // resolved overload was found by argument-dependent lookup (occurs in some boost header)
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
@@ -64,22 +64,22 @@ typedef wxOutputStream OutputStream;
|
||||
// ----------------------------------------------------------------------------- : Compatability fixes
|
||||
|
||||
#if wxVERSION_NUMBER < 2805
|
||||
#define wxBORDER_THEME wxSUNKEN_BORDER
|
||||
#define wxBORDER_THEME wxSUNKEN_BORDER
|
||||
#endif
|
||||
#if wxVERSION_NUMBER < 2900
|
||||
// wx >= 2.9 requires the use of HandleWindowEvent on windows, instead of ProcessEvent
|
||||
#define HandleWindowEvent ProcessEvent
|
||||
// wx >= 2.9 requires the use of HandleWindowEvent on windows, instead of ProcessEvent
|
||||
#define HandleWindowEvent ProcessEvent
|
||||
#endif
|
||||
#if wxVERSION_NUMBER < 2700
|
||||
// is it worth it to still support wx2.6?
|
||||
#define wxFD_SAVE wxSAVE
|
||||
#define wxFD_OPEN wxOPEN
|
||||
#define wxFD_OVERWRITE_PROMPT wxOVERWRITE_PROMPT
|
||||
#define SetDeviceClippingRegion SetClippingRegion
|
||||
typedef wxEvent wxMouseCaptureLostEvent;
|
||||
#define EVT_MOUSE_CAPTURE_LOST(handler) // ignore
|
||||
#define wxEVT_MOUSE_CAPTURE_LOST 12345678 // not an actual event type
|
||||
#define wxAutoBufferedPaintDC wxBufferedPaintDC
|
||||
// is it worth it to still support wx2.6?
|
||||
#define wxFD_SAVE wxSAVE
|
||||
#define wxFD_OPEN wxOPEN
|
||||
#define wxFD_OVERWRITE_PROMPT wxOVERWRITE_PROMPT
|
||||
#define SetDeviceClippingRegion SetClippingRegion
|
||||
typedef wxEvent wxMouseCaptureLostEvent;
|
||||
#define EVT_MOUSE_CAPTURE_LOST(handler) // ignore
|
||||
#define wxEVT_MOUSE_CAPTURE_LOST 12345678 // not an actual event type
|
||||
#define wxAutoBufferedPaintDC wxBufferedPaintDC
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : Other aliasses
|
||||
@@ -93,8 +93,8 @@ typedef unsigned int UInt;
|
||||
/// A string standing for a filename, has different behaviour when reading/writing
|
||||
class FileName : public wxString {
|
||||
public:
|
||||
FileName() {}
|
||||
FileName(const wxString& s) : wxString(s) {}
|
||||
FileName() {}
|
||||
FileName(const wxString& s) : wxString(s) {}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : MSE Headers
|
||||
@@ -112,18 +112,18 @@ class FileName : public wxString {
|
||||
// ----------------------------------------------------------------------------- : Debugging fixes
|
||||
|
||||
#ifdef _MSC_VER
|
||||
//# pragma conform(forScope,on) // in "for(int x=..);" x goes out of scope after the for
|
||||
// somehow forScope pragma doesn't work in precompiled headers, use this hack instead:
|
||||
#ifdef _DEBUG
|
||||
#define for if(false);else for
|
||||
#endif
|
||||
|
||||
#if defined(_DEBUG) && defined(_CRT_WIDE)
|
||||
// Use OutputDebugString/DebugBreak for assertions if in debug mode
|
||||
void msvc_assert(const wchar_t*, const wchar_t*, const wchar_t*, unsigned);
|
||||
#undef assert
|
||||
#define assert(exp) (void)( (exp) || (msvc_assert(nullptr, _CRT_WIDE(#exp), _CRT_WIDE(__FILE__), __LINE__), 0) )
|
||||
#endif
|
||||
//# pragma conform(forScope,on) // in "for(int x=..);" x goes out of scope after the for
|
||||
// somehow forScope pragma doesn't work in precompiled headers, use this hack instead:
|
||||
#ifdef _DEBUG
|
||||
#define for if(false);else for
|
||||
#endif
|
||||
|
||||
#if defined(_DEBUG) && defined(_CRT_WIDE)
|
||||
// Use OutputDebugString/DebugBreak for assertions if in debug mode
|
||||
void msvc_assert(const wchar_t*, const wchar_t*, const wchar_t*, unsigned);
|
||||
#undef assert
|
||||
#define assert(exp) (void)( (exp) || (msvc_assert(nullptr, _CRT_WIDE(#exp), _CRT_WIDE(__FILE__), __LINE__), 0) )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+124
-124
@@ -27,53 +27,53 @@ typedef Vector2D RealPoint;
|
||||
/// A size (width,height) using real (double) coordinates
|
||||
class RealSize {
|
||||
public:
|
||||
double width;
|
||||
double height;
|
||||
|
||||
inline RealSize()
|
||||
: width(0), height(0)
|
||||
{}
|
||||
inline RealSize(double w, double h)
|
||||
: width(w), height(h)
|
||||
{}
|
||||
inline RealSize(wxSize s)
|
||||
: width(s.x), height(s.y)
|
||||
{}
|
||||
inline explicit RealSize(const Vector2D& v)
|
||||
: width(v.x), height(v.y)
|
||||
{}
|
||||
/// size of an image
|
||||
inline explicit RealSize(const wxImage& img)
|
||||
: width(img.GetWidth()), height(img.GetHeight())
|
||||
{}
|
||||
/// size of a bitmap
|
||||
inline explicit RealSize(const wxBitmap& img)
|
||||
: width(img.GetWidth()), height(img.GetHeight())
|
||||
{}
|
||||
|
||||
/// Negation of a size, negates both components
|
||||
inline RealSize operator - () const {
|
||||
return RealSize(-width, -height);
|
||||
}
|
||||
|
||||
/// Multiplying a size by a scalar r, multiplies both components
|
||||
inline void operator *= (double r) {
|
||||
width *= r;
|
||||
height *= r;
|
||||
}
|
||||
/// Multiplying a size by a scalar r, multiplies both components
|
||||
inline RealSize operator * (double r) const {
|
||||
return RealSize(width * r, height * r);
|
||||
}
|
||||
/// Dividing a size by a scalar r, divides both components
|
||||
inline RealSize operator / (double r) const {
|
||||
return RealSize(width / r, height / r);
|
||||
}
|
||||
|
||||
/// Can be converted to a wxSize, with integer components
|
||||
inline operator wxSize() {
|
||||
return wxSize(to_int(width), to_int(height));
|
||||
}
|
||||
double width;
|
||||
double height;
|
||||
|
||||
inline RealSize()
|
||||
: width(0), height(0)
|
||||
{}
|
||||
inline RealSize(double w, double h)
|
||||
: width(w), height(h)
|
||||
{}
|
||||
inline RealSize(wxSize s)
|
||||
: width(s.x), height(s.y)
|
||||
{}
|
||||
inline explicit RealSize(const Vector2D& v)
|
||||
: width(v.x), height(v.y)
|
||||
{}
|
||||
/// size of an image
|
||||
inline explicit RealSize(const wxImage& img)
|
||||
: width(img.GetWidth()), height(img.GetHeight())
|
||||
{}
|
||||
/// size of a bitmap
|
||||
inline explicit RealSize(const wxBitmap& img)
|
||||
: width(img.GetWidth()), height(img.GetHeight())
|
||||
{}
|
||||
|
||||
/// Negation of a size, negates both components
|
||||
inline RealSize operator - () const {
|
||||
return RealSize(-width, -height);
|
||||
}
|
||||
|
||||
/// Multiplying a size by a scalar r, multiplies both components
|
||||
inline void operator *= (double r) {
|
||||
width *= r;
|
||||
height *= r;
|
||||
}
|
||||
/// Multiplying a size by a scalar r, multiplies both components
|
||||
inline RealSize operator * (double r) const {
|
||||
return RealSize(width * r, height * r);
|
||||
}
|
||||
/// Dividing a size by a scalar r, divides both components
|
||||
inline RealSize operator / (double r) const {
|
||||
return RealSize(width / r, height / r);
|
||||
}
|
||||
|
||||
/// Can be converted to a wxSize, with integer components
|
||||
inline operator wxSize() {
|
||||
return wxSize(to_int(width), to_int(height));
|
||||
}
|
||||
};
|
||||
|
||||
/// Add two sizes horizontally
|
||||
@@ -82,7 +82,7 @@ class RealSize {
|
||||
* #### ####...
|
||||
*/
|
||||
inline RealSize add_horizontal(const RealSize& a, const RealSize& b) {
|
||||
return RealSize(a.width + b.width, max(a.height, b.height));
|
||||
return RealSize(a.width + b.width, max(a.height, b.height));
|
||||
}
|
||||
|
||||
/// Add two sizes vertically
|
||||
@@ -93,7 +93,7 @@ inline RealSize add_horizontal(const RealSize& a, const RealSize& b) {
|
||||
* $$$.
|
||||
*/
|
||||
inline RealSize add_vertical(const RealSize& a, const RealSize& b) {
|
||||
return RealSize(max(a.width, b.width), a.height + b.height);
|
||||
return RealSize(max(a.width, b.width), a.height + b.height);
|
||||
}
|
||||
|
||||
/// Add two sizes diagonally
|
||||
@@ -104,22 +104,22 @@ inline RealSize add_vertical(const RealSize& a, const RealSize& b) {
|
||||
* ....$$$
|
||||
*/
|
||||
inline RealSize add_diagonal(const RealSize& a, const RealSize& b) {
|
||||
return RealSize(a.width + b.width, a.height + b.height);
|
||||
return RealSize(a.width + b.width, a.height + b.height);
|
||||
}
|
||||
|
||||
/// Piecewise minimum
|
||||
inline RealSize piecewise_min(const RealSize& a, const RealSize& b) {
|
||||
return RealSize(
|
||||
a.width < b.width ? a.width : b.width,
|
||||
a.height < b.height ? a.height : b.height
|
||||
);
|
||||
return RealSize(
|
||||
a.width < b.width ? a.width : b.width,
|
||||
a.height < b.height ? a.height : b.height
|
||||
);
|
||||
}
|
||||
/// Piecewise maximum
|
||||
inline RealSize piecewise_max(const RealSize& a, const RealSize& b) {
|
||||
return RealSize(
|
||||
a.width < b.width ? b.width : a.width,
|
||||
a.height < b.height ? b.height : a.height
|
||||
);
|
||||
return RealSize(
|
||||
a.width < b.width ? b.width : a.width,
|
||||
a.height < b.height ? b.height : a.height
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Rectangle using doubles
|
||||
@@ -127,62 +127,62 @@ inline RealSize piecewise_max(const RealSize& a, const RealSize& b) {
|
||||
/// A rectangle (postion and size) using real (double) coordinats
|
||||
class RealRect : private RealPoint, private RealSize {
|
||||
public:
|
||||
using RealPoint::x;
|
||||
using RealPoint::y;
|
||||
using RealSize::width;
|
||||
using RealSize::height;
|
||||
|
||||
inline RealRect(const wxRect& rect)
|
||||
: RealPoint(rect.x, rect.y), RealSize(rect.width, rect.height)
|
||||
{}
|
||||
inline RealRect(const RealPoint& position, const RealSize& size)
|
||||
: RealPoint(position), RealSize(size)
|
||||
{}
|
||||
inline RealRect(double x, double y, double w, double h)
|
||||
: RealPoint(x,y), RealSize(w,h)
|
||||
{}
|
||||
|
||||
/// Position of the top left corner
|
||||
inline RealPoint& position() { return *this; }
|
||||
inline const RealPoint& position() const { return *this; }
|
||||
/// Size of the rectangle
|
||||
inline RealSize& size() { return *this; }
|
||||
inline const RealSize& size() const { return *this; }
|
||||
|
||||
inline double left() const { return x; }
|
||||
inline double right() const { return x + width; }
|
||||
inline double top() const { return y; }
|
||||
inline double bottom() const { return y + height; }
|
||||
|
||||
inline RealPoint topLeft () const { return *this; }
|
||||
inline RealPoint topRight () const { return RealPoint(x + width, y); }
|
||||
inline RealPoint bottomLeft () const { return RealPoint(x, y + height); }
|
||||
inline RealPoint bottomRight() const { return RealPoint(x + width, y + height); }
|
||||
|
||||
/// Return a rectangle that is amount larger to all sides
|
||||
inline RealRect grow(double amount) {
|
||||
return RealRect(x - amount, y - amount, width + 2 * amount, height + 2 * amount);
|
||||
}
|
||||
/// Move the coordinates by some amount
|
||||
inline RealRect move(double dx, double dy, double dw, double dh) const {
|
||||
return RealRect(x + dx, y + dy, width + dw, height + dh);
|
||||
}
|
||||
|
||||
inline operator wxRect() const {
|
||||
// Prevent rounding errors, for example if
|
||||
// x = 0.6 and width = 0.6
|
||||
// the right = 1.2
|
||||
// so we want a rectangle from 0 to 1
|
||||
// not from 0 to 0
|
||||
int i_l = to_int(x), i_r = to_int(right());
|
||||
int i_t = to_int(y), i_b = to_int(bottom());
|
||||
return wxRect(i_l, i_t, i_r - i_l, i_b - i_t);
|
||||
}
|
||||
|
||||
/// Explicit conversion to wxRect, to not confuse gcc
|
||||
inline wxRect toRect() const {
|
||||
return *this;
|
||||
}
|
||||
using RealPoint::x;
|
||||
using RealPoint::y;
|
||||
using RealSize::width;
|
||||
using RealSize::height;
|
||||
|
||||
inline RealRect(const wxRect& rect)
|
||||
: RealPoint(rect.x, rect.y), RealSize(rect.width, rect.height)
|
||||
{}
|
||||
inline RealRect(const RealPoint& position, const RealSize& size)
|
||||
: RealPoint(position), RealSize(size)
|
||||
{}
|
||||
inline RealRect(double x, double y, double w, double h)
|
||||
: RealPoint(x,y), RealSize(w,h)
|
||||
{}
|
||||
|
||||
/// Position of the top left corner
|
||||
inline RealPoint& position() { return *this; }
|
||||
inline const RealPoint& position() const { return *this; }
|
||||
/// Size of the rectangle
|
||||
inline RealSize& size() { return *this; }
|
||||
inline const RealSize& size() const { return *this; }
|
||||
|
||||
inline double left() const { return x; }
|
||||
inline double right() const { return x + width; }
|
||||
inline double top() const { return y; }
|
||||
inline double bottom() const { return y + height; }
|
||||
|
||||
inline RealPoint topLeft () const { return *this; }
|
||||
inline RealPoint topRight () const { return RealPoint(x + width, y); }
|
||||
inline RealPoint bottomLeft () const { return RealPoint(x, y + height); }
|
||||
inline RealPoint bottomRight() const { return RealPoint(x + width, y + height); }
|
||||
|
||||
/// Return a rectangle that is amount larger to all sides
|
||||
inline RealRect grow(double amount) {
|
||||
return RealRect(x - amount, y - amount, width + 2 * amount, height + 2 * amount);
|
||||
}
|
||||
/// Move the coordinates by some amount
|
||||
inline RealRect move(double dx, double dy, double dw, double dh) const {
|
||||
return RealRect(x + dx, y + dy, width + dw, height + dh);
|
||||
}
|
||||
|
||||
inline operator wxRect() const {
|
||||
// Prevent rounding errors, for example if
|
||||
// x = 0.6 and width = 0.6
|
||||
// the right = 1.2
|
||||
// so we want a rectangle from 0 to 1
|
||||
// not from 0 to 0
|
||||
int i_l = to_int(x), i_r = to_int(right());
|
||||
int i_t = to_int(y), i_b = to_int(bottom());
|
||||
return wxRect(i_l, i_t, i_r - i_l, i_b - i_t);
|
||||
}
|
||||
|
||||
/// Explicit conversion to wxRect, to not confuse gcc
|
||||
inline wxRect toRect() const {
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/// Split a rectangle horizontally
|
||||
@@ -198,31 +198,31 @@ class RealRect : private RealPoint, private RealSize {
|
||||
* +----+-------+
|
||||
*/
|
||||
inline RealRect split_left(RealRect& r, double w) {
|
||||
RealRect result(r.x, r.y, w, r.height);
|
||||
r.width -= w;
|
||||
r.x += w;
|
||||
return result;
|
||||
RealRect result(r.x, r.y, w, r.height);
|
||||
r.width -= w;
|
||||
r.x += w;
|
||||
return result;
|
||||
}
|
||||
/// Split a rectangle horizontally
|
||||
inline RealRect split_left(RealRect& r, const RealSize& s) {
|
||||
return split_left(r, s.width);
|
||||
return split_left(r, s.width);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Operators
|
||||
|
||||
inline RealPoint operator + (const RealSize& s, const RealPoint& p) {
|
||||
return RealPoint(p.x + s.width, p.y + s.height);
|
||||
return RealPoint(p.x + s.width, p.y + s.height);
|
||||
}
|
||||
inline RealPoint operator + (const RealPoint& p, const RealSize& s) {
|
||||
return RealPoint(p.x + s.width, p.y + s.height);
|
||||
return RealPoint(p.x + s.width, p.y + s.height);
|
||||
}
|
||||
inline RealPoint operator - (const RealPoint& p, const RealSize& s) {
|
||||
return RealPoint(p.x - s.width, p.y - s.height);
|
||||
return RealPoint(p.x - s.width, p.y - s.height);
|
||||
}
|
||||
inline void operator += (RealPoint& p, const RealSize& s) {
|
||||
p.x += s.width;
|
||||
p.y += s.height;
|
||||
p.x += s.width;
|
||||
p.y += s.height;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+68
-68
@@ -23,32 +23,32 @@
|
||||
/// Declare that a class supports reflection
|
||||
/** Reflection allows the member variables of a class to be inspected at runtime.
|
||||
*/
|
||||
#define DECLARE_REFLECTION() \
|
||||
protected: \
|
||||
template<class Tag> void reflect_impl(Tag& tag); \
|
||||
friend class Reader; \
|
||||
friend class Writer; \
|
||||
friend class GetDefaultMember; \
|
||||
friend class GetMember; \
|
||||
void reflect(Reader& reader); \
|
||||
void reflect(Writer& writer); \
|
||||
void reflect(GetDefaultMember& gdm); \
|
||||
void reflect(GetMember& gm)
|
||||
#define DECLARE_REFLECTION() \
|
||||
protected: \
|
||||
template<class Tag> void reflect_impl(Tag& tag); \
|
||||
friend class Reader; \
|
||||
friend class Writer; \
|
||||
friend class GetDefaultMember; \
|
||||
friend class GetMember; \
|
||||
void reflect(Reader& reader); \
|
||||
void reflect(Writer& writer); \
|
||||
void reflect(GetDefaultMember& gdm); \
|
||||
void reflect(GetMember& gm)
|
||||
|
||||
/// Declare that a class supports reflection, which can be overridden in derived classes
|
||||
#define DECLARE_REFLECTION_VIRTUAL() \
|
||||
protected: \
|
||||
template<class Tag> void reflect_impl(Tag& tag); \
|
||||
friend class Reader; \
|
||||
friend class Writer; \
|
||||
friend class GetDefaultMember; \
|
||||
friend class GetMember; \
|
||||
/* extra level of indirection between Tag::handle \
|
||||
* and reflect_impl, to allow for virtual */ \
|
||||
virtual void reflect(Reader& reader); \
|
||||
virtual void reflect(Writer& writer); \
|
||||
virtual void reflect(GetDefaultMember& gdm); \
|
||||
virtual void reflect(GetMember& gm)
|
||||
#define DECLARE_REFLECTION_VIRTUAL() \
|
||||
protected: \
|
||||
template<class Tag> void reflect_impl(Tag& tag); \
|
||||
friend class Reader; \
|
||||
friend class Writer; \
|
||||
friend class GetDefaultMember; \
|
||||
friend class GetMember; \
|
||||
/* extra level of indirection between Tag::handle \
|
||||
* and reflect_impl, to allow for virtual */ \
|
||||
virtual void reflect(Reader& reader); \
|
||||
virtual void reflect(Writer& writer); \
|
||||
virtual void reflect(GetDefaultMember& gdm); \
|
||||
virtual void reflect(GetMember& gm)
|
||||
|
||||
// ----------------------------------------------------------------------------- : Implementing reflection
|
||||
|
||||
@@ -67,40 +67,40 @@
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
#define IMPLEMENT_REFLECTION(Cls) \
|
||||
REFLECT_OBJECT_READER(Cls) \
|
||||
REFLECT_OBJECT_WRITER(Cls) \
|
||||
REFLECT_OBJECT_GET_DEFAULT_MEMBER_NOT(Cls) \
|
||||
REFLECT_OBJECT_GET_MEMBER(Cls) \
|
||||
template <class Tag> \
|
||||
void Cls::reflect_impl(Tag& tag)
|
||||
#define IMPLEMENT_REFLECTION(Cls) \
|
||||
REFLECT_OBJECT_READER(Cls) \
|
||||
REFLECT_OBJECT_WRITER(Cls) \
|
||||
REFLECT_OBJECT_GET_DEFAULT_MEMBER_NOT(Cls) \
|
||||
REFLECT_OBJECT_GET_MEMBER(Cls) \
|
||||
template <class Tag> \
|
||||
void Cls::reflect_impl(Tag& tag)
|
||||
|
||||
/// Implement the refelection of a class type Cls that only uses REFLECT_NAMELESS
|
||||
#define IMPLEMENT_REFLECTION_NAMELESS(Cls) \
|
||||
REFLECT_OBJECT_READER(Cls) \
|
||||
REFLECT_OBJECT_WRITER(Cls) \
|
||||
REFLECT_OBJECT_GET_DEFAULT_MEMBER(Cls) \
|
||||
REFLECT_OBJECT_GET_MEMBER_NOT(Cls) \
|
||||
template <class Tag> \
|
||||
void Cls::reflect_impl(Tag& tag)
|
||||
#define IMPLEMENT_REFLECTION_NAMELESS(Cls) \
|
||||
REFLECT_OBJECT_READER(Cls) \
|
||||
REFLECT_OBJECT_WRITER(Cls) \
|
||||
REFLECT_OBJECT_GET_DEFAULT_MEMBER(Cls) \
|
||||
REFLECT_OBJECT_GET_MEMBER_NOT(Cls) \
|
||||
template <class Tag> \
|
||||
void Cls::reflect_impl(Tag& tag)
|
||||
|
||||
/// Implement the refelection of a class type Cls, but only for Reader and Writer,
|
||||
/** There is custom code for GetMember and GetDefaultMember */
|
||||
#define IMPLEMENT_REFLECTION_NO_GET_MEMBER(Cls) \
|
||||
REFLECT_OBJECT_READER(Cls) \
|
||||
REFLECT_OBJECT_WRITER(Cls) \
|
||||
template <class Tag> \
|
||||
void Cls::reflect_impl(Tag& tag)
|
||||
#define IMPLEMENT_REFLECTION_NO_GET_MEMBER(Cls) \
|
||||
REFLECT_OBJECT_READER(Cls) \
|
||||
REFLECT_OBJECT_WRITER(Cls) \
|
||||
template <class Tag> \
|
||||
void Cls::reflect_impl(Tag& tag)
|
||||
|
||||
/// Implement the refelection of a class type Cls, but only for Reader and Writer
|
||||
/** There is no code for GetMember and GetDefaultMember */
|
||||
#define IMPLEMENT_REFLECTION_NO_SCRIPT(Cls) \
|
||||
REFLECT_OBJECT_READER(Cls) \
|
||||
REFLECT_OBJECT_WRITER(Cls) \
|
||||
REFLECT_OBJECT_GET_DEFAULT_MEMBER_NOT(Cls) \
|
||||
REFLECT_OBJECT_GET_MEMBER_NOT(Cls) \
|
||||
template <class Tag> \
|
||||
void Cls::reflect_impl(Tag& tag)
|
||||
#define IMPLEMENT_REFLECTION_NO_SCRIPT(Cls) \
|
||||
REFLECT_OBJECT_READER(Cls) \
|
||||
REFLECT_OBJECT_WRITER(Cls) \
|
||||
REFLECT_OBJECT_GET_DEFAULT_MEMBER_NOT(Cls) \
|
||||
REFLECT_OBJECT_GET_MEMBER_NOT(Cls) \
|
||||
template <class Tag> \
|
||||
void Cls::reflect_impl(Tag& tag)
|
||||
|
||||
/// Reflect a variable
|
||||
#define REFLECT(var) tag.handle(_(#var), var)
|
||||
@@ -150,15 +150,15 @@
|
||||
#define REFLECT_NO_SCRIPT_N(name, var) tag.handleNoScript(_(name), var)
|
||||
|
||||
/// Explicitly instantiate reflection; this is occasionally required.
|
||||
#define INSTANTIATE_REFLECTION(Class) \
|
||||
template void Class::reflect_impl<Reader> (Reader&); \
|
||||
template void Class::reflect_impl<Writer> (Writer&); \
|
||||
template void Class::reflect_impl<GetMember> (GetMember&);
|
||||
#define INSTANTIATE_REFLECTION(Class) \
|
||||
template void Class::reflect_impl<Reader> (Reader&); \
|
||||
template void Class::reflect_impl<Writer> (Writer&); \
|
||||
template void Class::reflect_impl<GetMember> (GetMember&);
|
||||
|
||||
#define INSTANTIATE_REFLECTION_NAMELESS(Class) \
|
||||
template void Class::reflect_impl<Reader> (Reader&); \
|
||||
template void Class::reflect_impl<Writer> (Writer&); \
|
||||
template void Class::reflect_impl<GetDefaultMember> (GetDefaultMember&);
|
||||
#define INSTANTIATE_REFLECTION_NAMELESS(Class) \
|
||||
template void Class::reflect_impl<Reader> (Reader&); \
|
||||
template void Class::reflect_impl<Writer> (Writer&); \
|
||||
template void Class::reflect_impl<GetDefaultMember> (GetDefaultMember&);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Reflecting enums
|
||||
|
||||
@@ -178,20 +178,20 @@
|
||||
* - Writer::handle(const Enum&)
|
||||
* - GetDefaultMember::handle(const Enum&)
|
||||
*/
|
||||
#define IMPLEMENT_REFLECTION_ENUM(Enum) \
|
||||
template <class Tag> \
|
||||
void reflect_ ## Enum (Enum& enum_, Tag& tag); \
|
||||
REFLECT_ENUM_READER(Enum) \
|
||||
REFLECT_ENUM_WRITER(Enum) \
|
||||
REFLECT_ENUM_GET_MEMBER(Enum) \
|
||||
template <class Tag> \
|
||||
void reflect_ ## Enum (Enum& enum_, Tag& tag)
|
||||
#define IMPLEMENT_REFLECTION_ENUM(Enum) \
|
||||
template <class Tag> \
|
||||
void reflect_ ## Enum (Enum& enum_, Tag& tag); \
|
||||
REFLECT_ENUM_READER(Enum) \
|
||||
REFLECT_ENUM_WRITER(Enum) \
|
||||
REFLECT_ENUM_GET_MEMBER(Enum) \
|
||||
template <class Tag> \
|
||||
void reflect_ ## Enum (Enum& enum_, Tag& tag)
|
||||
|
||||
/// Declare a possible value of an enum
|
||||
#define VALUE(val) tag.handle(_(#val), val, enum_)
|
||||
#define VALUE(val) tag.handle(_(#val), val, enum_)
|
||||
|
||||
/// Declare a possible value of an enum under the given name
|
||||
#define VALUE_N(name, val) tag.handle(_(name), val, enum_)
|
||||
#define VALUE_N(name, val) tag.handle(_(name), val, enum_)
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
|
||||
+19
-19
@@ -14,34 +14,34 @@
|
||||
// ----------------------------------------------------------------------------- : Regex : boost
|
||||
|
||||
void Regex::assign(const String& code) {
|
||||
// compile string
|
||||
try {
|
||||
regex.assign(code.begin(),code.end());
|
||||
} catch (const boost::regex_error& e) {
|
||||
/// TODO: be more precise
|
||||
throw ScriptError(String::Format(_("Error while compiling regular expression: '%s'\nAt position: %d\n%s"),
|
||||
code.c_str(), e.position(), String(e.what(), IF_UNICODE(wxConvUTF8,String::npos)).c_str()));
|
||||
}
|
||||
// compile string
|
||||
try {
|
||||
regex.assign(code.begin(),code.end());
|
||||
} catch (const boost::regex_error& e) {
|
||||
/// TODO: be more precise
|
||||
throw ScriptError(String::Format(_("Error while compiling regular expression: '%s'\nAt position: %d\n%s"),
|
||||
code.c_str(), e.position(), String(e.what(), IF_UNICODE(wxConvUTF8,String::npos)).c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
void Regex::replace_all(String* input, const String& format) {
|
||||
//std::basic_string<Char> fmt; format_string(format,fmt);
|
||||
std::basic_string<Char> fmt(format.begin(),format.end());
|
||||
String output;
|
||||
regex_replace(insert_iterator<String>(output, output.end()),
|
||||
input->begin(), input->end(), regex, fmt, boost::format_sed);
|
||||
*input = output;
|
||||
//std::basic_string<Char> fmt; format_string(format,fmt);
|
||||
std::basic_string<Char> fmt(format.begin(),format.end());
|
||||
String output;
|
||||
regex_replace(insert_iterator<String>(output, output.end()),
|
||||
input->begin(), input->end(), regex, fmt, boost::format_sed);
|
||||
*input = output;
|
||||
}
|
||||
|
||||
#else // USE_BOOST_REGEX
|
||||
// ----------------------------------------------------------------------------- : Regex : wx
|
||||
|
||||
void Regex::assign(const String& code) {
|
||||
// compile string
|
||||
if (!regex.Compile(code, wxRE_ADVANCED)) {
|
||||
throw ScriptError(_("Error while compiling regular expression: '") + code + _("'"));
|
||||
}
|
||||
assert(regex.IsValid());
|
||||
// compile string
|
||||
if (!regex.Compile(code, wxRE_ADVANCED)) {
|
||||
throw ScriptError(_("Error while compiling regular expression: '") + code + _("'"));
|
||||
}
|
||||
assert(regex.IsValid());
|
||||
}
|
||||
|
||||
#endif // USE_BOOST_REGEX
|
||||
|
||||
+123
-123
@@ -25,137 +25,137 @@
|
||||
#define USE_BOOST_REGEX 1
|
||||
|
||||
#if USE_BOOST_REGEX
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/regex/pattern_except.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/regex/pattern_except.hpp>
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : Boost implementation
|
||||
|
||||
#if USE_BOOST_REGEX
|
||||
/// Our own regular expression wrapper
|
||||
/** Suppors both boost::regex and wxRegEx.
|
||||
* Has an interface like boost::regex, but compatible with wxStrings.
|
||||
*/
|
||||
class Regex {
|
||||
public:
|
||||
struct Results : public boost::match_results<String::const_iterator> {
|
||||
/// Get a sub match
|
||||
inline String str(int sub = 0) const {
|
||||
const_reference v = (*this)[sub];
|
||||
return String(v.first, v.second);
|
||||
}
|
||||
/// Format a replacement string
|
||||
inline String format(const String& format) const {
|
||||
std::basic_string<Char> fmt(format.begin(),format.end());
|
||||
String output;
|
||||
boost::match_results<String::const_iterator>::format(
|
||||
insert_iterator<String>(output, output.end()), fmt, boost::format_sed);
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
inline Regex() {}
|
||||
inline Regex(const String& code) { assign(code); }
|
||||
|
||||
void assign(const String& code);
|
||||
inline bool matches(const String& str) const {
|
||||
return regex_search(str.begin(), str.end(), regex);
|
||||
}
|
||||
inline bool matches(Results& results, const String& str, size_t start = 0) const {
|
||||
return matches(results, str.begin() + start, str.end());
|
||||
}
|
||||
inline bool matches(Results& results, const String::const_iterator& begin, const String::const_iterator& end) const {
|
||||
return regex_search(begin, end, results, regex);
|
||||
}
|
||||
void replace_all(String* input, const String& format);
|
||||
|
||||
inline bool empty() const {
|
||||
return regex.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
boost::basic_regex<Char> regex; ///< The regular expression
|
||||
};
|
||||
/// Our own regular expression wrapper
|
||||
/** Suppors both boost::regex and wxRegEx.
|
||||
* Has an interface like boost::regex, but compatible with wxStrings.
|
||||
*/
|
||||
class Regex {
|
||||
public:
|
||||
struct Results : public boost::match_results<String::const_iterator> {
|
||||
/// Get a sub match
|
||||
inline String str(int sub = 0) const {
|
||||
const_reference v = (*this)[sub];
|
||||
return String(v.first, v.second);
|
||||
}
|
||||
/// Format a replacement string
|
||||
inline String format(const String& format) const {
|
||||
std::basic_string<Char> fmt(format.begin(),format.end());
|
||||
String output;
|
||||
boost::match_results<String::const_iterator>::format(
|
||||
insert_iterator<String>(output, output.end()), fmt, boost::format_sed);
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
inline Regex() {}
|
||||
inline Regex(const String& code) { assign(code); }
|
||||
|
||||
void assign(const String& code);
|
||||
inline bool matches(const String& str) const {
|
||||
return regex_search(str.begin(), str.end(), regex);
|
||||
}
|
||||
inline bool matches(Results& results, const String& str, size_t start = 0) const {
|
||||
return matches(results, str.begin() + start, str.end());
|
||||
}
|
||||
inline bool matches(Results& results, const String::const_iterator& begin, const String::const_iterator& end) const {
|
||||
return regex_search(begin, end, results, regex);
|
||||
}
|
||||
void replace_all(String* input, const String& format);
|
||||
|
||||
inline bool empty() const {
|
||||
return regex.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
boost::basic_regex<Char> regex; ///< The regular expression
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Wx implementation
|
||||
#else
|
||||
/// Our own regular expression wrapper
|
||||
/** Suppors both boost::regex and wxRegEx.
|
||||
* Has an interface like boost::regex.
|
||||
*/
|
||||
class Regex
|
||||
public:
|
||||
// Interface for compatability with boost::regex
|
||||
class Results {
|
||||
public:
|
||||
typedef pair<const Char*,const Char*> value_type; // (begin,end)
|
||||
typedef value_type const_reference;
|
||||
/// Number of submatches (+1 for the total match)
|
||||
inline size_t size() const { return regex->GetMatchCount(); }
|
||||
/// Get a submatch
|
||||
inline value_type operator [] (int sub) const {
|
||||
size_t pos, length;
|
||||
bool ok = regex->GetMatch(&pos, &length, sub);
|
||||
assert(ok);
|
||||
return make_pair(begin + pos, begin + pos + length);
|
||||
}
|
||||
inline size_t position(int sub = 0) const {
|
||||
size_t pos, length;
|
||||
bool ok = regex->GetMatch(&pos, &length, sub);
|
||||
assert(ok);
|
||||
return pos;
|
||||
}
|
||||
inline size_t length(int sub = 0) const {
|
||||
size_t pos, length;
|
||||
bool ok = regex->GetMatch(&pos, &length, sub);
|
||||
assert(ok);
|
||||
return length;
|
||||
}
|
||||
/// Get a sub match
|
||||
inline String str(int sub = 0) const {
|
||||
const_reference v = (*this)[sub];
|
||||
return String(v.first, v.second);
|
||||
}
|
||||
/// Format a replacement string
|
||||
inline String format(const String& format) const {
|
||||
const_reference v = (*this)[0];
|
||||
String inside(v.first, v.second);
|
||||
regex->ReplaceFirst(&inside, format);
|
||||
return inside;
|
||||
}
|
||||
private:
|
||||
wxRegEx* regex;
|
||||
const Char* begin;
|
||||
friend class ScriptRegex;
|
||||
};
|
||||
|
||||
inline Regex() {}
|
||||
inline Regex(const String& code) { assign(code); }
|
||||
|
||||
void assign(const String& code);
|
||||
inline bool matches(const String& str) const {
|
||||
return regex.Matches(str);
|
||||
}
|
||||
inline bool matches(Results& results, const String& str) const {
|
||||
results.regex = ®ex;
|
||||
results.begin = str.begin();
|
||||
return regex.Matches(str);
|
||||
}
|
||||
inline bool matches(Results& results, const Char* begin, const Char* end) const {
|
||||
results.regex = ®ex;
|
||||
results.begin = begin;
|
||||
return regex.Matches(begin, 0, end - begin);
|
||||
}
|
||||
inline void replace_all(String* input, const String& format) {
|
||||
regex.Replace(input, format);
|
||||
}
|
||||
inline bool empty() const {
|
||||
return !regex.IsValid();
|
||||
}
|
||||
|
||||
private:
|
||||
wxRegEx regex; ///< The regular expression
|
||||
};
|
||||
/// Our own regular expression wrapper
|
||||
/** Suppors both boost::regex and wxRegEx.
|
||||
* Has an interface like boost::regex.
|
||||
*/
|
||||
class Regex
|
||||
public:
|
||||
// Interface for compatability with boost::regex
|
||||
class Results {
|
||||
public:
|
||||
typedef pair<const Char*,const Char*> value_type; // (begin,end)
|
||||
typedef value_type const_reference;
|
||||
/// Number of submatches (+1 for the total match)
|
||||
inline size_t size() const { return regex->GetMatchCount(); }
|
||||
/// Get a submatch
|
||||
inline value_type operator [] (int sub) const {
|
||||
size_t pos, length;
|
||||
bool ok = regex->GetMatch(&pos, &length, sub);
|
||||
assert(ok);
|
||||
return make_pair(begin + pos, begin + pos + length);
|
||||
}
|
||||
inline size_t position(int sub = 0) const {
|
||||
size_t pos, length;
|
||||
bool ok = regex->GetMatch(&pos, &length, sub);
|
||||
assert(ok);
|
||||
return pos;
|
||||
}
|
||||
inline size_t length(int sub = 0) const {
|
||||
size_t pos, length;
|
||||
bool ok = regex->GetMatch(&pos, &length, sub);
|
||||
assert(ok);
|
||||
return length;
|
||||
}
|
||||
/// Get a sub match
|
||||
inline String str(int sub = 0) const {
|
||||
const_reference v = (*this)[sub];
|
||||
return String(v.first, v.second);
|
||||
}
|
||||
/// Format a replacement string
|
||||
inline String format(const String& format) const {
|
||||
const_reference v = (*this)[0];
|
||||
String inside(v.first, v.second);
|
||||
regex->ReplaceFirst(&inside, format);
|
||||
return inside;
|
||||
}
|
||||
private:
|
||||
wxRegEx* regex;
|
||||
const Char* begin;
|
||||
friend class ScriptRegex;
|
||||
};
|
||||
|
||||
inline Regex() {}
|
||||
inline Regex(const String& code) { assign(code); }
|
||||
|
||||
void assign(const String& code);
|
||||
inline bool matches(const String& str) const {
|
||||
return regex.Matches(str);
|
||||
}
|
||||
inline bool matches(Results& results, const String& str) const {
|
||||
results.regex = ®ex;
|
||||
results.begin = str.begin();
|
||||
return regex.Matches(str);
|
||||
}
|
||||
inline bool matches(Results& results, const Char* begin, const Char* end) const {
|
||||
results.regex = ®ex;
|
||||
results.begin = begin;
|
||||
return regex.Matches(begin, 0, end - begin);
|
||||
}
|
||||
inline void replace_all(String* input, const String& format) {
|
||||
regex.Replace(input, format);
|
||||
}
|
||||
inline bool empty() const {
|
||||
return !regex.IsValid();
|
||||
}
|
||||
|
||||
private:
|
||||
wxRegEx regex; ///< The regular expression
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+251
-251
@@ -14,292 +14,292 @@
|
||||
// ----------------------------------------------------------------------------- : Rotation
|
||||
|
||||
Rotation::Rotation(Radians angle, const RealRect& rect, double zoom, double stretch, RotationFlags flags)
|
||||
: angle(constrain_radians(angle))
|
||||
, size(rect.size())
|
||||
, origin(rect.position())
|
||||
, zoomX(zoom * stretch)
|
||||
, zoomY(zoom)
|
||||
: angle(constrain_radians(angle))
|
||||
, size(rect.size())
|
||||
, origin(rect.position())
|
||||
, zoomX(zoom * stretch)
|
||||
, zoomY(zoom)
|
||||
{
|
||||
if (stretch != 1.0) {
|
||||
size.width /= stretch;
|
||||
}
|
||||
// set origin
|
||||
if (flags & ROTATION_ATTACH_TOP_LEFT) {
|
||||
origin -= boundingBoxCorner(size);
|
||||
}
|
||||
if (stretch != 1.0) {
|
||||
size.width /= stretch;
|
||||
}
|
||||
// set origin
|
||||
if (flags & ROTATION_ATTACH_TOP_LEFT) {
|
||||
origin -= boundingBoxCorner(size);
|
||||
}
|
||||
}
|
||||
|
||||
void Rotation::setStretch(double s) {
|
||||
size.width *= s * getStretch();
|
||||
zoomX = zoomY * s;
|
||||
size.width *= s * getStretch();
|
||||
zoomX = zoomY * s;
|
||||
}
|
||||
|
||||
|
||||
RealPoint Rotation::tr(const RealPoint& p) const {
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x * zoomX, y = p.y * zoomY;
|
||||
return RealPoint(c * x + s * y + origin.x,
|
||||
-s * x + c * y + origin.y);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x * zoomX, y = p.y * zoomY;
|
||||
return RealPoint(c * x + s * y + origin.x,
|
||||
-s * x + c * y + origin.y);
|
||||
}
|
||||
RealPoint Rotation::trPixel(const RealPoint& p) const {
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x * zoomX + 0.5, y = p.y * zoomY + 0.5;
|
||||
return RealPoint(c * x + s * y + origin.x - 0.5,
|
||||
-s * x + c * y + origin.y - 0.5);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x * zoomX + 0.5, y = p.y * zoomY + 0.5;
|
||||
return RealPoint(c * x + s * y + origin.x - 0.5,
|
||||
-s * x + c * y + origin.y - 0.5);
|
||||
}
|
||||
RealPoint Rotation::trNoZoom(const RealPoint& p) const {
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x, y = p.y;
|
||||
return RealPoint(c * x + s * y + origin.x,
|
||||
-s * x + c * y + origin.y);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x, y = p.y;
|
||||
return RealPoint(c * x + s * y + origin.x,
|
||||
-s * x + c * y + origin.y);
|
||||
}
|
||||
RealPoint Rotation::trPixelNoZoom(const RealPoint& p) const {
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x + 0.5, y = p.y + 0.5;
|
||||
return RealPoint(c * x + s * y + origin.x - 0.5,
|
||||
-s * x + c * y + origin.y - 0.5);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x + 0.5, y = p.y + 0.5;
|
||||
return RealPoint(c * x + s * y + origin.x - 0.5,
|
||||
-s * x + c * y + origin.y - 0.5);
|
||||
}
|
||||
|
||||
RealSize Rotation::trSizeToBB(const RealSize& size) const {
|
||||
if (is_straight(angle)) {
|
||||
if (is_sideways(angle)) {
|
||||
return RealSize(size.height * zoomY, size.width * zoomX);
|
||||
} else {
|
||||
return RealSize(size.width * zoomX, size.height * zoomY);
|
||||
}
|
||||
} else {
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = size.width * zoomX, y = size.height * zoomY;
|
||||
return RealSize(fabs(c * x) + fabs(s * y), fabs(s * x) + fabs(c * y));
|
||||
}
|
||||
if (is_straight(angle)) {
|
||||
if (is_sideways(angle)) {
|
||||
return RealSize(size.height * zoomY, size.width * zoomX);
|
||||
} else {
|
||||
return RealSize(size.width * zoomX, size.height * zoomY);
|
||||
}
|
||||
} else {
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = size.width * zoomX, y = size.height * zoomY;
|
||||
return RealSize(fabs(c * x) + fabs(s * y), fabs(s * x) + fabs(c * y));
|
||||
}
|
||||
}
|
||||
|
||||
RealRect Rotation::trRectToBB(const RealRect& r) const {
|
||||
const bool special_case_optimization = false;
|
||||
double x = r.x * zoomX, y = r.y * zoomY;
|
||||
double w = r.width * zoomX, h = r.height * zoomY;
|
||||
if (special_case_optimization && is_rad0(angle)) {
|
||||
return RealRect(origin.x + x, origin.y + y, w, h);
|
||||
} else if (special_case_optimization && is_rad180(angle)) {
|
||||
return RealRect(origin.x - x - w, origin.y - y - h, w, h);
|
||||
} else if (special_case_optimization && is_rad90(angle)) {
|
||||
return RealRect(origin.x + y, origin.y - x - w, h, w);
|
||||
} else if (special_case_optimization && is_rad270(angle)) {
|
||||
return RealRect(origin.x - y - h, origin.y + x, h, w);
|
||||
} else {
|
||||
double s = sin(angle), c = cos(angle);
|
||||
RealRect result(c * x + s * y + origin.x,
|
||||
-s * x + c * y + origin.y,
|
||||
0,0);
|
||||
if (c > 0) {
|
||||
result.width += c * w;
|
||||
result.height += c * h;
|
||||
} else {
|
||||
result.x += c * w;
|
||||
result.width -= c * w;
|
||||
result.y += c * h;
|
||||
result.height -= c * h;
|
||||
}
|
||||
if (s > 0) {
|
||||
result.width += s * h;
|
||||
result.y -= s * w;
|
||||
result.height += s * w;
|
||||
} else {
|
||||
result.x += s * h;
|
||||
result.width -= s * h;
|
||||
result.height -= s * w;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const bool special_case_optimization = false;
|
||||
double x = r.x * zoomX, y = r.y * zoomY;
|
||||
double w = r.width * zoomX, h = r.height * zoomY;
|
||||
if (special_case_optimization && is_rad0(angle)) {
|
||||
return RealRect(origin.x + x, origin.y + y, w, h);
|
||||
} else if (special_case_optimization && is_rad180(angle)) {
|
||||
return RealRect(origin.x - x - w, origin.y - y - h, w, h);
|
||||
} else if (special_case_optimization && is_rad90(angle)) {
|
||||
return RealRect(origin.x + y, origin.y - x - w, h, w);
|
||||
} else if (special_case_optimization && is_rad270(angle)) {
|
||||
return RealRect(origin.x - y - h, origin.y + x, h, w);
|
||||
} else {
|
||||
double s = sin(angle), c = cos(angle);
|
||||
RealRect result(c * x + s * y + origin.x,
|
||||
-s * x + c * y + origin.y,
|
||||
0,0);
|
||||
if (c > 0) {
|
||||
result.width += c * w;
|
||||
result.height += c * h;
|
||||
} else {
|
||||
result.x += c * w;
|
||||
result.width -= c * w;
|
||||
result.y += c * h;
|
||||
result.height -= c * h;
|
||||
}
|
||||
if (s > 0) {
|
||||
result.width += s * h;
|
||||
result.y -= s * w;
|
||||
result.height += s * w;
|
||||
} else {
|
||||
result.x += s * h;
|
||||
result.width -= s * h;
|
||||
result.height -= s * w;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
wxRegion Rotation::trRectToRegion(const RealRect& r) const {
|
||||
if (is_straight(angle)) {
|
||||
return trRectToBB(r).toRect();
|
||||
} else {
|
||||
wxPoint points[4] = {trPixel(RealPoint(r.left(), r.top() ))
|
||||
,trPixel(RealPoint(r.left(), r.bottom()))
|
||||
,trPixel(RealPoint(r.right(), r.bottom()))
|
||||
,trPixel(RealPoint(r.right(), r.top() ))};
|
||||
return wxRegion(4,points);
|
||||
}
|
||||
if (is_straight(angle)) {
|
||||
return trRectToBB(r).toRect();
|
||||
} else {
|
||||
wxPoint points[4] = {trPixel(RealPoint(r.left(), r.top() ))
|
||||
,trPixel(RealPoint(r.left(), r.bottom()))
|
||||
,trPixel(RealPoint(r.right(), r.bottom()))
|
||||
,trPixel(RealPoint(r.right(), r.top() ))};
|
||||
return wxRegion(4,points);
|
||||
}
|
||||
}
|
||||
|
||||
RealPoint Rotation::trInv(const RealPoint& p) const {
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x - origin.x, y = p.y - origin.y;
|
||||
return RealPoint((c * x - s * y) / zoomX,
|
||||
(s * x + c * y) / zoomY);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x - origin.x, y = p.y - origin.y;
|
||||
return RealPoint((c * x - s * y) / zoomX,
|
||||
(s * x + c * y) / zoomY);
|
||||
}
|
||||
RealSize Rotation::trInv(const RealSize& x) const {
|
||||
double s = sin(angle), c = cos(angle);
|
||||
return RealSize((c * x.width - s * x.height) / zoomX,
|
||||
(s * x.width + c * x.height) / zoomY);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
return RealSize((c * x.width - s * x.height) / zoomX,
|
||||
(s * x.width + c * x.height) / zoomY);
|
||||
}
|
||||
|
||||
RealPoint Rotation::boundingBoxCorner(const RealSize& size) const {
|
||||
// This function is a bit tricky,
|
||||
// I derived it by drawing the four cases.
|
||||
// Two succeeding cases must agree where they overlap (0,90,180,270 degrees)
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double w = size.width * zoomX, h = size.height * zoomY;
|
||||
if (angle <= rad90) return RealPoint(0, -w * s);
|
||||
if (angle <= rad180) return RealPoint(w * c, h * c - w * s);
|
||||
if (angle <= rad270) return RealPoint(w * c + h * s, h * c);
|
||||
else return RealPoint(h * s, 0);
|
||||
// This function is a bit tricky,
|
||||
// I derived it by drawing the four cases.
|
||||
// Two succeeding cases must agree where they overlap (0,90,180,270 degrees)
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double w = size.width * zoomX, h = size.height * zoomY;
|
||||
if (angle <= rad90) return RealPoint(0, -w * s);
|
||||
if (angle <= rad180) return RealPoint(w * c, h * c - w * s);
|
||||
if (angle <= rad270) return RealPoint(w * c + h * s, h * c);
|
||||
else return RealPoint(h * s, 0);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Rotater
|
||||
|
||||
Rotater::Rotater(Rotation& rot, const Rotation& by)
|
||||
: old(rot)
|
||||
, rot(rot)
|
||||
: old(rot)
|
||||
, rot(rot)
|
||||
{
|
||||
// apply rotation
|
||||
rot.origin = rot.tr(by.origin);
|
||||
rot.size = by.size;
|
||||
rot.angle = constrain_radians(rot.angle + by.angle);
|
||||
// zooming is not really correct if rot.zoomX != rot.zoomY
|
||||
rot.zoomX *= by.zoomX;
|
||||
rot.zoomY *= by.zoomY;
|
||||
// apply rotation
|
||||
rot.origin = rot.tr(by.origin);
|
||||
rot.size = by.size;
|
||||
rot.angle = constrain_radians(rot.angle + by.angle);
|
||||
// zooming is not really correct if rot.zoomX != rot.zoomY
|
||||
rot.zoomX *= by.zoomX;
|
||||
rot.zoomY *= by.zoomY;
|
||||
}
|
||||
|
||||
Rotater::~Rotater() {
|
||||
rot = old; // restore
|
||||
rot = old; // restore
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : RotatedDC
|
||||
|
||||
RotatedDC::RotatedDC(DC& dc, Radians angle, const RealRect& rect, double zoom, RenderQuality quality, RotationFlags flags)
|
||||
: Rotation(angle, rect, zoom, 1.0, flags)
|
||||
, dc(dc), quality(quality)
|
||||
: Rotation(angle, rect, zoom, 1.0, flags)
|
||||
, dc(dc), quality(quality)
|
||||
{}
|
||||
|
||||
RotatedDC::RotatedDC(DC& dc, const Rotation& rotation, RenderQuality quality)
|
||||
: Rotation(rotation)
|
||||
, dc(dc), quality(quality)
|
||||
: Rotation(rotation)
|
||||
, dc(dc), quality(quality)
|
||||
{}
|
||||
|
||||
// ----------------------------------------------------------------------------- : RotatedDC : Drawing
|
||||
|
||||
void RotatedDC::DrawText (const String& text, const RealPoint& pos, int blur_radius, int boldness, double stretch_) {
|
||||
DrawText(text, pos, dc.GetTextForeground(), blur_radius, boldness, stretch_);
|
||||
DrawText(text, pos, dc.GetTextForeground(), blur_radius, boldness, stretch_);
|
||||
}
|
||||
|
||||
void RotatedDC::DrawText (const String& text, const RealPoint& pos, AColor color, int blur_radius, int boldness, double stretch_) {
|
||||
if (text.empty()) return;
|
||||
if (color.alpha == 0) return;
|
||||
if (quality >= QUALITY_AA) {
|
||||
RealRect r(pos, GetTextExtent(text));
|
||||
RealRect r_ext = trRectToBB(r);
|
||||
RealPoint pos2 = tr(pos);
|
||||
stretch_ *= getStretch();
|
||||
if (fabs(stretch_ - 1) > 1e-6) {
|
||||
r.width *= stretch_;
|
||||
RealRect r_ext2 = trRectToBB(r);
|
||||
pos2.x += r_ext2.x - r_ext.x;
|
||||
pos2.y += r_ext2.y - r_ext.y;
|
||||
r_ext.x = r_ext2.x;
|
||||
r_ext.y = r_ext2.y;
|
||||
}
|
||||
draw_resampled_text(dc, pos2, r_ext, stretch_, angle, color, text, blur_radius, boldness);
|
||||
} else if (quality >= QUALITY_SUB_PIXEL) {
|
||||
RealPoint p_ext = tr(pos)*text_scaling;
|
||||
double usx,usy;
|
||||
dc.GetUserScale(&usx, &usy);
|
||||
dc.SetUserScale(usx/text_scaling, usy/text_scaling);
|
||||
dc.SetTextForeground(color);
|
||||
dc.DrawRotatedText(text, (int) p_ext.x, (int) p_ext.y, rad_to_deg(angle));
|
||||
dc.SetUserScale(usx, usy);
|
||||
} else {
|
||||
RealPoint p_ext = tr(pos);
|
||||
dc.SetTextForeground(color);
|
||||
dc.DrawRotatedText(text, (int) p_ext.x, (int) p_ext.y, rad_to_deg(angle));
|
||||
}
|
||||
if (text.empty()) return;
|
||||
if (color.alpha == 0) return;
|
||||
if (quality >= QUALITY_AA) {
|
||||
RealRect r(pos, GetTextExtent(text));
|
||||
RealRect r_ext = trRectToBB(r);
|
||||
RealPoint pos2 = tr(pos);
|
||||
stretch_ *= getStretch();
|
||||
if (fabs(stretch_ - 1) > 1e-6) {
|
||||
r.width *= stretch_;
|
||||
RealRect r_ext2 = trRectToBB(r);
|
||||
pos2.x += r_ext2.x - r_ext.x;
|
||||
pos2.y += r_ext2.y - r_ext.y;
|
||||
r_ext.x = r_ext2.x;
|
||||
r_ext.y = r_ext2.y;
|
||||
}
|
||||
draw_resampled_text(dc, pos2, r_ext, stretch_, angle, color, text, blur_radius, boldness);
|
||||
} else if (quality >= QUALITY_SUB_PIXEL) {
|
||||
RealPoint p_ext = tr(pos)*text_scaling;
|
||||
double usx,usy;
|
||||
dc.GetUserScale(&usx, &usy);
|
||||
dc.SetUserScale(usx/text_scaling, usy/text_scaling);
|
||||
dc.SetTextForeground(color);
|
||||
dc.DrawRotatedText(text, (int) p_ext.x, (int) p_ext.y, rad_to_deg(angle));
|
||||
dc.SetUserScale(usx, usy);
|
||||
} else {
|
||||
RealPoint p_ext = tr(pos);
|
||||
dc.SetTextForeground(color);
|
||||
dc.DrawRotatedText(text, (int) p_ext.x, (int) p_ext.y, rad_to_deg(angle));
|
||||
}
|
||||
}
|
||||
|
||||
void RotatedDC::DrawTextWithShadow(const String& text, const Font& font, const RealPoint& pos, double scale, double stretch) {
|
||||
DrawText(text, pos + font.shadow_displacement * scale, font.shadow_color, font.shadow_blur * scale, 1, stretch);
|
||||
DrawText(text, pos, font.color, 0, 1, stretch);
|
||||
DrawText(text, pos + font.shadow_displacement * scale, font.shadow_color, font.shadow_blur * scale, 1, stretch);
|
||||
DrawText(text, pos, font.color, 0, 1, stretch);
|
||||
}
|
||||
|
||||
void RotatedDC::DrawBitmap(const Bitmap& bitmap, const RealPoint& pos) {
|
||||
if (is_rad0(angle)) {
|
||||
RealPoint p_ext = tr(pos);
|
||||
dc.DrawBitmap(bitmap, to_int(p_ext.x), to_int(p_ext.y), true);
|
||||
} else {
|
||||
DrawImage(bitmap.ConvertToImage(), pos);
|
||||
}
|
||||
if (is_rad0(angle)) {
|
||||
RealPoint p_ext = tr(pos);
|
||||
dc.DrawBitmap(bitmap, to_int(p_ext.x), to_int(p_ext.y), true);
|
||||
} else {
|
||||
DrawImage(bitmap.ConvertToImage(), pos);
|
||||
}
|
||||
}
|
||||
void RotatedDC::DrawImage(const Image& image, const RealPoint& pos, ImageCombine combine) {
|
||||
Image rotated = rotate_image(image, angle);
|
||||
DrawPreRotatedImage(rotated, RealRect(pos,trInvS(RealSize(image))), combine);
|
||||
Image rotated = rotate_image(image, angle);
|
||||
DrawPreRotatedImage(rotated, RealRect(pos,trInvS(RealSize(image))), combine);
|
||||
}
|
||||
void RotatedDC::DrawPreRotatedBitmap(const Bitmap& bitmap, const RealRect& rect) {
|
||||
RealPoint p_ext = tr(rect.position()) + boundingBoxCorner(rect.size());
|
||||
dc.DrawBitmap(bitmap, to_int(p_ext.x), to_int(p_ext.y), true);
|
||||
RealPoint p_ext = tr(rect.position()) + boundingBoxCorner(rect.size());
|
||||
dc.DrawBitmap(bitmap, to_int(p_ext.x), to_int(p_ext.y), true);
|
||||
}
|
||||
void RotatedDC::DrawPreRotatedImage (const Image& image, const RealRect& rect, ImageCombine combine) {
|
||||
RealPoint p_ext = tr(rect.position()) + boundingBoxCorner(rect.size());
|
||||
draw_combine_image(dc, to_int(p_ext.x), to_int(p_ext.y), image, combine);
|
||||
RealPoint p_ext = tr(rect.position()) + boundingBoxCorner(rect.size());
|
||||
draw_combine_image(dc, to_int(p_ext.x), to_int(p_ext.y), image, combine);
|
||||
}
|
||||
|
||||
void RotatedDC::DrawLine (const RealPoint& p1, const RealPoint& p2) {
|
||||
wxPoint p1_ext = tr(p1), p2_ext = tr(p2);
|
||||
dc.DrawLine(p1_ext.x, p1_ext.y, p2_ext.x, p2_ext.y);
|
||||
wxPoint p1_ext = tr(p1), p2_ext = tr(p2);
|
||||
dc.DrawLine(p1_ext.x, p1_ext.y, p2_ext.x, p2_ext.y);
|
||||
}
|
||||
|
||||
void RotatedDC::DrawRectangle(const RealRect& r) {
|
||||
if (is_straight(angle)) {
|
||||
wxRect r_ext = trRectToBB(r);
|
||||
dc.DrawRectangle(r_ext.x, r_ext.y, r_ext.width, r_ext.height);
|
||||
} else {
|
||||
wxPoint points[4] = {trPixel(RealPoint(r.left(), r.top() ))
|
||||
,trPixel(RealPoint(r.left(), r.bottom()))
|
||||
,trPixel(RealPoint(r.right(), r.bottom()))
|
||||
,trPixel(RealPoint(r.right(), r.top() ))};
|
||||
dc.DrawPolygon(4,points);
|
||||
}
|
||||
if (is_straight(angle)) {
|
||||
wxRect r_ext = trRectToBB(r);
|
||||
dc.DrawRectangle(r_ext.x, r_ext.y, r_ext.width, r_ext.height);
|
||||
} else {
|
||||
wxPoint points[4] = {trPixel(RealPoint(r.left(), r.top() ))
|
||||
,trPixel(RealPoint(r.left(), r.bottom()))
|
||||
,trPixel(RealPoint(r.right(), r.bottom()))
|
||||
,trPixel(RealPoint(r.right(), r.top() ))};
|
||||
dc.DrawPolygon(4,points);
|
||||
}
|
||||
}
|
||||
|
||||
void RotatedDC::DrawRoundedRectangle(const RealRect& r, double radius) {
|
||||
if (is_straight(angle)) {
|
||||
wxRect r_ext = trRectToBB(r);
|
||||
dc.DrawRoundedRectangle(r_ext.x, r_ext.y, r_ext.width, r_ext.height, trS(radius));
|
||||
} else {
|
||||
// TODO
|
||||
DrawRectangle(r);
|
||||
}
|
||||
if (is_straight(angle)) {
|
||||
wxRect r_ext = trRectToBB(r);
|
||||
dc.DrawRoundedRectangle(r_ext.x, r_ext.y, r_ext.width, r_ext.height, trS(radius));
|
||||
} else {
|
||||
// TODO
|
||||
DrawRectangle(r);
|
||||
}
|
||||
}
|
||||
|
||||
void RotatedDC::DrawCircle(const RealPoint& center, double radius) {
|
||||
wxPoint p = tr(center);
|
||||
dc.DrawCircle(p.x + 1, p.y + 1, int(trS(radius)));
|
||||
wxPoint p = tr(center);
|
||||
dc.DrawCircle(p.x + 1, p.y + 1, int(trS(radius)));
|
||||
}
|
||||
|
||||
void RotatedDC::DrawEllipse(const RealPoint& center, const RealSize& size) {
|
||||
wxPoint c_ext = tr(center - size/2);
|
||||
wxSize s_ext = trSizeToBB(size);
|
||||
dc.DrawEllipse(c_ext.x, c_ext.y, s_ext.x, s_ext.y);
|
||||
wxPoint c_ext = tr(center - size/2);
|
||||
wxSize s_ext = trSizeToBB(size);
|
||||
dc.DrawEllipse(c_ext.x, c_ext.y, s_ext.x, s_ext.y);
|
||||
}
|
||||
void RotatedDC::DrawEllipticArc(const RealPoint& center, const RealSize& size, Radians start, Radians end) {
|
||||
wxPoint c_ext = tr(center - size/2);
|
||||
wxSize s_ext = trSizeToBB(size);
|
||||
dc.DrawEllipticArc(c_ext.x, c_ext.y, s_ext.x, s_ext.y, rad_to_deg(start + angle), rad_to_deg(end + angle));
|
||||
wxPoint c_ext = tr(center - size/2);
|
||||
wxSize s_ext = trSizeToBB(size);
|
||||
dc.DrawEllipticArc(c_ext.x, c_ext.y, s_ext.x, s_ext.y, rad_to_deg(start + angle), rad_to_deg(end + angle));
|
||||
}
|
||||
void RotatedDC::DrawEllipticSpoke(const RealPoint& center, const RealSize& size, Radians angle) {
|
||||
wxPoint c_ext = tr(center - size/2);
|
||||
wxSize s_ext = trSizeToBB(size);
|
||||
Radians rot_angle = angle + this->angle;
|
||||
Radians sin_angle = sin(rot_angle), cos_angle = cos(rot_angle);
|
||||
// position of center and of point on the boundary can vary because of rounding errors,
|
||||
// this code matches DrawEllipticArc (at least on windows xp).
|
||||
dc.DrawLine(
|
||||
c_ext.x + int( 0.5 * (s_ext.x + cos_angle) ), // center
|
||||
c_ext.y + int( 0.5 * (s_ext.y - sin_angle) ),
|
||||
c_ext.x + int( 0.5 + 0.5 * (s_ext.x-1) * (1 + cos_angle) ), // boundary
|
||||
c_ext.y + int( 0.5 + 0.5 * (s_ext.y-1) * (1 - sin_angle) )
|
||||
);
|
||||
wxPoint c_ext = tr(center - size/2);
|
||||
wxSize s_ext = trSizeToBB(size);
|
||||
Radians rot_angle = angle + this->angle;
|
||||
Radians sin_angle = sin(rot_angle), cos_angle = cos(rot_angle);
|
||||
// position of center and of point on the boundary can vary because of rounding errors,
|
||||
// this code matches DrawEllipticArc (at least on windows xp).
|
||||
dc.DrawLine(
|
||||
c_ext.x + int( 0.5 * (s_ext.x + cos_angle) ), // center
|
||||
c_ext.y + int( 0.5 * (s_ext.y - sin_angle) ),
|
||||
c_ext.x + int( 0.5 + 0.5 * (s_ext.x-1) * (1 + cos_angle) ), // boundary
|
||||
c_ext.y + int( 0.5 + 0.5 * (s_ext.y-1) * (1 - sin_angle) )
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Forwarded properties
|
||||
@@ -310,76 +310,76 @@ void RotatedDC::SetTextForeground(const Color& color) { dc.SetTextForeground(col
|
||||
void RotatedDC::SetLogicalFunction(wxRasterOperationMode function) { dc.SetLogicalFunction(function); }
|
||||
|
||||
void RotatedDC::SetFont(const wxFont& font) {
|
||||
if (quality == QUALITY_LOW && zoomX == 1 && zoomY == 1) {
|
||||
dc.SetFont(font);
|
||||
} else {
|
||||
wxFont scaled = font;
|
||||
if (quality == QUALITY_LOW) {
|
||||
scaled.SetPointSize((int) trY(font.GetPointSize()));
|
||||
} else {
|
||||
scaled.SetPointSize((int) (trY(font.GetPointSize()) * text_scaling));
|
||||
}
|
||||
dc.SetFont(scaled);
|
||||
}
|
||||
if (quality == QUALITY_LOW && zoomX == 1 && zoomY == 1) {
|
||||
dc.SetFont(font);
|
||||
} else {
|
||||
wxFont scaled = font;
|
||||
if (quality == QUALITY_LOW) {
|
||||
scaled.SetPointSize((int) trY(font.GetPointSize()));
|
||||
} else {
|
||||
scaled.SetPointSize((int) (trY(font.GetPointSize()) * text_scaling));
|
||||
}
|
||||
dc.SetFont(scaled);
|
||||
}
|
||||
}
|
||||
void RotatedDC::SetFont(const Font& font, double scale) {
|
||||
dc.SetFont(font.toWxFont(trS(scale) * (quality == QUALITY_LOW ? 1 : text_scaling)));
|
||||
dc.SetFont(font.toWxFont(trS(scale) * (quality == QUALITY_LOW ? 1 : text_scaling)));
|
||||
}
|
||||
|
||||
double RotatedDC::getFontSizeStep() const {
|
||||
if (quality == QUALITY_LOW) {
|
||||
return 1;
|
||||
} else {
|
||||
return 1. / text_scaling;
|
||||
}
|
||||
if (quality == QUALITY_LOW) {
|
||||
return 1;
|
||||
} else {
|
||||
return 1. / text_scaling;
|
||||
}
|
||||
}
|
||||
|
||||
RealSize RotatedDC::GetTextExtent(const String& text) const {
|
||||
int w, h;
|
||||
dc.GetTextExtent(text, &w, &h);
|
||||
#ifdef __WXGTK__
|
||||
// HACK: Some fonts don't get the descender height set correctly.
|
||||
int charHeight = dc.GetCharHeight();
|
||||
if (charHeight != h)
|
||||
h += h - charHeight;
|
||||
#endif
|
||||
if (quality == QUALITY_LOW) {
|
||||
return RealSize(w / zoomX, h / zoomY);
|
||||
} else {
|
||||
return RealSize(w / (zoomX * text_scaling), h / (zoomY * text_scaling));
|
||||
}
|
||||
int w, h;
|
||||
dc.GetTextExtent(text, &w, &h);
|
||||
#ifdef __WXGTK__
|
||||
// HACK: Some fonts don't get the descender height set correctly.
|
||||
int charHeight = dc.GetCharHeight();
|
||||
if (charHeight != h)
|
||||
h += h - charHeight;
|
||||
#endif
|
||||
if (quality == QUALITY_LOW) {
|
||||
return RealSize(w / zoomX, h / zoomY);
|
||||
} else {
|
||||
return RealSize(w / (zoomX * text_scaling), h / (zoomY * text_scaling));
|
||||
}
|
||||
}
|
||||
double RotatedDC::GetCharHeight() const {
|
||||
int h = dc.GetCharHeight();
|
||||
#ifdef __WXGTK__
|
||||
// See above HACK
|
||||
int extent;
|
||||
dc.GetTextExtent(_("H"), 0, &extent);
|
||||
if (h != extent)
|
||||
h = 2 * extent - h;
|
||||
#endif
|
||||
if (quality == QUALITY_LOW) {
|
||||
return h / zoomY;
|
||||
} else {
|
||||
return h / (zoomY * text_scaling);
|
||||
}
|
||||
int h = dc.GetCharHeight();
|
||||
#ifdef __WXGTK__
|
||||
// See above HACK
|
||||
int extent;
|
||||
dc.GetTextExtent(_("H"), 0, &extent);
|
||||
if (h != extent)
|
||||
h = 2 * extent - h;
|
||||
#endif
|
||||
if (quality == QUALITY_LOW) {
|
||||
return h / zoomY;
|
||||
} else {
|
||||
return h / (zoomY * text_scaling);
|
||||
}
|
||||
}
|
||||
|
||||
void RotatedDC::SetClippingRegion(const RealRect& rect) {
|
||||
dc.SetDeviceClippingRegion(trRectToRegion(rect));
|
||||
dc.SetDeviceClippingRegion(trRectToRegion(rect));
|
||||
}
|
||||
void RotatedDC::DestroyClippingRegion() {
|
||||
dc.DestroyClippingRegion();
|
||||
dc.DestroyClippingRegion();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Other
|
||||
|
||||
Bitmap RotatedDC::GetBackground(const RealRect& r) {
|
||||
wxRect wr = trRectToBB(r);
|
||||
Bitmap background(wr.width, wr.height);
|
||||
wxMemoryDC mdc;
|
||||
mdc.SelectObject(background);
|
||||
mdc.Blit(0, 0, wr.width, wr.height, &dc, wr.x, wr.y);
|
||||
mdc.SelectObject(wxNullBitmap);
|
||||
return background;
|
||||
wxRect wr = trRectToBB(r);
|
||||
Bitmap background(wr.width, wr.height);
|
||||
wxMemoryDC mdc;
|
||||
mdc.SelectObject(background);
|
||||
mdc.Blit(0, 0, wr.width, wr.height, &dc, wr.x, wr.y);
|
||||
mdc.SelectObject(wxNullBitmap);
|
||||
return background;
|
||||
}
|
||||
|
||||
+155
-155
@@ -18,8 +18,8 @@ class Font;
|
||||
// ----------------------------------------------------------------------------- : Rotation
|
||||
|
||||
enum RotationFlags
|
||||
{ ROTATION_NORMAL
|
||||
, ROTATION_ATTACH_TOP_LEFT
|
||||
{ ROTATION_NORMAL
|
||||
, ROTATION_ATTACH_TOP_LEFT
|
||||
};
|
||||
|
||||
/// An object that can rotate coordinates inside a specified rectangle
|
||||
@@ -29,86 +29,86 @@ enum RotationFlags
|
||||
*/
|
||||
class Rotation {
|
||||
public:
|
||||
/// Construct a rotation object
|
||||
/** with the given rectangle of external coordinates and a given rotation angle and zoom factor.
|
||||
* if is_internal then the rect gives the internal coordinates, its origin should be (0,0)
|
||||
*/
|
||||
Rotation(Radians angle = 0, const RealRect& rect = RealRect(0,0,0,0), double zoom = 1.0, double strectch = 1.0, RotationFlags flags = ROTATION_NORMAL);
|
||||
|
||||
/// Change the zoom factor
|
||||
inline void setZoom(double z) { zoomX = zoomY = z; }
|
||||
/// Retrieve the zoom factor
|
||||
inline double getZoom() const { return zoomY; }
|
||||
/// Change the stretch factor
|
||||
void setStretch(double s);
|
||||
/// Stretch factor
|
||||
inline double getStretch() const { return zoomX / zoomY; }
|
||||
/// Get the angle
|
||||
inline Radians getAngle() const { return angle; }
|
||||
/// Change the origin
|
||||
inline void setOrigin(const RealPoint& o) { origin = o; }
|
||||
|
||||
|
||||
/// The internal size
|
||||
inline RealSize getInternalSize() const { return size; }
|
||||
inline double getWidth() const { return size.width; }
|
||||
inline double getHeight() const { return size.height; }
|
||||
/// The intarnal rectangle (origin at (0,0))
|
||||
inline RealRect getInternalRect() const { return RealRect(RealPoint(0,0), size); }
|
||||
/// The size of the external rectangle (as passed to the constructor) == trNoNeg(getInternalSize())
|
||||
inline RealSize getExternalSize() const { return trSizeToBB(size); }
|
||||
/// The external rectangle (as passed to the constructor) == trNoNeg(getInternalRect())
|
||||
inline RealRect getExternalRect() const { return trRectToBB(getInternalRect()); }
|
||||
|
||||
/// Translate a size or length
|
||||
inline double trS(double s) const { return s * zoomY; }
|
||||
inline double trX(double s) const { return s * zoomX; }
|
||||
inline double trY(double s) const { return s * zoomY; }
|
||||
inline RealSize trS(const RealSize& s) const { return RealSize(s.width * zoomX, s.height * zoomY); }
|
||||
|
||||
/// Translate an angle
|
||||
inline Radians trAngle(Radians a) { return constrain_radians(angle + a); }
|
||||
|
||||
/// Translate a single point
|
||||
RealPoint tr(const RealPoint& p) const;
|
||||
/// Translate a single point, but don't zoom
|
||||
RealPoint trNoZoom(const RealPoint& p) const;
|
||||
/// Translate a 'pixel'. A pixel has size 1*1
|
||||
RealPoint trPixel(const RealPoint& p) const;
|
||||
/// Translate a 'pixel', but don't zoom
|
||||
RealPoint trPixelNoZoom(const RealPoint& p) const;
|
||||
/// Translate a single size
|
||||
RealSize trSize(const RealSize& s) const;
|
||||
/// Translate a single size, returns the bounding box size (non-negative)
|
||||
RealSize trSizeToBB(const RealSize& s) const;
|
||||
/// Translate a rectangle, returns the bounding box, the size will be non-negative
|
||||
RealRect trRectToBB(const RealRect& r) const;
|
||||
/// Translate a rectangle into a region (supports rotation)
|
||||
wxRegion trRectToRegion(const RealRect& rect) const;
|
||||
|
||||
/// Translate a size or length back to internal 'coordinates'
|
||||
inline double trInvS(double s) const { return s / zoomY; }
|
||||
inline double trInvX(double s) const { return s / zoomX; }
|
||||
inline double trInvY(double s) const { return s / zoomY; }
|
||||
/// Translate a size back to internal 'coordinates', doesn't rotate
|
||||
inline RealSize trInvS(const RealSize& s) const { return RealSize(s.width / zoomX, s.height / zoomY); }
|
||||
|
||||
/// Translate a point back to internal coordinates
|
||||
RealPoint trInv(const RealPoint& p) const;
|
||||
/// Translate a size back to internal coordinates
|
||||
RealSize trInv(const RealSize& p) const;
|
||||
|
||||
/// Construct a rotation object
|
||||
/** with the given rectangle of external coordinates and a given rotation angle and zoom factor.
|
||||
* if is_internal then the rect gives the internal coordinates, its origin should be (0,0)
|
||||
*/
|
||||
Rotation(Radians angle = 0, const RealRect& rect = RealRect(0,0,0,0), double zoom = 1.0, double strectch = 1.0, RotationFlags flags = ROTATION_NORMAL);
|
||||
|
||||
/// Change the zoom factor
|
||||
inline void setZoom(double z) { zoomX = zoomY = z; }
|
||||
/// Retrieve the zoom factor
|
||||
inline double getZoom() const { return zoomY; }
|
||||
/// Change the stretch factor
|
||||
void setStretch(double s);
|
||||
/// Stretch factor
|
||||
inline double getStretch() const { return zoomX / zoomY; }
|
||||
/// Get the angle
|
||||
inline Radians getAngle() const { return angle; }
|
||||
/// Change the origin
|
||||
inline void setOrigin(const RealPoint& o) { origin = o; }
|
||||
|
||||
|
||||
/// The internal size
|
||||
inline RealSize getInternalSize() const { return size; }
|
||||
inline double getWidth() const { return size.width; }
|
||||
inline double getHeight() const { return size.height; }
|
||||
/// The intarnal rectangle (origin at (0,0))
|
||||
inline RealRect getInternalRect() const { return RealRect(RealPoint(0,0), size); }
|
||||
/// The size of the external rectangle (as passed to the constructor) == trNoNeg(getInternalSize())
|
||||
inline RealSize getExternalSize() const { return trSizeToBB(size); }
|
||||
/// The external rectangle (as passed to the constructor) == trNoNeg(getInternalRect())
|
||||
inline RealRect getExternalRect() const { return trRectToBB(getInternalRect()); }
|
||||
|
||||
/// Translate a size or length
|
||||
inline double trS(double s) const { return s * zoomY; }
|
||||
inline double trX(double s) const { return s * zoomX; }
|
||||
inline double trY(double s) const { return s * zoomY; }
|
||||
inline RealSize trS(const RealSize& s) const { return RealSize(s.width * zoomX, s.height * zoomY); }
|
||||
|
||||
/// Translate an angle
|
||||
inline Radians trAngle(Radians a) { return constrain_radians(angle + a); }
|
||||
|
||||
/// Translate a single point
|
||||
RealPoint tr(const RealPoint& p) const;
|
||||
/// Translate a single point, but don't zoom
|
||||
RealPoint trNoZoom(const RealPoint& p) const;
|
||||
/// Translate a 'pixel'. A pixel has size 1*1
|
||||
RealPoint trPixel(const RealPoint& p) const;
|
||||
/// Translate a 'pixel', but don't zoom
|
||||
RealPoint trPixelNoZoom(const RealPoint& p) const;
|
||||
/// Translate a single size
|
||||
RealSize trSize(const RealSize& s) const;
|
||||
/// Translate a single size, returns the bounding box size (non-negative)
|
||||
RealSize trSizeToBB(const RealSize& s) const;
|
||||
/// Translate a rectangle, returns the bounding box, the size will be non-negative
|
||||
RealRect trRectToBB(const RealRect& r) const;
|
||||
/// Translate a rectangle into a region (supports rotation)
|
||||
wxRegion trRectToRegion(const RealRect& rect) const;
|
||||
|
||||
/// Translate a size or length back to internal 'coordinates'
|
||||
inline double trInvS(double s) const { return s / zoomY; }
|
||||
inline double trInvX(double s) const { return s / zoomX; }
|
||||
inline double trInvY(double s) const { return s / zoomY; }
|
||||
/// Translate a size back to internal 'coordinates', doesn't rotate
|
||||
inline RealSize trInvS(const RealSize& s) const { return RealSize(s.width / zoomX, s.height / zoomY); }
|
||||
|
||||
/// Translate a point back to internal coordinates
|
||||
RealPoint trInv(const RealPoint& p) const;
|
||||
/// Translate a size back to internal coordinates
|
||||
RealSize trInv(const RealSize& p) const;
|
||||
|
||||
protected:
|
||||
Radians angle; ///< The angle of rotation in radians (counterclockwise)
|
||||
RealSize size; ///< Size of the rectangle, in internal coordinates
|
||||
RealPoint origin; ///< tr(0,0)
|
||||
double zoomX; ///< Zoom factor, zoom = 2.0 means that 1 internal = 2 external
|
||||
double zoomY;
|
||||
|
||||
friend class Rotater;
|
||||
|
||||
/// Determine the top-left corner of the bounding box around the rotated box s (in external coordinates)
|
||||
RealPoint boundingBoxCorner(const RealSize& s) const;
|
||||
Radians angle; ///< The angle of rotation in radians (counterclockwise)
|
||||
RealSize size; ///< Size of the rectangle, in internal coordinates
|
||||
RealPoint origin; ///< tr(0,0)
|
||||
double zoomX; ///< Zoom factor, zoom = 2.0 means that 1 internal = 2 external
|
||||
double zoomY;
|
||||
|
||||
friend class Rotater;
|
||||
|
||||
/// Determine the top-left corner of the bounding box around the rotated box s (in external coordinates)
|
||||
RealPoint boundingBoxCorner(const RealSize& s) const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Rotater
|
||||
@@ -123,28 +123,28 @@ class Rotation {
|
||||
*/
|
||||
class Rotater {
|
||||
public:
|
||||
/// Compose a rotation by onto the rotation rot
|
||||
/** rot is restored when this object is destructed
|
||||
*/
|
||||
Rotater(Rotation& rot, const Rotation& by);
|
||||
~Rotater();
|
||||
/// Compose a rotation by onto the rotation rot
|
||||
/** rot is restored when this object is destructed
|
||||
*/
|
||||
Rotater(Rotation& rot, const Rotation& by);
|
||||
~Rotater();
|
||||
private:
|
||||
Rotation old;
|
||||
Rotation& rot;
|
||||
Rotation old;
|
||||
Rotation& rot;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : RotatedDC
|
||||
|
||||
/// Render quality of text
|
||||
enum RenderQuality {
|
||||
QUALITY_LOW, ///< Normal
|
||||
QUALITY_SUB_PIXEL, ///< Sub-pixel positioning
|
||||
QUALITY_AA, ///< Our own anti aliassing
|
||||
QUALITY_LOW, ///< Normal
|
||||
QUALITY_SUB_PIXEL, ///< Sub-pixel positioning
|
||||
QUALITY_AA, ///< Our own anti aliassing
|
||||
};
|
||||
|
||||
#if wxVERSION_NUMBER < 2900
|
||||
// argument type to SetLogicalFunction
|
||||
typedef int wxRasterOperationMode;
|
||||
// argument type to SetLogicalFunction
|
||||
typedef int wxRasterOperationMode;
|
||||
#endif
|
||||
|
||||
/// A DC with rotation applied
|
||||
@@ -152,69 +152,69 @@ enum RenderQuality {
|
||||
*/
|
||||
class RotatedDC : public Rotation {
|
||||
public:
|
||||
RotatedDC(DC& dc, Radians angle, const RealRect& rect, double zoom, RenderQuality quality, RotationFlags flags = ROTATION_NORMAL);
|
||||
RotatedDC(DC& dc, const Rotation& rotation, RenderQuality quality);
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
/// Draw text
|
||||
void DrawText (const String& text, const RealPoint& pos, int blur_radius = 0, int boldness = 1, double stretch = 1.0);
|
||||
void DrawText (const String& text, const RealPoint& pos, AColor color, int blur_radius = 0, int boldness = 1, double stretch = 1.0);
|
||||
/// Draw text with the shadow and color settings of the given font
|
||||
void DrawTextWithShadow(const String& text, const Font& font, const RealPoint& pos, double scale = 1.0, double stretch = 1.0);
|
||||
/// Draw abitmap, it must already be zoomed!
|
||||
void DrawBitmap(const Bitmap& bitmap, const RealPoint& pos);
|
||||
/// Draw an image using the given combining mode, the image must already be zoomed!
|
||||
void DrawImage (const Image& image, const RealPoint& pos, ImageCombine combine = COMBINE_DEFAULT);
|
||||
/// Draw a bitmap that is already zoomed and rotated.
|
||||
/** The rectangle the position in internal coordinates, and the size before rotating and zooming */
|
||||
void DrawPreRotatedBitmap(const Bitmap& bitmap, const RealRect& rect);
|
||||
/// Draw an image that is already zoomed and rotated
|
||||
void DrawPreRotatedImage(const Image& image, const RealRect& rect, ImageCombine combine = COMBINE_DEFAULT);
|
||||
void DrawLine (const RealPoint& p1, const RealPoint& p2);
|
||||
void DrawRectangle(const RealRect& r);
|
||||
void DrawRoundedRectangle(const RealRect& r, double radius);
|
||||
void DrawCircle(const RealPoint& center, double radius);
|
||||
void DrawEllipse(const RealPoint& center, const RealSize& size);
|
||||
/// Draw an arc of an ellipse, angles are in radians
|
||||
void DrawEllipticArc(const RealPoint& center, const RealSize& size, Radians start, Radians end);
|
||||
/// Draw spokes of an ellipse
|
||||
void DrawEllipticSpoke(const RealPoint& center, const RealSize& size, Radians start);
|
||||
|
||||
// Fill the dc with the color of the current brush
|
||||
void Fill();
|
||||
|
||||
// --------------------------------------------------- : Properties
|
||||
|
||||
/// Sets the pen for the dc, does not scale the line width
|
||||
void SetPen(const wxPen&);
|
||||
void SetBrush(const wxBrush&);
|
||||
void SetTextForeground(const Color&);
|
||||
void SetLogicalFunction(wxRasterOperationMode function);
|
||||
|
||||
void SetFont(const wxFont& font);
|
||||
/// Set the font, scales for zoom and high_quality
|
||||
/** The font size will be multiplied by 'scale' */
|
||||
void SetFont(const Font& font, double scale);
|
||||
/// Steps to use when decrementing font size
|
||||
double getFontSizeStep() const;
|
||||
|
||||
RealSize GetTextExtent(const String& text) const;
|
||||
double GetCharHeight() const;
|
||||
|
||||
void SetClippingRegion(const RealRect& rect);
|
||||
void DestroyClippingRegion();
|
||||
|
||||
// --------------------------------------------------- : Other
|
||||
|
||||
/// Get the current contents of the given ractangle, for later restoring
|
||||
Bitmap GetBackground(const RealRect& r);
|
||||
|
||||
inline wxDC& getDC() { return dc; }
|
||||
|
||||
RotatedDC(DC& dc, Radians angle, const RealRect& rect, double zoom, RenderQuality quality, RotationFlags flags = ROTATION_NORMAL);
|
||||
RotatedDC(DC& dc, const Rotation& rotation, RenderQuality quality);
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
/// Draw text
|
||||
void DrawText (const String& text, const RealPoint& pos, int blur_radius = 0, int boldness = 1, double stretch = 1.0);
|
||||
void DrawText (const String& text, const RealPoint& pos, AColor color, int blur_radius = 0, int boldness = 1, double stretch = 1.0);
|
||||
/// Draw text with the shadow and color settings of the given font
|
||||
void DrawTextWithShadow(const String& text, const Font& font, const RealPoint& pos, double scale = 1.0, double stretch = 1.0);
|
||||
/// Draw abitmap, it must already be zoomed!
|
||||
void DrawBitmap(const Bitmap& bitmap, const RealPoint& pos);
|
||||
/// Draw an image using the given combining mode, the image must already be zoomed!
|
||||
void DrawImage (const Image& image, const RealPoint& pos, ImageCombine combine = COMBINE_DEFAULT);
|
||||
/// Draw a bitmap that is already zoomed and rotated.
|
||||
/** The rectangle the position in internal coordinates, and the size before rotating and zooming */
|
||||
void DrawPreRotatedBitmap(const Bitmap& bitmap, const RealRect& rect);
|
||||
/// Draw an image that is already zoomed and rotated
|
||||
void DrawPreRotatedImage(const Image& image, const RealRect& rect, ImageCombine combine = COMBINE_DEFAULT);
|
||||
void DrawLine (const RealPoint& p1, const RealPoint& p2);
|
||||
void DrawRectangle(const RealRect& r);
|
||||
void DrawRoundedRectangle(const RealRect& r, double radius);
|
||||
void DrawCircle(const RealPoint& center, double radius);
|
||||
void DrawEllipse(const RealPoint& center, const RealSize& size);
|
||||
/// Draw an arc of an ellipse, angles are in radians
|
||||
void DrawEllipticArc(const RealPoint& center, const RealSize& size, Radians start, Radians end);
|
||||
/// Draw spokes of an ellipse
|
||||
void DrawEllipticSpoke(const RealPoint& center, const RealSize& size, Radians start);
|
||||
|
||||
// Fill the dc with the color of the current brush
|
||||
void Fill();
|
||||
|
||||
// --------------------------------------------------- : Properties
|
||||
|
||||
/// Sets the pen for the dc, does not scale the line width
|
||||
void SetPen(const wxPen&);
|
||||
void SetBrush(const wxBrush&);
|
||||
void SetTextForeground(const Color&);
|
||||
void SetLogicalFunction(wxRasterOperationMode function);
|
||||
|
||||
void SetFont(const wxFont& font);
|
||||
/// Set the font, scales for zoom and high_quality
|
||||
/** The font size will be multiplied by 'scale' */
|
||||
void SetFont(const Font& font, double scale);
|
||||
/// Steps to use when decrementing font size
|
||||
double getFontSizeStep() const;
|
||||
|
||||
RealSize GetTextExtent(const String& text) const;
|
||||
double GetCharHeight() const;
|
||||
|
||||
void SetClippingRegion(const RealRect& rect);
|
||||
void DestroyClippingRegion();
|
||||
|
||||
// --------------------------------------------------- : Other
|
||||
|
||||
/// Get the current contents of the given ractangle, for later restoring
|
||||
Bitmap GetBackground(const RealRect& r);
|
||||
|
||||
inline wxDC& getDC() { return dc; }
|
||||
|
||||
private:
|
||||
wxDC& dc; ///< The actual dc
|
||||
RenderQuality quality; ///< Quality of the text
|
||||
wxDC& dc; ///< The actual dc
|
||||
RenderQuality quality; ///< Quality of the text
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+105
-105
@@ -16,8 +16,8 @@
|
||||
|
||||
#include <util/atomic.hpp>
|
||||
#ifdef HAVE_FAST_ATOMIC
|
||||
/// Using intrusive_ptr where possible? (as opposed to smart_ptr)
|
||||
#define USE_INTRUSIVE_PTR
|
||||
/// Using intrusive_ptr where possible? (as opposed to smart_ptr)
|
||||
#define USE_INTRUSIVE_PTR
|
||||
#endif
|
||||
|
||||
// Use slightly less fancy template stuff, so msvc7.1 doesn't crash with an internal compiler error
|
||||
@@ -26,16 +26,16 @@
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#ifdef USE_INTRUSIVE_PTR
|
||||
#include <boost/intrusive_ptr.hpp>
|
||||
#include <boost/intrusive_ptr.hpp>
|
||||
#endif
|
||||
|
||||
// Can't do using namespace boost;
|
||||
// because boost::shared_ptr conflicts with std::tr1::shared_ptr
|
||||
// and some boost headers do include boost/shared_ptr themselves
|
||||
#if _HAS_TR1
|
||||
using std::tr1::shared_ptr;
|
||||
using std::tr1::shared_ptr;
|
||||
#else
|
||||
using boost::shared_ptr;
|
||||
using boost::shared_ptr;
|
||||
#endif
|
||||
using boost::intrusive_ptr;
|
||||
using boost::scoped_ptr;
|
||||
@@ -45,123 +45,123 @@ using boost::dynamic_pointer_cast;
|
||||
// ----------------------------------------------------------------------------- : Declaring
|
||||
|
||||
/// Declares the type TypeP as a shared_ptr<Type>
|
||||
#define DECLARE_SHARED_POINTER_TYPE(Type) \
|
||||
class Type; \
|
||||
typedef shared_ptr<Type> Type##P;
|
||||
#define DECLARE_SHARED_POINTER_TYPE(Type) \
|
||||
class Type; \
|
||||
typedef shared_ptr<Type> Type##P;
|
||||
|
||||
// ----------------------------------------------------------------------------- : Creating
|
||||
|
||||
/// Wrap a newly allocated pointer in an shared_ptr
|
||||
/** Usage:
|
||||
* return shared(new T(stuff)));
|
||||
*/
|
||||
* return shared(new T(stuff)));
|
||||
*/
|
||||
template <typename T>
|
||||
inline shared_ptr<T> shared(T* ptr) {
|
||||
return shared_ptr<T>(ptr);
|
||||
return shared_ptr<T>(ptr);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Intrusive pointers
|
||||
|
||||
#ifdef USE_INTRUSIVE_PTR
|
||||
|
||||
/// Declares the type TypeP as a intrusive_ptr<Type>
|
||||
#define DECLARE_POINTER_TYPE(Type) \
|
||||
class Type; \
|
||||
typedef intrusive_ptr<Type> Type##P;
|
||||
/// Declares the type TypeP as a intrusive_ptr<Type>
|
||||
#define DECLARE_POINTER_TYPE(Type) \
|
||||
class Type; \
|
||||
typedef intrusive_ptr<Type> Type##P;
|
||||
|
||||
/// Wrap a newly allocated pointer in an intrusive_ptr
|
||||
/** Usage:
|
||||
* return intrusive(new T(stuff)));
|
||||
*/
|
||||
template <typename T>
|
||||
inline intrusive_ptr<T> intrusive(T* ptr) {
|
||||
return intrusive_ptr<T>(ptr);
|
||||
}
|
||||
/// Wrap a newly allocated pointer in an intrusive_ptr
|
||||
/** Usage:
|
||||
* return intrusive(new T(stuff)));
|
||||
*/
|
||||
template <typename T>
|
||||
inline intrusive_ptr<T> intrusive(T* ptr) {
|
||||
return intrusive_ptr<T>(ptr);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Intrusive pointer base
|
||||
|
||||
template <typename T> class IntrusivePtrBase;
|
||||
template <typename T> void intrusive_ptr_add_ref(IntrusivePtrBase<T>*);
|
||||
template <typename T> void intrusive_ptr_release(IntrusivePtrBase<T>*);
|
||||
/// Base class for objects wishing to use intrusive_ptrs.
|
||||
/** There is no implicit virtual destructor, objects are destructed as type T
|
||||
* Usage:
|
||||
* @code
|
||||
* DECLARE_POINTER_TYPE(MyClass);
|
||||
* class MyClass : public IntrusivePtrBase<MyClass> { ... }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename T> class IntrusivePtrBase {
|
||||
public:
|
||||
inline IntrusivePtrBase() : ref_count(0) {}
|
||||
// don't copy construct the reference count!
|
||||
inline IntrusivePtrBase(const IntrusivePtrBase&) : ref_count(0) {}
|
||||
// don't assign the reference count!
|
||||
inline void operator = (const IntrusivePtrBase&) { }
|
||||
protected:
|
||||
/// Delete this object, can be overloaded
|
||||
inline void destroy() {
|
||||
delete static_cast<T*>(this);
|
||||
}
|
||||
private:
|
||||
AtomicInt ref_count;
|
||||
friend void intrusive_ptr_add_ref <> (IntrusivePtrBase*);
|
||||
friend void intrusive_ptr_release <> (IntrusivePtrBase*);
|
||||
};
|
||||
// ----------------------------------------------------------------------------- : Intrusive pointer base
|
||||
|
||||
template <typename T> class IntrusivePtrBase;
|
||||
template <typename T> void intrusive_ptr_add_ref(IntrusivePtrBase<T>*);
|
||||
template <typename T> void intrusive_ptr_release(IntrusivePtrBase<T>*);
|
||||
/// Base class for objects wishing to use intrusive_ptrs.
|
||||
/** There is no implicit virtual destructor, objects are destructed as type T
|
||||
* Usage:
|
||||
* @code
|
||||
* DECLARE_POINTER_TYPE(MyClass);
|
||||
* class MyClass : public IntrusivePtrBase<MyClass> { ... }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename T> class IntrusivePtrBase {
|
||||
public:
|
||||
inline IntrusivePtrBase() : ref_count(0) {}
|
||||
// don't copy construct the reference count!
|
||||
inline IntrusivePtrBase(const IntrusivePtrBase&) : ref_count(0) {}
|
||||
// don't assign the reference count!
|
||||
inline void operator = (const IntrusivePtrBase&) { }
|
||||
protected:
|
||||
/// Delete this object, can be overloaded
|
||||
inline void destroy() {
|
||||
delete static_cast<T*>(this);
|
||||
}
|
||||
private:
|
||||
AtomicInt ref_count;
|
||||
friend void intrusive_ptr_add_ref <> (IntrusivePtrBase*);
|
||||
friend void intrusive_ptr_release <> (IntrusivePtrBase*);
|
||||
};
|
||||
|
||||
template <typename T> void intrusive_ptr_add_ref(IntrusivePtrBase<T>* p) {
|
||||
++(p->ref_count);
|
||||
}
|
||||
|
||||
template <typename T> void intrusive_ptr_release(IntrusivePtrBase<T>* p) {
|
||||
if (--p->ref_count == 0) {
|
||||
static_cast<T*>(p)->destroy();
|
||||
}
|
||||
}
|
||||
// ----------------------------------------------------------------------------- : Intrusive pointer base : virtual
|
||||
|
||||
/// IntrusivePtrBase with a virtual destructor
|
||||
class IntrusivePtrVirtualBase : public IntrusivePtrBase<IntrusivePtrVirtualBase> {
|
||||
public:
|
||||
virtual ~IntrusivePtrVirtualBase() {}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Intrusive pointer base : with delete
|
||||
|
||||
/// Base class for objects wishing to use intrusive_ptrs, using a manual delete function
|
||||
class IntrusivePtrBaseWithDelete : public IntrusivePtrBase<IntrusivePtrBaseWithDelete> {
|
||||
public:
|
||||
virtual ~IntrusivePtrBaseWithDelete() {}
|
||||
protected:
|
||||
/// Delete this object
|
||||
virtual void destroy() {
|
||||
delete this;
|
||||
}
|
||||
template <typename T> friend void intrusive_ptr_release(IntrusivePtrBase<T>*);
|
||||
};
|
||||
|
||||
template <typename T> void intrusive_ptr_add_ref(IntrusivePtrBase<T>* p) {
|
||||
++(p->ref_count);
|
||||
}
|
||||
|
||||
template <typename T> void intrusive_ptr_release(IntrusivePtrBase<T>* p) {
|
||||
if (--p->ref_count == 0) {
|
||||
static_cast<T*>(p)->destroy();
|
||||
}
|
||||
}
|
||||
// ----------------------------------------------------------------------------- : Intrusive pointer base : virtual
|
||||
|
||||
/// IntrusivePtrBase with a virtual destructor
|
||||
class IntrusivePtrVirtualBase : public IntrusivePtrBase<IntrusivePtrVirtualBase> {
|
||||
public:
|
||||
virtual ~IntrusivePtrVirtualBase() {}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Intrusive pointer base : with delete
|
||||
|
||||
/// Base class for objects wishing to use intrusive_ptrs, using a manual delete function
|
||||
class IntrusivePtrBaseWithDelete : public IntrusivePtrBase<IntrusivePtrBaseWithDelete> {
|
||||
public:
|
||||
virtual ~IntrusivePtrBaseWithDelete() {}
|
||||
protected:
|
||||
/// Delete this object
|
||||
virtual void destroy() {
|
||||
delete this;
|
||||
}
|
||||
template <typename T> friend void intrusive_ptr_release(IntrusivePtrBase<T>*);
|
||||
};
|
||||
|
||||
#else
|
||||
#define DECLARE_POINTER_TYPE DECLARE_SHARED_POINTER_TYPE
|
||||
#define intrusive_ptr shared_ptr
|
||||
|
||||
template <typename T> class IntrusivePtrBase {};
|
||||
|
||||
/// IntrusivePtrBase with a virtual destructor
|
||||
class IntrusivePtrVirtualBase : public IntrusivePtrBase<IntrusivePtrVirtualBase> {
|
||||
public:
|
||||
virtual ~IntrusivePtrVirtualBase() {}
|
||||
};
|
||||
|
||||
class IntrusivePtrBaseWithDelete : public IntrusivePtrBase<IntrusivePtrBaseWithDelete> {
|
||||
public:
|
||||
virtual ~IntrusivePtrBaseWithDelete() {}
|
||||
protected:
|
||||
/// Delete this object
|
||||
virtual void destroy() {
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
#define DECLARE_POINTER_TYPE DECLARE_SHARED_POINTER_TYPE
|
||||
#define intrusive_ptr shared_ptr
|
||||
|
||||
template <typename T> class IntrusivePtrBase {};
|
||||
|
||||
/// IntrusivePtrBase with a virtual destructor
|
||||
class IntrusivePtrVirtualBase : public IntrusivePtrBase<IntrusivePtrVirtualBase> {
|
||||
public:
|
||||
virtual ~IntrusivePtrVirtualBase() {}
|
||||
};
|
||||
|
||||
class IntrusivePtrBaseWithDelete : public IntrusivePtrBase<IntrusivePtrBaseWithDelete> {
|
||||
public:
|
||||
virtual ~IntrusivePtrBaseWithDelete() {}
|
||||
protected:
|
||||
/// Delete this object
|
||||
virtual void destroy() {
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/// Pointer to 'anything'
|
||||
|
||||
+287
-287
@@ -20,94 +20,94 @@ String spec_sort(const String& spec, String& input, String& ret);
|
||||
/// Iterator over a sort specification (for spec_sort)
|
||||
class SpecIterator {
|
||||
public:
|
||||
SpecIterator(const String& spec, size_t pos = 0)
|
||||
: spec(spec), pos(pos)
|
||||
{}
|
||||
|
||||
Char value; ///< Current character
|
||||
bool escaped; ///< Was the current character escaped?
|
||||
bool preceded_by_space; ///< Was there a ' ' before this character?
|
||||
|
||||
/// Move to the next item in the specification.
|
||||
/** returns false if we are at the end or encounter close.
|
||||
*/
|
||||
bool nextUntil(Char close, bool skip_space = true) {
|
||||
if (pos >= spec.size()) {
|
||||
value = 0;
|
||||
if (close == 0) {
|
||||
return false;
|
||||
} else {
|
||||
throw ParseError(String::Format(_("Expected '%c' in sort_rule specification"),close));
|
||||
}
|
||||
}
|
||||
value = spec.GetChar(pos++);
|
||||
preceded_by_space = false;
|
||||
// skip whitespace
|
||||
if (skip_space) {
|
||||
while (value == _(' ')) {
|
||||
if (pos >= spec.size()) {
|
||||
if (close == 0) {
|
||||
return false;
|
||||
} else {
|
||||
throw ParseError(String::Format(_("Expected '%c' in sort_rule specification"),close));
|
||||
}
|
||||
}
|
||||
value = spec.GetChar(pos++);
|
||||
preceded_by_space = true;
|
||||
}
|
||||
}
|
||||
// escape?
|
||||
if (value == _('\\')) {
|
||||
escaped = true;
|
||||
if (pos >= spec.size()) {
|
||||
throw ParseError(String::Format(_("Expected '%c' in sort_rule specification"),close));
|
||||
}
|
||||
value = spec.GetChar(pos++);
|
||||
} else {
|
||||
escaped = false;
|
||||
if (value == close) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Read a whole parameter, terminated by close
|
||||
String readParam(Char close, bool skip_space = true) {
|
||||
String ret;
|
||||
while(nextUntil(close)) ret += value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Read a parameter, matches nested parentheses, keeps escape sequences
|
||||
String readRawParam(Char close1, Char close2 = 0) {
|
||||
String ret;
|
||||
int parens = 0;
|
||||
while (nextUntil(0, false)) {
|
||||
if (escaped) {
|
||||
ret += _('\\');
|
||||
} else {
|
||||
if (parens == 0 && (value == close1 || value == close2)) break;
|
||||
if (value == _('(')) parens++;
|
||||
else if (value == _(')')) parens--;
|
||||
}
|
||||
ret += value;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Does the current position match a keyword? If so, skip it
|
||||
bool keyword(const Char* kw) {
|
||||
if (value == kw[0]) {
|
||||
if (is_substr(spec, pos, kw + 1)) {
|
||||
pos += wxStrlen(kw + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SpecIterator(const String& spec, size_t pos = 0)
|
||||
: spec(spec), pos(pos)
|
||||
{}
|
||||
|
||||
Char value; ///< Current character
|
||||
bool escaped; ///< Was the current character escaped?
|
||||
bool preceded_by_space; ///< Was there a ' ' before this character?
|
||||
|
||||
/// Move to the next item in the specification.
|
||||
/** returns false if we are at the end or encounter close.
|
||||
*/
|
||||
bool nextUntil(Char close, bool skip_space = true) {
|
||||
if (pos >= spec.size()) {
|
||||
value = 0;
|
||||
if (close == 0) {
|
||||
return false;
|
||||
} else {
|
||||
throw ParseError(String::Format(_("Expected '%c' in sort_rule specification"),close));
|
||||
}
|
||||
}
|
||||
value = spec.GetChar(pos++);
|
||||
preceded_by_space = false;
|
||||
// skip whitespace
|
||||
if (skip_space) {
|
||||
while (value == _(' ')) {
|
||||
if (pos >= spec.size()) {
|
||||
if (close == 0) {
|
||||
return false;
|
||||
} else {
|
||||
throw ParseError(String::Format(_("Expected '%c' in sort_rule specification"),close));
|
||||
}
|
||||
}
|
||||
value = spec.GetChar(pos++);
|
||||
preceded_by_space = true;
|
||||
}
|
||||
}
|
||||
// escape?
|
||||
if (value == _('\\')) {
|
||||
escaped = true;
|
||||
if (pos >= spec.size()) {
|
||||
throw ParseError(String::Format(_("Expected '%c' in sort_rule specification"),close));
|
||||
}
|
||||
value = spec.GetChar(pos++);
|
||||
} else {
|
||||
escaped = false;
|
||||
if (value == close) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Read a whole parameter, terminated by close
|
||||
String readParam(Char close, bool skip_space = true) {
|
||||
String ret;
|
||||
while(nextUntil(close)) ret += value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Read a parameter, matches nested parentheses, keeps escape sequences
|
||||
String readRawParam(Char close1, Char close2 = 0) {
|
||||
String ret;
|
||||
int parens = 0;
|
||||
while (nextUntil(0, false)) {
|
||||
if (escaped) {
|
||||
ret += _('\\');
|
||||
} else {
|
||||
if (parens == 0 && (value == close1 || value == close2)) break;
|
||||
if (value == _('(')) parens++;
|
||||
else if (value == _(')')) parens--;
|
||||
}
|
||||
ret += value;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Does the current position match a keyword? If so, skip it
|
||||
bool keyword(const Char* kw) {
|
||||
if (value == kw[0]) {
|
||||
if (is_substr(spec, pos, kw + 1)) {
|
||||
pos += wxStrlen(kw + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const String& spec;
|
||||
size_t pos;
|
||||
const String& spec;
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Sort functions
|
||||
@@ -115,230 +115,230 @@ class SpecIterator {
|
||||
/// Sort a string using a specification using the shortest cycle method, see spec_sort
|
||||
/** Removed used characters from input! */
|
||||
void cycle_sort(const String& spec, String& input, String& ret) {
|
||||
size_t size = spec.size();
|
||||
vector<UInt> counts;
|
||||
// count occurences of each char in spec
|
||||
FOR_EACH_CONST(s, spec) {
|
||||
UInt c = 0;
|
||||
FOR_EACH(i, input) {
|
||||
if (s == i) {
|
||||
i = REMOVED; // remove
|
||||
c++;
|
||||
}
|
||||
}
|
||||
counts.push_back(c);
|
||||
}
|
||||
// determine best start point
|
||||
size_t best_start = 0;
|
||||
UInt best_start_score = 0xffffffff;
|
||||
for (size_t start = 0 ; start < size ; ++start) {
|
||||
// score of a start position, can be considered as:
|
||||
// - count saturated to binary
|
||||
// - rotated left by start
|
||||
// - interpreted as a binary number, but without trailing 0s
|
||||
UInt score = 0, mul = 1;
|
||||
for (size_t i = 0 ; i < size ; ++i) {
|
||||
mul *= 2;
|
||||
if (counts[(start + i) % size]) {
|
||||
score = score * mul + 1;
|
||||
mul = 1;
|
||||
}
|
||||
}
|
||||
if (score < best_start_score) {
|
||||
best_start_score = score;
|
||||
best_start = start;
|
||||
}
|
||||
}
|
||||
// return string
|
||||
for (size_t i = 0 ; i < size ; ++i) {
|
||||
size_t pos = (best_start + i) % size;
|
||||
ret.append(counts[pos], spec[pos]);
|
||||
}
|
||||
size_t size = spec.size();
|
||||
vector<UInt> counts;
|
||||
// count occurences of each char in spec
|
||||
FOR_EACH_CONST(s, spec) {
|
||||
UInt c = 0;
|
||||
FOR_EACH(i, input) {
|
||||
if (s == i) {
|
||||
i = REMOVED; // remove
|
||||
c++;
|
||||
}
|
||||
}
|
||||
counts.push_back(c);
|
||||
}
|
||||
// determine best start point
|
||||
size_t best_start = 0;
|
||||
UInt best_start_score = 0xffffffff;
|
||||
for (size_t start = 0 ; start < size ; ++start) {
|
||||
// score of a start position, can be considered as:
|
||||
// - count saturated to binary
|
||||
// - rotated left by start
|
||||
// - interpreted as a binary number, but without trailing 0s
|
||||
UInt score = 0, mul = 1;
|
||||
for (size_t i = 0 ; i < size ; ++i) {
|
||||
mul *= 2;
|
||||
if (counts[(start + i) % size]) {
|
||||
score = score * mul + 1;
|
||||
mul = 1;
|
||||
}
|
||||
}
|
||||
if (score < best_start_score) {
|
||||
best_start_score = score;
|
||||
best_start = start;
|
||||
}
|
||||
}
|
||||
// return string
|
||||
for (size_t i = 0 ; i < size ; ++i) {
|
||||
size_t pos = (best_start + i) % size;
|
||||
ret.append(counts[pos], spec[pos]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sort a string, keeping the characters in the original order
|
||||
/** Removed used characters from input! */
|
||||
void mixed_sort(const String& spec, String& input, String& ret) {
|
||||
FOR_EACH(c, input) {
|
||||
if (spec.find(c) != String::npos) {
|
||||
ret += c;
|
||||
c = REMOVED;
|
||||
}
|
||||
}
|
||||
FOR_EACH(c, input) {
|
||||
if (spec.find(c) != String::npos) {
|
||||
ret += c;
|
||||
c = REMOVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sort a string, find a compound item
|
||||
/** Removed used characters from input! */
|
||||
void compound_sort(const String& spec, String& input, String& ret) {
|
||||
size_t pos = input.find(spec);
|
||||
while (pos != String::npos) {
|
||||
ret += spec;
|
||||
for (size_t j = 0 ; j < spec.size() ; ++j) input.SetChar(pos + j, REMOVED);
|
||||
pos = input.find(spec, pos + 1);
|
||||
}
|
||||
size_t pos = input.find(spec);
|
||||
while (pos != String::npos) {
|
||||
ret += spec;
|
||||
for (size_t j = 0 ; j < spec.size() ; ++j) input.SetChar(pos + j, REMOVED);
|
||||
pos = input.find(spec, pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sort things matching a pattern
|
||||
void pattern_sort(const String& pattern, const String& spec, String& input, String& ret) {
|
||||
if (pattern.size() > input.size()) return;
|
||||
size_t end = input.size() - pattern.size() + 1;
|
||||
for (size_t pos = 0 ; pos < end ; ++pos) {
|
||||
// does the pattern match here?
|
||||
String placeholders;
|
||||
bool match = true;
|
||||
for (size_t j = 0 ; j < pattern.size() ; ++j) {
|
||||
Char c = input.GetChar(pos + j);
|
||||
Char p = pattern.GetChar(j);
|
||||
if (c == REMOVED) { match = false; break; }
|
||||
else if (p == PLACEHOLDER) {
|
||||
placeholders += c;
|
||||
} else if (c != p) { match = false; break; }
|
||||
}
|
||||
// do we have a match?
|
||||
if (match) {
|
||||
// sort placeholders
|
||||
String new_placeholders = spec_sort(spec, placeholders);
|
||||
if (new_placeholders.size() == placeholders.size()) {
|
||||
// add to output, erase from input
|
||||
size_t ph = 0;
|
||||
for (size_t j = 0 ; j < pattern.size() ; ++j) {
|
||||
Char p = pattern.GetChar(j);
|
||||
if (p == PLACEHOLDER) {
|
||||
ret += new_placeholders.GetChar(ph++);
|
||||
} else {
|
||||
ret += p;
|
||||
}
|
||||
input.SetChar(pos + j, REMOVED);
|
||||
}
|
||||
// erase from input
|
||||
pos += pattern.size() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pattern.size() > input.size()) return;
|
||||
size_t end = input.size() - pattern.size() + 1;
|
||||
for (size_t pos = 0 ; pos < end ; ++pos) {
|
||||
// does the pattern match here?
|
||||
String placeholders;
|
||||
bool match = true;
|
||||
for (size_t j = 0 ; j < pattern.size() ; ++j) {
|
||||
Char c = input.GetChar(pos + j);
|
||||
Char p = pattern.GetChar(j);
|
||||
if (c == REMOVED) { match = false; break; }
|
||||
else if (p == PLACEHOLDER) {
|
||||
placeholders += c;
|
||||
} else if (c != p) { match = false; break; }
|
||||
}
|
||||
// do we have a match?
|
||||
if (match) {
|
||||
// sort placeholders
|
||||
String new_placeholders = spec_sort(spec, placeholders);
|
||||
if (new_placeholders.size() == placeholders.size()) {
|
||||
// add to output, erase from input
|
||||
size_t ph = 0;
|
||||
for (size_t j = 0 ; j < pattern.size() ; ++j) {
|
||||
Char p = pattern.GetChar(j);
|
||||
if (p == PLACEHOLDER) {
|
||||
ret += new_placeholders.GetChar(ph++);
|
||||
} else {
|
||||
ret += p;
|
||||
}
|
||||
input.SetChar(pos + j, REMOVED);
|
||||
}
|
||||
// erase from input
|
||||
pos += pattern.size() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sort things in place, keep the rest of the input
|
||||
void in_place_sort(const String& spec, String& input, String& ret) {
|
||||
String result;
|
||||
spec_sort(spec, input, result);
|
||||
// restore into the same order as in 'input'
|
||||
size_t pos_r = 0;
|
||||
FOR_EACH(c, input) {
|
||||
if (c == REMOVED) {
|
||||
if (pos_r < result.size()) {
|
||||
ret += result.GetChar(pos_r++);
|
||||
}
|
||||
} else {
|
||||
ret += c;
|
||||
}
|
||||
}
|
||||
input.clear(); // we ate all the input
|
||||
String result;
|
||||
spec_sort(spec, input, result);
|
||||
// restore into the same order as in 'input'
|
||||
size_t pos_r = 0;
|
||||
FOR_EACH(c, input) {
|
||||
if (c == REMOVED) {
|
||||
if (pos_r < result.size()) {
|
||||
ret += result.GetChar(pos_r++);
|
||||
}
|
||||
} else {
|
||||
ret += c;
|
||||
}
|
||||
}
|
||||
input.clear(); // we ate all the input
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : spec_sort
|
||||
|
||||
String spec_sort(const String& spec, String& input, String& ret) {
|
||||
SpecIterator it(spec);
|
||||
while(it.nextUntil(0)) {
|
||||
if (it.escaped) { // single character, escaped
|
||||
FOR_EACH(d, input) {
|
||||
if (d == it.value) {
|
||||
ret += d;
|
||||
d = REMOVED;
|
||||
}
|
||||
}
|
||||
} else if (it.value == _('<')) { // keep only a single copy
|
||||
while (it.nextUntil(_('>'))) {
|
||||
size_t pos = input.find_first_of(it.value);
|
||||
if (pos != String::npos) {
|
||||
input.SetChar(pos, REMOVED);
|
||||
ret += it.value; // input contains it.value
|
||||
}
|
||||
}
|
||||
} else if (it.keyword(_("once("))) {
|
||||
while (it.nextUntil(_(')'))) {
|
||||
size_t pos = input.find_first_of(it.value);
|
||||
if (pos != String::npos) {
|
||||
input.SetChar(pos, REMOVED);
|
||||
ret += it.value; // input contains it.value
|
||||
}
|
||||
}
|
||||
|
||||
} else if (it.value == _('[')) { // in input order
|
||||
mixed_sort(it.readParam(_(']')), input, ret);
|
||||
} else if (it.keyword(_("mixed("))) {
|
||||
mixed_sort(it.readParam(_(')')), input, ret);
|
||||
|
||||
} else if (it.keyword(_("cycle("))) {
|
||||
cycle_sort(it.readParam(_(')')), input, ret);
|
||||
} else if (it.value == _('(')) {
|
||||
cycle_sort(it.readParam(_(')')), input, ret);
|
||||
|
||||
} else if (it.keyword(_("compound("))) { // compound item
|
||||
compound_sort(it.readParam(_(')')), input, ret);
|
||||
|
||||
} else if (it.keyword(_("pattern("))) { // recurse with pattern
|
||||
String pattern;
|
||||
// read pattern
|
||||
while (it.nextUntil(_(' '), false)) {
|
||||
if (it.value == _('.') && !it.escaped) {
|
||||
it.value = PLACEHOLDER;
|
||||
}
|
||||
pattern += it.value;
|
||||
}
|
||||
// read spec to apply to pattern
|
||||
String sub_spec = it.readRawParam(_(')'));
|
||||
// sort
|
||||
pattern_sort(pattern, sub_spec, input, ret);
|
||||
|
||||
} else if (it.keyword(_("in_place("))) { // recurse without pattern
|
||||
// read spec to apply to pattern
|
||||
String sub_spec = it.readRawParam(_(')'));
|
||||
in_place_sort(sub_spec, input, ret);
|
||||
|
||||
} else if (it.keyword(_("any()"))) { // remaining input
|
||||
FOR_EACH(d, input) {
|
||||
if (d != REMOVED) {
|
||||
ret += d;
|
||||
d = REMOVED;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (it.keyword(_("reverse_order("))) { // reverse order of preference
|
||||
size_t old_ret_size = ret.size();
|
||||
while (it.value != _(')')) {
|
||||
size_t before_ret_size = ret.size();
|
||||
String sub_spec = it.readRawParam(_(')'),_(' '));
|
||||
spec_sort(sub_spec, input, ret);
|
||||
// reverse this item
|
||||
reverse(ret.begin() + before_ret_size, ret.end());
|
||||
}
|
||||
// re-reverse all items
|
||||
reverse(ret.begin() + old_ret_size, ret.end());
|
||||
|
||||
} else if (it.keyword(_("ordered("))) { // in spec order
|
||||
while (it.nextUntil(_(')'))) {
|
||||
FOR_EACH(d, input) {
|
||||
if (d == it.value) {
|
||||
ret += d;
|
||||
d = REMOVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // single char
|
||||
FOR_EACH(d, input) {
|
||||
if (d == it.value) {
|
||||
ret += d;
|
||||
d = REMOVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
SpecIterator it(spec);
|
||||
while(it.nextUntil(0)) {
|
||||
if (it.escaped) { // single character, escaped
|
||||
FOR_EACH(d, input) {
|
||||
if (d == it.value) {
|
||||
ret += d;
|
||||
d = REMOVED;
|
||||
}
|
||||
}
|
||||
} else if (it.value == _('<')) { // keep only a single copy
|
||||
while (it.nextUntil(_('>'))) {
|
||||
size_t pos = input.find_first_of(it.value);
|
||||
if (pos != String::npos) {
|
||||
input.SetChar(pos, REMOVED);
|
||||
ret += it.value; // input contains it.value
|
||||
}
|
||||
}
|
||||
} else if (it.keyword(_("once("))) {
|
||||
while (it.nextUntil(_(')'))) {
|
||||
size_t pos = input.find_first_of(it.value);
|
||||
if (pos != String::npos) {
|
||||
input.SetChar(pos, REMOVED);
|
||||
ret += it.value; // input contains it.value
|
||||
}
|
||||
}
|
||||
|
||||
} else if (it.value == _('[')) { // in input order
|
||||
mixed_sort(it.readParam(_(']')), input, ret);
|
||||
} else if (it.keyword(_("mixed("))) {
|
||||
mixed_sort(it.readParam(_(')')), input, ret);
|
||||
|
||||
} else if (it.keyword(_("cycle("))) {
|
||||
cycle_sort(it.readParam(_(')')), input, ret);
|
||||
} else if (it.value == _('(')) {
|
||||
cycle_sort(it.readParam(_(')')), input, ret);
|
||||
|
||||
} else if (it.keyword(_("compound("))) { // compound item
|
||||
compound_sort(it.readParam(_(')')), input, ret);
|
||||
|
||||
} else if (it.keyword(_("pattern("))) { // recurse with pattern
|
||||
String pattern;
|
||||
// read pattern
|
||||
while (it.nextUntil(_(' '), false)) {
|
||||
if (it.value == _('.') && !it.escaped) {
|
||||
it.value = PLACEHOLDER;
|
||||
}
|
||||
pattern += it.value;
|
||||
}
|
||||
// read spec to apply to pattern
|
||||
String sub_spec = it.readRawParam(_(')'));
|
||||
// sort
|
||||
pattern_sort(pattern, sub_spec, input, ret);
|
||||
|
||||
} else if (it.keyword(_("in_place("))) { // recurse without pattern
|
||||
// read spec to apply to pattern
|
||||
String sub_spec = it.readRawParam(_(')'));
|
||||
in_place_sort(sub_spec, input, ret);
|
||||
|
||||
} else if (it.keyword(_("any()"))) { // remaining input
|
||||
FOR_EACH(d, input) {
|
||||
if (d != REMOVED) {
|
||||
ret += d;
|
||||
d = REMOVED;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (it.keyword(_("reverse_order("))) { // reverse order of preference
|
||||
size_t old_ret_size = ret.size();
|
||||
while (it.value != _(')')) {
|
||||
size_t before_ret_size = ret.size();
|
||||
String sub_spec = it.readRawParam(_(')'),_(' '));
|
||||
spec_sort(sub_spec, input, ret);
|
||||
// reverse this item
|
||||
reverse(ret.begin() + before_ret_size, ret.end());
|
||||
}
|
||||
// re-reverse all items
|
||||
reverse(ret.begin() + old_ret_size, ret.end());
|
||||
|
||||
} else if (it.keyword(_("ordered("))) { // in spec order
|
||||
while (it.nextUntil(_(')'))) {
|
||||
FOR_EACH(d, input) {
|
||||
if (d == it.value) {
|
||||
ret += d;
|
||||
d = REMOVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // single char
|
||||
FOR_EACH(d, input) {
|
||||
if (d == it.value) {
|
||||
ret += d;
|
||||
d = REMOVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String spec_sort(const String& spec, String input) {
|
||||
String ret;
|
||||
spec_sort(spec, input, ret);
|
||||
return ret;
|
||||
String ret;
|
||||
spec_sort(spec, input, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
+92
-92
@@ -16,119 +16,119 @@
|
||||
map<String,SpellCheckerP> SpellChecker::spellers;
|
||||
|
||||
SpellChecker& SpellChecker::get(const String& language) {
|
||||
SpellCheckerP& speller = spellers[language];
|
||||
if (!speller) {
|
||||
String local_dir = package_manager.getDictionaryDir(true);
|
||||
String global_dir = package_manager.getDictionaryDir(false);
|
||||
String aff_path = language + _(".aff");
|
||||
String dic_path = language + _(".dic");
|
||||
if (wxFileExists(local_dir + aff_path) && wxFileExists(local_dir + dic_path)) {
|
||||
speller = SpellCheckerP(new SpellChecker((local_dir + aff_path).mb_str(),
|
||||
(local_dir + dic_path).mb_str()));
|
||||
} else if (wxFileExists(global_dir + aff_path) && wxFileExists(global_dir + dic_path)) {
|
||||
speller = SpellCheckerP(new SpellChecker((global_dir + aff_path).mb_str(),
|
||||
(global_dir + dic_path).mb_str()));
|
||||
} else {
|
||||
throw Error(_("Dictionary not found for language: ") + language);
|
||||
}
|
||||
}
|
||||
return *speller;
|
||||
SpellCheckerP& speller = spellers[language];
|
||||
if (!speller) {
|
||||
String local_dir = package_manager.getDictionaryDir(true);
|
||||
String global_dir = package_manager.getDictionaryDir(false);
|
||||
String aff_path = language + _(".aff");
|
||||
String dic_path = language + _(".dic");
|
||||
if (wxFileExists(local_dir + aff_path) && wxFileExists(local_dir + dic_path)) {
|
||||
speller = SpellCheckerP(new SpellChecker((local_dir + aff_path).mb_str(),
|
||||
(local_dir + dic_path).mb_str()));
|
||||
} else if (wxFileExists(global_dir + aff_path) && wxFileExists(global_dir + dic_path)) {
|
||||
speller = SpellCheckerP(new SpellChecker((global_dir + aff_path).mb_str(),
|
||||
(global_dir + dic_path).mb_str()));
|
||||
} else {
|
||||
throw Error(_("Dictionary not found for language: ") + language);
|
||||
}
|
||||
}
|
||||
return *speller;
|
||||
}
|
||||
|
||||
SpellChecker& SpellChecker::get(const String& filename, const String& language) {
|
||||
SpellCheckerP& speller = spellers[filename + _(".") + language];
|
||||
if (!speller) {
|
||||
Packaged* package = nullptr;
|
||||
String prefix = package_manager.openFilenameFromPackage(package, filename) + _(".");
|
||||
String local_dir = package_manager.getDictionaryDir(true);
|
||||
String global_dir = package_manager.getDictionaryDir(false);
|
||||
String aff_path = language + _(".aff");
|
||||
String dic_path = language + _(".dic");
|
||||
if (wxFileExists(prefix + aff_path) && wxFileExists(prefix + dic_path)) {
|
||||
speller = SpellCheckerP(new SpellChecker((prefix + aff_path).mb_str(),
|
||||
(prefix + dic_path).mb_str()));
|
||||
} else if (wxFileExists(local_dir + aff_path) && wxFileExists(prefix + dic_path)) {
|
||||
speller = SpellCheckerP(new SpellChecker((local_dir + aff_path).mb_str(),
|
||||
(prefix + dic_path).mb_str()));
|
||||
} else if (wxFileExists(global_dir + aff_path) && wxFileExists(prefix + dic_path)) {
|
||||
speller = SpellCheckerP(new SpellChecker((global_dir + aff_path).mb_str(),
|
||||
(prefix + dic_path).mb_str()));
|
||||
} else {
|
||||
throw Error(_("Dictionary '") + filename + _("' not found for language: ") + language);
|
||||
}
|
||||
}
|
||||
return *speller;
|
||||
SpellCheckerP& speller = spellers[filename + _(".") + language];
|
||||
if (!speller) {
|
||||
Packaged* package = nullptr;
|
||||
String prefix = package_manager.openFilenameFromPackage(package, filename) + _(".");
|
||||
String local_dir = package_manager.getDictionaryDir(true);
|
||||
String global_dir = package_manager.getDictionaryDir(false);
|
||||
String aff_path = language + _(".aff");
|
||||
String dic_path = language + _(".dic");
|
||||
if (wxFileExists(prefix + aff_path) && wxFileExists(prefix + dic_path)) {
|
||||
speller = SpellCheckerP(new SpellChecker((prefix + aff_path).mb_str(),
|
||||
(prefix + dic_path).mb_str()));
|
||||
} else if (wxFileExists(local_dir + aff_path) && wxFileExists(prefix + dic_path)) {
|
||||
speller = SpellCheckerP(new SpellChecker((local_dir + aff_path).mb_str(),
|
||||
(prefix + dic_path).mb_str()));
|
||||
} else if (wxFileExists(global_dir + aff_path) && wxFileExists(prefix + dic_path)) {
|
||||
speller = SpellCheckerP(new SpellChecker((global_dir + aff_path).mb_str(),
|
||||
(prefix + dic_path).mb_str()));
|
||||
} else {
|
||||
throw Error(_("Dictionary '") + filename + _("' not found for language: ") + language);
|
||||
}
|
||||
}
|
||||
return *speller;
|
||||
}
|
||||
|
||||
SpellChecker::SpellChecker(const char* aff_path, const char* dic_path)
|
||||
: Hunspell(aff_path,dic_path)
|
||||
, encoding(String(get_dic_encoding(), IF_UNICODE(wxConvLibc, wxSTRING_MAXLEN)))
|
||||
: Hunspell(aff_path,dic_path)
|
||||
, encoding(String(get_dic_encoding(), IF_UNICODE(wxConvLibc, wxSTRING_MAXLEN)))
|
||||
{}
|
||||
|
||||
void SpellChecker::destroyAll() {
|
||||
spellers.clear();
|
||||
spellers.clear();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Spell checker : use
|
||||
|
||||
bool SpellChecker::convert_encoding(const String& word, CharBuffer& out) {
|
||||
// fix curly quotes, especially apstrophes
|
||||
String fixed;
|
||||
FOR_EACH_CONST(c,word) {
|
||||
if (c == LEFT_SINGLE_QUOTE || c == RIGHT_SINGLE_QUOTE) {
|
||||
fixed += _('\'');
|
||||
} else if (c == LEFT_DOUBLE_QUOTE || c == RIGHT_DOUBLE_QUOTE) {
|
||||
fixed += _('\"');
|
||||
} else if (c == 0x00C6) {
|
||||
// expand ligatures, TODO: put this in a better place
|
||||
fixed += _("Ae");
|
||||
} else if (c == 0x0132) {
|
||||
fixed += _("IJ");
|
||||
} else if (c == 0x0152) {
|
||||
fixed += _("Oe");
|
||||
} else if (c == 0xFB01) {
|
||||
fixed += _("fi");
|
||||
} else if (c == 0xFB02) {
|
||||
fixed += _("fl");
|
||||
} else {
|
||||
fixed += c;
|
||||
}
|
||||
}
|
||||
// convert encoding
|
||||
out = fixed.mb_str(encoding);
|
||||
if (!out || *out == '\0') {
|
||||
// If encoding fails we get an empty string, since the word was not empty this can never happen
|
||||
// words that can't be encoded are not in the dictionary, so they are wrong.
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
// fix curly quotes, especially apstrophes
|
||||
String fixed;
|
||||
FOR_EACH_CONST(c,word) {
|
||||
if (c == LEFT_SINGLE_QUOTE || c == RIGHT_SINGLE_QUOTE) {
|
||||
fixed += _('\'');
|
||||
} else if (c == LEFT_DOUBLE_QUOTE || c == RIGHT_DOUBLE_QUOTE) {
|
||||
fixed += _('\"');
|
||||
} else if (c == 0x00C6) {
|
||||
// expand ligatures, TODO: put this in a better place
|
||||
fixed += _("Ae");
|
||||
} else if (c == 0x0132) {
|
||||
fixed += _("IJ");
|
||||
} else if (c == 0x0152) {
|
||||
fixed += _("Oe");
|
||||
} else if (c == 0xFB01) {
|
||||
fixed += _("fi");
|
||||
} else if (c == 0xFB02) {
|
||||
fixed += _("fl");
|
||||
} else {
|
||||
fixed += c;
|
||||
}
|
||||
}
|
||||
// convert encoding
|
||||
out = fixed.mb_str(encoding);
|
||||
if (!out || *out == '\0') {
|
||||
// If encoding fails we get an empty string, since the word was not empty this can never happen
|
||||
// words that can't be encoded are not in the dictionary, so they are wrong.
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool SpellChecker::spell(const String& word) {
|
||||
if (word.empty()) return true; // empty word is okay
|
||||
CharBuffer str;
|
||||
if (!convert_encoding(word,str)) return false;
|
||||
return Hunspell::spell(str);
|
||||
if (word.empty()) return true; // empty word is okay
|
||||
CharBuffer str;
|
||||
if (!convert_encoding(word,str)) return false;
|
||||
return Hunspell::spell(str);
|
||||
}
|
||||
|
||||
bool SpellChecker::spell_with_punctuation(const String& word) {
|
||||
size_t start = 0, end = String::npos;
|
||||
trim_punctuation(word, start, end);
|
||||
if (start >= end) return true; // just punctuation is wrong
|
||||
return spell(word.substr(start,end-start));
|
||||
size_t start = 0, end = String::npos;
|
||||
trim_punctuation(word, start, end);
|
||||
if (start >= end) return true; // just punctuation is wrong
|
||||
return spell(word.substr(start,end-start));
|
||||
}
|
||||
|
||||
void SpellChecker::suggest(const String& word, vector<String>& suggestions_out) {
|
||||
CharBuffer str;
|
||||
if (!convert_encoding(word,str)) return;
|
||||
// call Hunspell
|
||||
char** suggestions;
|
||||
int num_suggestions = Hunspell::suggest(&suggestions, str);
|
||||
// copy sugestions
|
||||
for (int i = 0 ; i < num_suggestions ; ++i) {
|
||||
suggestions_out.push_back(String(suggestions[i],encoding));
|
||||
free(suggestions[i]);
|
||||
}
|
||||
free(suggestions);
|
||||
CharBuffer str;
|
||||
if (!convert_encoding(word,str)) return;
|
||||
// call Hunspell
|
||||
char** suggestions;
|
||||
int num_suggestions = Hunspell::suggest(&suggestions, str);
|
||||
// copy sugestions
|
||||
for (int i = 0 ; i < num_suggestions ; ++i) {
|
||||
suggestions_out.push_back(String(suggestions[i],encoding));
|
||||
free(suggestions[i]);
|
||||
}
|
||||
free(suggestions);
|
||||
}
|
||||
|
||||
+21
-21
@@ -16,9 +16,9 @@
|
||||
DECLARE_POINTER_TYPE(SpellChecker);
|
||||
|
||||
#ifdef UNICODE
|
||||
typedef wxCharBuffer CharBuffer;
|
||||
typedef wxCharBuffer CharBuffer;
|
||||
#else
|
||||
typedef const char* CharBuffer;
|
||||
typedef const char* CharBuffer;
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : Spell checker
|
||||
@@ -26,30 +26,30 @@ DECLARE_POINTER_TYPE(SpellChecker);
|
||||
/// A spelling checker for a particular language
|
||||
class SpellChecker : public Hunspell, public IntrusivePtrBase<SpellChecker> {
|
||||
public:
|
||||
/// Get a SpellChecker object for the given language.
|
||||
/** Note: This is not threadsafe yet */
|
||||
static SpellChecker& get(const String& language);
|
||||
/// Get a SpellChecker object for the given language and filename
|
||||
/** Note: This is not threadsafe yet */
|
||||
static SpellChecker& get(const String& filename, const String& language);
|
||||
/// Destroy all cached SpellChecker objects
|
||||
static void destroyAll();
|
||||
/// Get a SpellChecker object for the given language.
|
||||
/** Note: This is not threadsafe yet */
|
||||
static SpellChecker& get(const String& language);
|
||||
/// Get a SpellChecker object for the given language and filename
|
||||
/** Note: This is not threadsafe yet */
|
||||
static SpellChecker& get(const String& filename, const String& language);
|
||||
/// Destroy all cached SpellChecker objects
|
||||
static void destroyAll();
|
||||
|
||||
/// Check the spelling of a single word
|
||||
bool spell(const String& word);
|
||||
/// Check the spelling of a single word, ignore punctuation
|
||||
bool spell_with_punctuation(const String& word);
|
||||
/// Check the spelling of a single word
|
||||
bool spell(const String& word);
|
||||
/// Check the spelling of a single word, ignore punctuation
|
||||
bool spell_with_punctuation(const String& word);
|
||||
|
||||
/// Give spelling suggestions
|
||||
void suggest(const String& word, vector<String>& suggestions_out);
|
||||
/// Give spelling suggestions
|
||||
void suggest(const String& word, vector<String>& suggestions_out);
|
||||
|
||||
private:
|
||||
/// Convert between String and dictionary encoding
|
||||
wxCSConv encoding;
|
||||
bool convert_encoding(const String& word, CharBuffer& out);
|
||||
/// Convert between String and dictionary encoding
|
||||
wxCSConv encoding;
|
||||
bool convert_encoding(const String& word, CharBuffer& out);
|
||||
|
||||
SpellChecker(const char* aff_path, const char* dic_path);
|
||||
static map<String,SpellCheckerP> spellers; //< Cached checkers for each language
|
||||
SpellChecker(const char* aff_path, const char* dic_path);
|
||||
static map<String,SpellCheckerP> spellers; //< Cached checkers for each language
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+296
-296
@@ -14,31 +14,31 @@
|
||||
// ----------------------------------------------------------------------------- : Unicode
|
||||
|
||||
String decodeUTF8BOM(const String& s) {
|
||||
#ifdef UNICODE
|
||||
if (!s.empty() && s.GetChar(0) == L'\xFEFF') {
|
||||
// skip byte-order-mark
|
||||
return s.substr(1);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
#else
|
||||
wxWCharBuffer buf = s.wc_str(wxConvUTF8);
|
||||
if (buf && buf[size_t(0)] == L'\xFEFF') {
|
||||
// skip byte-order-mark
|
||||
return String(buf + 1, *wxConvCurrent);
|
||||
} else {
|
||||
return String(buf, *wxConvCurrent);
|
||||
}
|
||||
#endif
|
||||
#ifdef UNICODE
|
||||
if (!s.empty() && s.GetChar(0) == L'\xFEFF') {
|
||||
// skip byte-order-mark
|
||||
return s.substr(1);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
#else
|
||||
wxWCharBuffer buf = s.wc_str(wxConvUTF8);
|
||||
if (buf && buf[size_t(0)] == L'\xFEFF') {
|
||||
// skip byte-order-mark
|
||||
return String(buf + 1, *wxConvCurrent);
|
||||
} else {
|
||||
return String(buf, *wxConvCurrent);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void writeUTF8(wxTextOutputStream& stream, const String& str) {
|
||||
#ifdef UNICODE
|
||||
stream.WriteString(str);
|
||||
#else
|
||||
wxWCharBuffer buf = str.wc_str(*wxConvCurrent);
|
||||
stream.WriteString(wxString(buf, wxConvUTF8));
|
||||
#endif
|
||||
#ifdef UNICODE
|
||||
stream.WriteString(str);
|
||||
#else
|
||||
wxWCharBuffer buf = str.wc_str(*wxConvCurrent);
|
||||
stream.WriteString(wxString(buf, wxConvUTF8));
|
||||
#endif
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Char functions
|
||||
@@ -46,21 +46,21 @@ void writeUTF8(wxTextOutputStream& stream, const String& str) {
|
||||
#ifdef CHAR_FUNCTIONS_ARE_SLOW
|
||||
|
||||
Char toLower(Char c) {
|
||||
if (c <= 128) {
|
||||
if (c >= _('A') && c <= _('Z')) return c + (_('a') - _('A'));
|
||||
else return c;
|
||||
} else {
|
||||
return IF_UNICODE( towlower(c) , tolower(c) );
|
||||
}
|
||||
if (c <= 128) {
|
||||
if (c >= _('A') && c <= _('Z')) return c + (_('a') - _('A'));
|
||||
else return c;
|
||||
} else {
|
||||
return IF_UNICODE( towlower(c) , tolower(c) );
|
||||
}
|
||||
}
|
||||
|
||||
Char toUpper(Char c) {
|
||||
if (c <= 128) {
|
||||
if (c >= _('a') && c <= _('z')) return c + (_('A') - _('a'));
|
||||
else return c;
|
||||
} else {
|
||||
return IF_UNICODE( towupper(c) , toupper(c) );
|
||||
}
|
||||
if (c <= 128) {
|
||||
if (c >= _('a') && c <= _('z')) return c + (_('A') - _('a'));
|
||||
else return c;
|
||||
} else {
|
||||
return IF_UNICODE( towupper(c) , toupper(c) );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -68,152 +68,152 @@ Char toUpper(Char c) {
|
||||
// ----------------------------------------------------------------------------- : String utilities
|
||||
|
||||
String trim(const String& s){
|
||||
size_t start = s.find_first_not_of(_(" \t"));
|
||||
size_t end = s.find_last_not_of( _(" \t"));
|
||||
if (start == String::npos) {
|
||||
return String();
|
||||
} else if (start == 0 && end == s.size() - 1) {
|
||||
return s;
|
||||
} else {
|
||||
return s.substr(start, end - start + 1);
|
||||
}
|
||||
size_t start = s.find_first_not_of(_(" \t"));
|
||||
size_t end = s.find_last_not_of( _(" \t"));
|
||||
if (start == String::npos) {
|
||||
return String();
|
||||
} else if (start == 0 && end == s.size() - 1) {
|
||||
return s;
|
||||
} else {
|
||||
return s.substr(start, end - start + 1);
|
||||
}
|
||||
}
|
||||
|
||||
String trim_left(const String& s) {
|
||||
size_t start = s.find_first_not_of(_(" \t"));
|
||||
if (start == String::npos) {
|
||||
return String();
|
||||
} else {
|
||||
return s.substr(start);
|
||||
}
|
||||
size_t start = s.find_first_not_of(_(" \t"));
|
||||
if (start == String::npos) {
|
||||
return String();
|
||||
} else {
|
||||
return s.substr(start);
|
||||
}
|
||||
}
|
||||
|
||||
String substr_replace(const String& input, size_t start, size_t end, const String& replacement) {
|
||||
return input.substr(0,start) + replacement + input.substr(end);
|
||||
return input.substr(0,start) + replacement + input.substr(end);
|
||||
}
|
||||
|
||||
String replace_all(const String& heystack, const String& needle, const String& replacement) {
|
||||
String ret = heystack;
|
||||
ret.Replace(needle, replacement);
|
||||
return ret;
|
||||
String ret = heystack;
|
||||
ret.Replace(needle, replacement);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Words
|
||||
|
||||
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) {
|
||||
return String(); // empty string
|
||||
} else if (startLastWord == String::npos) {
|
||||
return s.substr(0, endLastWord + 1);// first word
|
||||
} else {
|
||||
return s.substr(startLastWord + 1, endLastWord - startLastWord);
|
||||
}
|
||||
size_t endLastWord = s.find_last_not_of(_(' '));
|
||||
size_t startLastWord = s.find_last_of( _(' '), endLastWord);
|
||||
if (endLastWord == String::npos) {
|
||||
return String(); // empty string
|
||||
} else if (startLastWord == String::npos) {
|
||||
return s.substr(0, endLastWord + 1);// first word
|
||||
} else {
|
||||
return s.substr(startLastWord + 1, endLastWord - startLastWord);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
return String(); // single word or empty string
|
||||
} else {
|
||||
return s.substr(0, startLastWord + 1);
|
||||
}
|
||||
size_t endLastWord = s.find_last_not_of(_(' '));
|
||||
size_t startLastWord = s.find_last_of(_(' '), endLastWord);
|
||||
if (endLastWord == String::npos || startLastWord == String::npos) {
|
||||
return String(); // single word or empty string
|
||||
} else {
|
||||
return s.substr(0, startLastWord + 1);
|
||||
}
|
||||
}
|
||||
|
||||
const String word_start_chars = String(_("[({\"\'")) + LEFT_SINGLE_QUOTE + LEFT_DOUBLE_QUOTE;
|
||||
const String word_end_chars = String(_("])}.,;:?!\"\'")) + RIGHT_SINGLE_QUOTE + RIGHT_DOUBLE_QUOTE;
|
||||
|
||||
void trim_punctuation(const String& str, size_t& start, size_t& end) {
|
||||
start = str.find_first_not_of(word_start_chars, start);
|
||||
end = str.find_last_not_of(word_end_chars, min(end,str.size()-1)) + 1;
|
||||
if (start >= end) start = end;
|
||||
start = str.find_first_not_of(word_start_chars, start);
|
||||
end = str.find_last_not_of(word_end_chars, min(end,str.size()-1)) + 1;
|
||||
if (start >= end) start = end;
|
||||
}
|
||||
|
||||
bool is_word_start_punctuation(Char c) {
|
||||
return word_start_chars.find_first_of(c) != String::npos;
|
||||
return word_start_chars.find_first_of(c) != String::npos;
|
||||
}
|
||||
bool is_word_end_punctuation(Char c) {
|
||||
return word_end_chars.find_first_of(c) != String::npos;
|
||||
return word_end_chars.find_first_of(c) != String::npos;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Caseing
|
||||
|
||||
/// Quick check to see if the substring starting at the given iterator is equal to some given string
|
||||
bool is_substr(const String& s, String::const_iterator it, const Char* cmp) {
|
||||
while (it != s.end() && *cmp != 0) {
|
||||
if (*it++ != *cmp++) return false;
|
||||
}
|
||||
return *cmp == 0;
|
||||
while (it != s.end() && *cmp != 0) {
|
||||
if (*it++ != *cmp++) return false;
|
||||
}
|
||||
return *cmp == 0;
|
||||
}
|
||||
|
||||
String capitalize(const String& s) {
|
||||
String result = s;
|
||||
bool after_space = true;
|
||||
FOR_EACH_IT(it, result) {
|
||||
if (*it == _(' ') || *it == _('/')) {
|
||||
after_space = true;
|
||||
} else if (after_space) {
|
||||
after_space = false;
|
||||
// See http://trac.wxwidgets.org/ticket/12594
|
||||
//if (it != s.begin() &&
|
||||
if (s.begin() != it &&
|
||||
(is_substr(result,it,_("is ")) || is_substr(result,it,_("the ")) ||
|
||||
is_substr(result,it,_("in ")) || is_substr(result,it,_("of ")) ||
|
||||
is_substr(result,it,_("to ")) || is_substr(result,it,_("at ")) ||
|
||||
is_substr(result,it,_("a " )))) {
|
||||
// Short words are not capitalized, keep lower case
|
||||
} else {
|
||||
*it = toUpper(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
String result = s;
|
||||
bool after_space = true;
|
||||
FOR_EACH_IT(it, result) {
|
||||
if (*it == _(' ') || *it == _('/')) {
|
||||
after_space = true;
|
||||
} else if (after_space) {
|
||||
after_space = false;
|
||||
// See http://trac.wxwidgets.org/ticket/12594
|
||||
//if (it != s.begin() &&
|
||||
if (s.begin() != it &&
|
||||
(is_substr(result,it,_("is ")) || is_substr(result,it,_("the ")) ||
|
||||
is_substr(result,it,_("in ")) || is_substr(result,it,_("of ")) ||
|
||||
is_substr(result,it,_("to ")) || is_substr(result,it,_("at ")) ||
|
||||
is_substr(result,it,_("a " )))) {
|
||||
// Short words are not capitalized, keep lower case
|
||||
} else {
|
||||
*it = toUpper(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String capitalize_sentence(const String& s) {
|
||||
String ret = s;//.Lower();
|
||||
if (!ret.empty()) {
|
||||
ret[0] = toUpper(ret[0]);
|
||||
}
|
||||
return ret;
|
||||
String ret = s;//.Lower();
|
||||
if (!ret.empty()) {
|
||||
ret[0] = toUpper(ret[0]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String canonical_name_form(const String& str) {
|
||||
String ret;
|
||||
ret.reserve(str.size());
|
||||
bool leading = true;
|
||||
FOR_EACH_CONST(c, str) {
|
||||
if ((c == _('_') || c == _(' '))) {
|
||||
ret += leading ? c : _(' ');
|
||||
} else {
|
||||
ret += c;
|
||||
leading = false;
|
||||
/*
|
||||
} else if (isAlnum(c) || c == _('-')) {
|
||||
ret += toLower(c);
|
||||
leading = false;
|
||||
} else {
|
||||
// ignore non alpha numeric*/
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
String ret;
|
||||
ret.reserve(str.size());
|
||||
bool leading = true;
|
||||
FOR_EACH_CONST(c, str) {
|
||||
if ((c == _('_') || c == _(' '))) {
|
||||
ret += leading ? c : _(' ');
|
||||
} else {
|
||||
ret += c;
|
||||
leading = false;
|
||||
/*
|
||||
} else if (isAlnum(c) || c == _('-')) {
|
||||
ret += toLower(c);
|
||||
leading = false;
|
||||
} else {
|
||||
// ignore non alpha numeric*/
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String singular_form(const String& str) {
|
||||
assert(str.size() > 1);
|
||||
assert(str.GetChar(str.size() - 1) == _('s')); // ends in 's'
|
||||
if (str.size() > 3 && is_substr(str, str.size()-3, _("ies"))) {
|
||||
return str.substr(0, str.size() - 3) + _("y");
|
||||
}
|
||||
return str.substr(0, str.size() - 1);
|
||||
assert(str.size() > 1);
|
||||
assert(str.GetChar(str.size() - 1) == _('s')); // ends in 's'
|
||||
if (str.size() > 3 && is_substr(str, str.size()-3, _("ies"))) {
|
||||
return str.substr(0, str.size() - 3) + _("y");
|
||||
}
|
||||
return str.substr(0, str.size() - 1);
|
||||
}
|
||||
|
||||
String remove_shortcut(const String& str) {
|
||||
size_t tab = str.find_last_of(_('\t'));
|
||||
if (tab == String::npos) return str;
|
||||
else return str.substr(0, tab);
|
||||
size_t tab = str.find_last_of(_('\t'));
|
||||
if (tab == String::npos) return str;
|
||||
else return str.substr(0, tab);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Comparing / finding
|
||||
@@ -263,213 +263,213 @@ char latin_E[] = "aabbbbbbccdddddd"
|
||||
|
||||
/// Remove accents from a (lowercase) character
|
||||
Char remove_accents(Char c) {
|
||||
char dec = ' ';
|
||||
if (c >= 0xC0) {
|
||||
if (c <= 0xFF) { // Latin 1
|
||||
dec = latin_1[c - 0xC0];
|
||||
} else if (c <= 0x17E) { // Latin extended A
|
||||
dec = latin_A[c - 0x100];
|
||||
} else if (c <= 0x180 && c <= 0x240) { // Latin extended B
|
||||
dec = latin_B[c - 0x180];
|
||||
} else if (c <= 0x1E00 && c <= 0x1EFF) { // Latin additional
|
||||
dec = latin_E[c - 0x1E00];
|
||||
}
|
||||
}
|
||||
return dec == ' ' ? toLower(c) : dec;
|
||||
char dec = ' ';
|
||||
if (c >= 0xC0) {
|
||||
if (c <= 0xFF) { // Latin 1
|
||||
dec = latin_1[c - 0xC0];
|
||||
} else if (c <= 0x17E) { // Latin extended A
|
||||
dec = latin_A[c - 0x100];
|
||||
} else if (c <= 0x180 && c <= 0x240) { // Latin extended B
|
||||
dec = latin_B[c - 0x180];
|
||||
} else if (c <= 0x1E00 && c <= 0x1EFF) { // Latin additional
|
||||
dec = latin_E[c - 0x1E00];
|
||||
}
|
||||
}
|
||||
return dec == ' ' ? toLower(c) : dec;
|
||||
}
|
||||
|
||||
/// Is c a precomposed character (not counting accent marks)
|
||||
/** If so, returns the second character of the decomposition */
|
||||
Char decompose_char2(Char c) {
|
||||
if (c < 0xC6) {
|
||||
return 0;
|
||||
} else if (c == 0xC6 || c == 0xE6 || c == 0x152 || c == 0x153 || c == 0x1E2 || c == 0x1E3 || c == 0x1FC || c == 0x1FD) {
|
||||
return _('e'); // "ae" or "oe"
|
||||
} else if (c == 0x132 || c == 0x133 || (c >= 0x1C7 && c <= 0x1CC)) {
|
||||
return _('j'); // "ij", "lj", "nj"
|
||||
} else if ((c >= 0x1C4 && c <= 0x1C6) || (c >= 0x1F1 && c <= 0x1F3)) {
|
||||
return _('z'); // "dz"
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
if (c < 0xC6) {
|
||||
return 0;
|
||||
} else if (c == 0xC6 || c == 0xE6 || c == 0x152 || c == 0x153 || c == 0x1E2 || c == 0x1E3 || c == 0x1FC || c == 0x1FD) {
|
||||
return _('e'); // "ae" or "oe"
|
||||
} else if (c == 0x132 || c == 0x133 || (c >= 0x1C7 && c <= 0x1CC)) {
|
||||
return _('j'); // "ij", "lj", "nj"
|
||||
} else if ((c >= 0x1C4 && c <= 0x1C6) || (c >= 0x1F1 && c <= 0x1F3)) {
|
||||
return _('z'); // "dz"
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int smart_compare(const String& sa, const String& sb) {
|
||||
bool in_num = false; // are we inside a number?
|
||||
bool lt = false; // is sa less than sb?
|
||||
bool eq = true; // so far is everything equal?
|
||||
size_t na = sa.size(), nb = sb.size();
|
||||
size_t pa = 0, pb = 0;
|
||||
for (; pa < na && pb < nb ; ++pa, ++pb) {
|
||||
Char a = sa.GetChar(pa), b = sb.GetChar(pb);
|
||||
next:
|
||||
bool da = isDigit(a), db = isDigit(b);
|
||||
if (da && db) {
|
||||
// compare numbers
|
||||
in_num = true;
|
||||
if (eq && a != b) {
|
||||
eq = false;
|
||||
lt = a < b;
|
||||
}
|
||||
} else if (in_num && da) {
|
||||
// comparing numbers, one is longer, therefore it is greater
|
||||
return 1;
|
||||
} else if (in_num && db) {
|
||||
return -1;
|
||||
} else if (in_num && !eq) {
|
||||
// two numbers of the same length, but not equal
|
||||
return lt ? -1 : 1;
|
||||
} else if (a != b) {
|
||||
// not a number
|
||||
eq = true; lt = false;
|
||||
if (a >= 0x20 && b >= 0x20) {
|
||||
// compare characters
|
||||
Char la = remove_accents(a), lb = remove_accents(b);
|
||||
// Decompose characters
|
||||
Char la2 = decompose_char2(a), lb2 = decompose_char2(b);
|
||||
// Compare
|
||||
if (la < lb) return -1;
|
||||
if (la > lb) return 1;
|
||||
// Remaining from decomposition
|
||||
if (la2 || lb2) {
|
||||
if (la2) a = la2;
|
||||
else {
|
||||
if (++pa >= na) return 1;
|
||||
a = sa.GetChar(pa);
|
||||
}
|
||||
if (lb2) b = lb2;
|
||||
else {
|
||||
if (++pb >= nb) return -1;
|
||||
b = sb.GetChar(pb);
|
||||
}
|
||||
goto next; // don't move to the next character in both strings
|
||||
}
|
||||
} else {
|
||||
// control characters
|
||||
if (a < b) return -1;
|
||||
else return 1;
|
||||
}
|
||||
}
|
||||
in_num = da && db;
|
||||
}
|
||||
// When we are at the end; shorter strings come first
|
||||
// This is true for normal string collation
|
||||
// and also when both end in a number and another digit follows
|
||||
if (in_num) {
|
||||
if (na - pa < nb - pb) {
|
||||
// number b continues?
|
||||
Char b = sb.GetChar(pb);
|
||||
if (isDigit(b) || eq) return -1; // b is longer
|
||||
} else if (na - pa > nb - pb) {
|
||||
Char a = sa.GetChar(pa);
|
||||
if (isDigit(a) || eq) return 1; // a is longer
|
||||
}
|
||||
return eq ? 0 : lt ? -1 : 1; // compare numbers
|
||||
} else {
|
||||
return na - pa == nb - pb ? 0
|
||||
: na - pa < nb - pb ? -1 : 1; // outside number, shorter string comes first
|
||||
}
|
||||
bool in_num = false; // are we inside a number?
|
||||
bool lt = false; // is sa less than sb?
|
||||
bool eq = true; // so far is everything equal?
|
||||
size_t na = sa.size(), nb = sb.size();
|
||||
size_t pa = 0, pb = 0;
|
||||
for (; pa < na && pb < nb ; ++pa, ++pb) {
|
||||
Char a = sa.GetChar(pa), b = sb.GetChar(pb);
|
||||
next:
|
||||
bool da = isDigit(a), db = isDigit(b);
|
||||
if (da && db) {
|
||||
// compare numbers
|
||||
in_num = true;
|
||||
if (eq && a != b) {
|
||||
eq = false;
|
||||
lt = a < b;
|
||||
}
|
||||
} else if (in_num && da) {
|
||||
// comparing numbers, one is longer, therefore it is greater
|
||||
return 1;
|
||||
} else if (in_num && db) {
|
||||
return -1;
|
||||
} else if (in_num && !eq) {
|
||||
// two numbers of the same length, but not equal
|
||||
return lt ? -1 : 1;
|
||||
} else if (a != b) {
|
||||
// not a number
|
||||
eq = true; lt = false;
|
||||
if (a >= 0x20 && b >= 0x20) {
|
||||
// compare characters
|
||||
Char la = remove_accents(a), lb = remove_accents(b);
|
||||
// Decompose characters
|
||||
Char la2 = decompose_char2(a), lb2 = decompose_char2(b);
|
||||
// Compare
|
||||
if (la < lb) return -1;
|
||||
if (la > lb) return 1;
|
||||
// Remaining from decomposition
|
||||
if (la2 || lb2) {
|
||||
if (la2) a = la2;
|
||||
else {
|
||||
if (++pa >= na) return 1;
|
||||
a = sa.GetChar(pa);
|
||||
}
|
||||
if (lb2) b = lb2;
|
||||
else {
|
||||
if (++pb >= nb) return -1;
|
||||
b = sb.GetChar(pb);
|
||||
}
|
||||
goto next; // don't move to the next character in both strings
|
||||
}
|
||||
} else {
|
||||
// control characters
|
||||
if (a < b) return -1;
|
||||
else return 1;
|
||||
}
|
||||
}
|
||||
in_num = da && db;
|
||||
}
|
||||
// When we are at the end; shorter strings come first
|
||||
// This is true for normal string collation
|
||||
// and also when both end in a number and another digit follows
|
||||
if (in_num) {
|
||||
if (na - pa < nb - pb) {
|
||||
// number b continues?
|
||||
Char b = sb.GetChar(pb);
|
||||
if (isDigit(b) || eq) return -1; // b is longer
|
||||
} else if (na - pa > nb - pb) {
|
||||
Char a = sa.GetChar(pa);
|
||||
if (isDigit(a) || eq) return 1; // a is longer
|
||||
}
|
||||
return eq ? 0 : lt ? -1 : 1; // compare numbers
|
||||
} else {
|
||||
return na - pa == nb - pb ? 0
|
||||
: na - pa < nb - pb ? -1 : 1; // outside number, shorter string comes first
|
||||
}
|
||||
}
|
||||
bool smart_less(const String& sa, const String& sb) {
|
||||
return smart_compare(sa, sb) == -1;
|
||||
return smart_compare(sa, sb) == -1;
|
||||
}
|
||||
bool smart_equal(const String& sa, const String& sb) {
|
||||
return smart_compare(sa, sb) == 0;
|
||||
return smart_compare(sa, sb) == 0;
|
||||
}
|
||||
|
||||
bool starts_with(const String& str, const String& start) {
|
||||
if (str.size() < start.size()) return false;
|
||||
FOR_EACH_2_CONST(a, str, b, start) {
|
||||
if (a != b) return false;
|
||||
}
|
||||
return true;
|
||||
if (str.size() < start.size()) return false;
|
||||
FOR_EACH_2_CONST(a, str, b, start) {
|
||||
if (a != b) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_substr(const String& str, size_t pos, const Char* cmp) {
|
||||
for (String::const_iterator it = str.begin() + pos ; *cmp && it < str.end() ; ++cmp, ++it) {
|
||||
if (*cmp != *it) return false;
|
||||
}
|
||||
return *cmp == _('\0');
|
||||
for (String::const_iterator it = str.begin() + pos ; *cmp && it < str.end() ; ++cmp, ++it) {
|
||||
if (*cmp != *it) return false;
|
||||
}
|
||||
return *cmp == _('\0');
|
||||
}
|
||||
bool is_substr(const String& str, size_t pos, const String& cmp) {
|
||||
return str.size() >= cmp.size() + pos && str.compare(pos, cmp.size(), cmp) == 0;
|
||||
return str.size() >= cmp.size() + pos && str.compare(pos, cmp.size(), cmp) == 0;
|
||||
}
|
||||
|
||||
|
||||
bool is_substr_i(const String& str, size_t pos, const Char* cmp) {
|
||||
for (String::const_iterator it = str.begin() + pos ; *cmp && it < str.end() ; ++cmp, ++it) {
|
||||
if (toLower(*cmp) != toLower(*it)) return false;
|
||||
}
|
||||
return *cmp == _('\0');
|
||||
for (String::const_iterator it = str.begin() + pos ; *cmp && it < str.end() ; ++cmp, ++it) {
|
||||
if (toLower(*cmp) != toLower(*it)) return false;
|
||||
}
|
||||
return *cmp == _('\0');
|
||||
}
|
||||
bool is_substr_i(const String& str, size_t pos, const String& cmp) {
|
||||
return is_substr_i(str, pos, static_cast<const Char*>(cmp.c_str()));
|
||||
return is_substr_i(str, pos, static_cast<const Char*>(cmp.c_str()));
|
||||
}
|
||||
|
||||
bool cannocial_name_compare(const String& as, const Char* b) {
|
||||
const Char* a = as.c_str();
|
||||
while (true) {
|
||||
if (*a != *b && !(*a == _(' ') && *b == _('_'))) return false;
|
||||
if (*a == _('\0')) return true;
|
||||
a++; b++;
|
||||
}
|
||||
const Char* a = as.c_str();
|
||||
while (true) {
|
||||
if (*a != *b && !(*a == _(' ') && *b == _('_'))) return false;
|
||||
if (*a == _('\0')) return true;
|
||||
a++; b++;
|
||||
}
|
||||
}
|
||||
|
||||
size_t find_i(const String& heystack, const String& needle) {
|
||||
if (needle.empty()) return 0;
|
||||
for (size_t i = 0 ; i + needle.size() <= heystack.size() ; ++i) {
|
||||
if (is_substr_i(heystack, i, needle)) return true;
|
||||
}
|
||||
return String::npos;
|
||||
if (needle.empty()) return 0;
|
||||
for (size_t i = 0 ; i + needle.size() <= heystack.size() ; ++i) {
|
||||
if (is_substr_i(heystack, i, needle)) return true;
|
||||
}
|
||||
return String::npos;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Regular expressions
|
||||
|
||||
/// Escape a single character for use in regular expressions
|
||||
String regex_escape(Char c) {
|
||||
if (c == _('(') || c == _(')') || c == _('[') || c == _(']') || c == _('{') ||
|
||||
c == _('.') || c == _('^') || c == _('$') || c == _('#') || c == _('\\') ||
|
||||
c == _('|') || c == _('+') || c == _('*') || c == _('?')) {
|
||||
// c needs to be escaped
|
||||
return _("\\") + String(1,c);
|
||||
} else {
|
||||
return String(1,c);
|
||||
}
|
||||
if (c == _('(') || c == _(')') || c == _('[') || c == _(']') || c == _('{') ||
|
||||
c == _('.') || c == _('^') || c == _('$') || c == _('#') || c == _('\\') ||
|
||||
c == _('|') || c == _('+') || c == _('*') || c == _('?')) {
|
||||
// c needs to be escaped
|
||||
return _("\\") + String(1,c);
|
||||
} else {
|
||||
return String(1,c);
|
||||
}
|
||||
}
|
||||
/// Escape a string for use in regular expressions
|
||||
String regex_escape(const String& s) {
|
||||
String ret;
|
||||
FOR_EACH_CONST(c,s) ret += regex_escape(static_cast<Char>(c));
|
||||
return ret;
|
||||
String ret;
|
||||
FOR_EACH_CONST(c,s) ret += regex_escape(static_cast<Char>(c));
|
||||
return ret;
|
||||
}
|
||||
|
||||
String make_non_capturing(const String& re) {
|
||||
String ret;
|
||||
bool escape = false, bracket = false, capture = false;
|
||||
FOR_EACH_CONST(c, re) {
|
||||
if (capture) {
|
||||
if (c != _('?')) {
|
||||
// change this capture into a non-capturing "(" by appending "?:"
|
||||
ret += _("?:");
|
||||
}
|
||||
capture = false;
|
||||
}
|
||||
if (escape) { // second char of escape sequence
|
||||
escape = false;
|
||||
} else if (c == _('\\')) { // start of escape sequence
|
||||
escape = true;
|
||||
} else if (c == _('[')) { // start of [...]
|
||||
bracket = true;
|
||||
} else if (c == _(']')) { // end of [...]
|
||||
bracket = false;
|
||||
} else if (bracket && c == _('(')) {
|
||||
// wx has a bug, it counts the '(' in "[(]" as a matching group
|
||||
// escape it so wx doesn't see it
|
||||
ret += _('\\');
|
||||
} else if (c == _('(')) { // start of capture?
|
||||
capture = true;
|
||||
}
|
||||
ret += c;
|
||||
}
|
||||
return ret;
|
||||
String ret;
|
||||
bool escape = false, bracket = false, capture = false;
|
||||
FOR_EACH_CONST(c, re) {
|
||||
if (capture) {
|
||||
if (c != _('?')) {
|
||||
// change this capture into a non-capturing "(" by appending "?:"
|
||||
ret += _("?:");
|
||||
}
|
||||
capture = false;
|
||||
}
|
||||
if (escape) { // second char of escape sequence
|
||||
escape = false;
|
||||
} else if (c == _('\\')) { // start of escape sequence
|
||||
escape = true;
|
||||
} else if (c == _('[')) { // start of [...]
|
||||
bracket = true;
|
||||
} else if (c == _(']')) { // end of [...]
|
||||
bracket = false;
|
||||
} else if (bracket && c == _('(')) {
|
||||
// wx has a bug, it counts the '(' in "[(]" as a matching group
|
||||
// escape it so wx doesn't see it
|
||||
ret += _('\\');
|
||||
} else if (c == _('(')) { // start of capture?
|
||||
capture = true;
|
||||
}
|
||||
ret += c;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
+36
-36
@@ -32,9 +32,9 @@ DECLARE_TYPEOF_NO_REV(String); // iterating over characters in a string
|
||||
|
||||
/// u if UNICODE is defined, a otherwise
|
||||
#ifdef UNICODE
|
||||
# define IF_UNICODE(u,a) u
|
||||
# define IF_UNICODE(u,a) u
|
||||
#else
|
||||
# define IF_UNICODE(u,a) a
|
||||
# define IF_UNICODE(u,a) a
|
||||
#endif
|
||||
|
||||
#undef _
|
||||
@@ -43,7 +43,7 @@ DECLARE_TYPEOF_NO_REV(String); // iterating over characters in a string
|
||||
|
||||
/// The character type used
|
||||
typedef IF_UNICODE(wchar_t, char) Char;
|
||||
|
||||
|
||||
/// Decode a UTF8 string
|
||||
/** In non-unicode builds the input is considered to be an incorrectly encoded utf8 string.
|
||||
* In unicode builds it is a normal string, utf8 already decoded.
|
||||
@@ -62,25 +62,25 @@ void writeUTF8(wxTextOutputStream& stream, const String& str);
|
||||
|
||||
/// Some constants we like to use
|
||||
#ifdef UNICODE
|
||||
#define LEFT_ANGLE_BRACKET _("\x2039")
|
||||
#define RIGHT_ANGLE_BRACKET _("\x203A")
|
||||
#define LEFT_SINGLE_QUOTE _('\x2018')
|
||||
#define RIGHT_SINGLE_QUOTE _('\x2019')
|
||||
#define LEFT_DOUBLE_QUOTE _('\x201C')
|
||||
#define RIGHT_DOUBLE_QUOTE _('\x201D')
|
||||
#define EN_DASH _('\x2013')
|
||||
#define EM_DASH _('\x2014')
|
||||
#define CONNECTION_SPACE _('\xEB00') // in private use area, untags to ' '
|
||||
#define LEFT_ANGLE_BRACKET _("\x2039")
|
||||
#define RIGHT_ANGLE_BRACKET _("\x203A")
|
||||
#define LEFT_SINGLE_QUOTE _('\x2018')
|
||||
#define RIGHT_SINGLE_QUOTE _('\x2019')
|
||||
#define LEFT_DOUBLE_QUOTE _('\x201C')
|
||||
#define RIGHT_DOUBLE_QUOTE _('\x201D')
|
||||
#define EN_DASH _('\x2013')
|
||||
#define EM_DASH _('\x2014')
|
||||
#define CONNECTION_SPACE _('\xEB00') // in private use area, untags to ' '
|
||||
#else
|
||||
#define LEFT_ANGLE_BRACKET _("<")
|
||||
#define RIGHT_ANGLE_BRACKET _(">")
|
||||
#define LEFT_SINGLE_QUOTE _('\'')
|
||||
#define RIGHT_SINGLE_QUOTE _('\'')
|
||||
#define LEFT_DOUBLE_QUOTE _('\"')
|
||||
#define RIGHT_DOUBLE_QUOTE _('\"')
|
||||
#define EN_DASH _('-') // 150?
|
||||
#define EM_DASH _('-') // 151?
|
||||
#define CONNECTION_SPACE _(' ') // too bad
|
||||
#define LEFT_ANGLE_BRACKET _("<")
|
||||
#define RIGHT_ANGLE_BRACKET _(">")
|
||||
#define LEFT_SINGLE_QUOTE _('\'')
|
||||
#define RIGHT_SINGLE_QUOTE _('\'')
|
||||
#define LEFT_DOUBLE_QUOTE _('\"')
|
||||
#define RIGHT_DOUBLE_QUOTE _('\"')
|
||||
#define EN_DASH _('-') // 150?
|
||||
#define EM_DASH _('-') // 151?
|
||||
#define CONNECTION_SPACE _(' ') // too bad
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : Char functions
|
||||
@@ -94,24 +94,24 @@ inline bool isLower(Char c) { return IF_UNICODE( iswlower(c) , islower((unsigned
|
||||
inline bool isPunct(Char c) { return IF_UNICODE( iswpunct(c) , ispunct((unsigned char)c) ); }
|
||||
// Character conversions
|
||||
#ifdef _MSC_VER
|
||||
#define CHAR_FUNCTIONS_ARE_SLOW
|
||||
#define CHAR_FUNCTIONS_ARE_SLOW
|
||||
#endif
|
||||
#ifdef CHAR_FUNCTIONS_ARE_SLOW
|
||||
// These functions are slow as hell on msvc.
|
||||
// If also in other compilers, they can also use these routines.
|
||||
Char toLower(Char c);
|
||||
Char toUpper(Char c);
|
||||
inline bool isSpace(Char c) {
|
||||
if (c <= 128) {
|
||||
return (c >= 0x09 && c <= 0x0D) || c == 0x20;
|
||||
} else {
|
||||
return IF_UNICODE( iswspace(c) , isspace((unsigned char)c) ) || c == CONNECTION_SPACE;
|
||||
}
|
||||
}
|
||||
// These functions are slow as hell on msvc.
|
||||
// If also in other compilers, they can also use these routines.
|
||||
Char toLower(Char c);
|
||||
Char toUpper(Char c);
|
||||
inline bool isSpace(Char c) {
|
||||
if (c <= 128) {
|
||||
return (c >= 0x09 && c <= 0x0D) || c == 0x20;
|
||||
} else {
|
||||
return IF_UNICODE( iswspace(c) , isspace((unsigned char)c) ) || c == CONNECTION_SPACE;
|
||||
}
|
||||
}
|
||||
#else
|
||||
inline Char toLower(Char c) { return IF_UNICODE( towlower(c) , tolower(c) ); }
|
||||
inline Char toUpper(Char c) { return IF_UNICODE( towupper(c) , toupper(c) ); }
|
||||
inline bool isSpace(Char c) { return IF_UNICODE( iswspace(c) , isspace((unsigned char)c) ) || c == CONNECTION_SPACE; }
|
||||
inline Char toLower(Char c) { return IF_UNICODE( towlower(c) , tolower(c) ); }
|
||||
inline Char toUpper(Char c) { return IF_UNICODE( towupper(c) , toupper(c) ); }
|
||||
inline bool isSpace(Char c) { return IF_UNICODE( iswspace(c) , isspace((unsigned char)c) ) || c == CONNECTION_SPACE; }
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : String utilities
|
||||
|
||||
+508
-508
File diff suppressed because it is too large
Load Diff
@@ -98,11 +98,11 @@ String anti_tag(const String& tag);
|
||||
|
||||
/// Directions of cursor movement
|
||||
enum Movement
|
||||
{ MOVE_LEFT = -2 ///< Always move the cursor to the left
|
||||
, MOVE_LEFT_OPT = -1 ///< Move the cursor to the left, but a position inside a tag is the same as that before
|
||||
, MOVE_MID = 0 ///< Move in whichever direction the distance to move is shorter (TODO: define shorter)
|
||||
, MOVE_RIGHT_OPT = 1 ///< Move the cursor to the left, but a position inside a tag is the same as that after
|
||||
, MOVE_RIGHT = 2 ///< Always move the cursor to the right
|
||||
{ MOVE_LEFT = -2 ///< Always move the cursor to the left
|
||||
, MOVE_LEFT_OPT = -1 ///< Move the cursor to the left, but a position inside a tag is the same as that before
|
||||
, MOVE_MID = 0 ///< Move in whichever direction the distance to move is shorter (TODO: define shorter)
|
||||
, MOVE_RIGHT_OPT = 1 ///< Move the cursor to the left, but a position inside a tag is the same as that after
|
||||
, MOVE_RIGHT = 2 ///< Always move the cursor to the right
|
||||
};
|
||||
|
||||
/// Find the cursor position corresponding to the given character index.
|
||||
@@ -190,9 +190,9 @@ String tagged_substr_replace(const String& input, size_t start, size_t end, cons
|
||||
*/
|
||||
void check_tagged(const String& str, bool check_balance = true);
|
||||
#ifdef _DEBUG
|
||||
#define assert_tagged check_tagged
|
||||
#define assert_tagged check_tagged
|
||||
#else
|
||||
inline void assert_tagged(const String& str, bool check_balance = true){}
|
||||
inline void assert_tagged(const String& str, bool check_balance = true){}
|
||||
#endif
|
||||
|
||||
/// Simplify a tagged string
|
||||
|
||||
+18
-18
@@ -14,31 +14,31 @@
|
||||
|
||||
template <>
|
||||
VCSP read_new<VCS>(Reader& reader) {
|
||||
// there must be a type specified
|
||||
String type;
|
||||
reader.handle(_("type"), type);
|
||||
if (type == _("none")) return intrusive(new VCS);
|
||||
else if (type == _("subversion")) return intrusive(new SubversionVCS);
|
||||
else if (type.empty()) {
|
||||
reader.warning(_ERROR_1_("expected key", _("version control system")));
|
||||
throw ParseError(_ERROR_("aborting parsing"));
|
||||
} else {
|
||||
reader.warning(format_string(_("Unsupported version control type: '%s'"), type));
|
||||
throw ParseError(_ERROR_("aborting parsing"));
|
||||
}
|
||||
// there must be a type specified
|
||||
String type;
|
||||
reader.handle(_("type"), type);
|
||||
if (type == _("none")) return intrusive(new VCS);
|
||||
else if (type == _("subversion")) return intrusive(new SubversionVCS);
|
||||
else if (type.empty()) {
|
||||
reader.warning(_ERROR_1_("expected key", _("version control system")));
|
||||
throw ParseError(_ERROR_("aborting parsing"));
|
||||
} else {
|
||||
reader.warning(format_string(_("Unsupported version control type: '%s'"), type));
|
||||
throw ParseError(_ERROR_("aborting parsing"));
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_REFLECTION(VCS) {
|
||||
REFLECT_IF_NOT_READING {
|
||||
String type = _("none");
|
||||
REFLECT(type);
|
||||
}
|
||||
REFLECT_IF_NOT_READING {
|
||||
String type = _("none");
|
||||
REFLECT(type);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void Reader::handle(VCSP& pointer) {
|
||||
pointer = read_new<VCS>(*this);
|
||||
handle(*pointer);
|
||||
pointer = read_new<VCS>(*this);
|
||||
handle(*pointer);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+13
-13
@@ -30,19 +30,19 @@ void Reader::handle(VCSP& pointer);
|
||||
class VCS : public IntrusivePtrVirtualBase
|
||||
{
|
||||
public:
|
||||
/// Add a file - it's assumed to already have been created
|
||||
virtual void addFile (const wxFileName& filename) {
|
||||
}
|
||||
/// Rename a file (currently unused)
|
||||
virtual void moveFile (const wxFileName& source, const wxFileName& destination) {
|
||||
wxRenameFile(source.GetFullName(), destination.GetFullName());
|
||||
}
|
||||
/// Delete a file right off the disk
|
||||
virtual void removeFile (const wxFileName& filename) {
|
||||
wxRemoveFile(filename.GetFullName());
|
||||
}
|
||||
|
||||
DECLARE_REFLECTION_VIRTUAL();
|
||||
/// Add a file - it's assumed to already have been created
|
||||
virtual void addFile (const wxFileName& filename) {
|
||||
}
|
||||
/// Rename a file (currently unused)
|
||||
virtual void moveFile (const wxFileName& source, const wxFileName& destination) {
|
||||
wxRenameFile(source.GetFullName(), destination.GetFullName());
|
||||
}
|
||||
/// Delete a file right off the disk
|
||||
virtual void removeFile (const wxFileName& filename) {
|
||||
wxRemoveFile(filename.GetFullName());
|
||||
}
|
||||
|
||||
DECLARE_REFLECTION_VIRTUAL();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+36
-36
@@ -12,57 +12,57 @@
|
||||
// ----------------------------------------------------------------------------- : SVN File Manipulation
|
||||
|
||||
bool run_svn(const Char** arguments) {
|
||||
switch (wxExecute(const_cast<Char**>(arguments), wxEXEC_SYNC)) { // Yuck, const_cast
|
||||
// Success
|
||||
case 0:
|
||||
return true;
|
||||
// Couldn't run SVN
|
||||
case -1:
|
||||
handle_error(String(_("Can't run SVN.")));
|
||||
return false;
|
||||
// SVN error
|
||||
default:
|
||||
handle_error(String(_("SVN encountered an error")));
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (wxExecute(const_cast<Char**>(arguments), wxEXEC_SYNC)) { // Yuck, const_cast
|
||||
// Success
|
||||
case 0:
|
||||
return true;
|
||||
// Couldn't run SVN
|
||||
case -1:
|
||||
handle_error(String(_("Can't run SVN.")));
|
||||
return false;
|
||||
// SVN error
|
||||
default:
|
||||
handle_error(String(_("SVN encountered an error")));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SubversionVCS::addFile(const wxFileName& filename)
|
||||
{
|
||||
String name = filename.GetFullPath();
|
||||
const Char* name_c[] = {_("svn"), _("add"), name.c_str(), nullptr};
|
||||
if (!run_svn(name_c)) {
|
||||
VCS::addFile(filename);
|
||||
}
|
||||
String name = filename.GetFullPath();
|
||||
const Char* name_c[] = {_("svn"), _("add"), name.c_str(), nullptr};
|
||||
if (!run_svn(name_c)) {
|
||||
VCS::addFile(filename);
|
||||
}
|
||||
}
|
||||
|
||||
void SubversionVCS::moveFile(const wxFileName& source, const wxFileName& dest)
|
||||
{
|
||||
String source_name = source.GetFullPath(), dest_name = dest.GetFullPath();
|
||||
const Char* name_c[] = {_("svn"), _("mv"), source_name.c_str(), dest_name.c_str(), nullptr};
|
||||
if (!run_svn(name_c)) {
|
||||
VCS::moveFile(source, dest);
|
||||
}
|
||||
String source_name = source.GetFullPath(), dest_name = dest.GetFullPath();
|
||||
const Char* name_c[] = {_("svn"), _("mv"), source_name.c_str(), dest_name.c_str(), nullptr};
|
||||
if (!run_svn(name_c)) {
|
||||
VCS::moveFile(source, dest);
|
||||
}
|
||||
}
|
||||
|
||||
void SubversionVCS::removeFile(const wxFileName& filename)
|
||||
{
|
||||
String name = filename.GetFullPath();
|
||||
const Char* name_c[] = {_("svn"), _("rm"), name.c_str(), nullptr};
|
||||
queue_message(MESSAGE_WARNING, String(name_c[0]) + name_c[1] + name_c[2]);
|
||||
// TODO: do we really need to remove the file before calling "svn remove"?
|
||||
VCS::removeFile(filename);
|
||||
if (!run_svn(name_c)) {
|
||||
VCS::removeFile(filename);
|
||||
}
|
||||
String name = filename.GetFullPath();
|
||||
const Char* name_c[] = {_("svn"), _("rm"), name.c_str(), nullptr};
|
||||
queue_message(MESSAGE_WARNING, String(name_c[0]) + name_c[1] + name_c[2]);
|
||||
// TODO: do we really need to remove the file before calling "svn remove"?
|
||||
VCS::removeFile(filename);
|
||||
if (!run_svn(name_c)) {
|
||||
VCS::removeFile(filename);
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_REFLECTION(SubversionVCS) {
|
||||
REFLECT_IF_NOT_READING {
|
||||
String type = _("subversion");
|
||||
REFLECT(type);
|
||||
}
|
||||
REFLECT_IF_NOT_READING {
|
||||
String type = _("subversion");
|
||||
REFLECT(type);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
class SubversionVCS : public VCS {
|
||||
public:
|
||||
virtual void addFile (const wxFileName& filename);
|
||||
virtual void moveFile (const wxFileName& source, const wxFileName& destination);
|
||||
virtual void removeFile (const wxFileName& filename);
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
virtual void addFile (const wxFileName& filename);
|
||||
virtual void moveFile (const wxFileName& source, const wxFileName& destination);
|
||||
virtual void removeFile (const wxFileName& filename);
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+105
-105
@@ -19,7 +19,7 @@
|
||||
/** Intentionally uses slightly less then 0.5, to give a more consistent result
|
||||
* when for instance something like "x/2" is used. */
|
||||
inline int to_int(double d) {
|
||||
return static_cast<int>(d > 0 ? d + 0.4999995 : d - 0.4999995);
|
||||
return static_cast<int>(d > 0 ? d + 0.4999995 : d - 0.4999995);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Vector2D
|
||||
@@ -27,118 +27,118 @@ inline int to_int(double d) {
|
||||
/// A simple 2d vector class
|
||||
class Vector2D {
|
||||
public:
|
||||
/// Coordinates of this vector
|
||||
double x, y;
|
||||
|
||||
/// Default contructor
|
||||
inline Vector2D()
|
||||
: x(0), y(0) {}
|
||||
/// Contructor with given x and y values
|
||||
inline Vector2D(double x, double y)
|
||||
: x(x), y(y) {}
|
||||
|
||||
/// Addition of two vectors
|
||||
inline Vector2D operator + (Vector2D p2) const {
|
||||
return Vector2D(x + p2.x, y + p2.y);
|
||||
}
|
||||
/// Addition of two vectors
|
||||
inline void operator += (Vector2D p2) {
|
||||
x += p2.x; y += p2.y;
|
||||
}
|
||||
/// Subtract two vectors
|
||||
inline Vector2D operator - (Vector2D p2) const {
|
||||
return Vector2D(x - p2.x, y - p2.y);
|
||||
}
|
||||
/// Subtract two vectors
|
||||
inline void operator -= (Vector2D p2) {
|
||||
x -= p2.x; y -= p2.y;
|
||||
}
|
||||
/// Invert a vector
|
||||
inline Vector2D operator - () const {
|
||||
return Vector2D(-x, -y);
|
||||
}
|
||||
|
||||
/// Multiply with a scalar
|
||||
inline Vector2D operator * (double r) const {
|
||||
return Vector2D(x * r, y * r);
|
||||
}
|
||||
inline Vector2D operator * (int r) const {
|
||||
return Vector2D(x * r, y * r);
|
||||
}
|
||||
/// Multiply with a scalar
|
||||
inline void operator *= (double r) {
|
||||
x *= r; y *= r;
|
||||
}
|
||||
/// Divide by a scalar
|
||||
inline Vector2D operator / (double r) const {
|
||||
return Vector2D(x / r, y / r);
|
||||
}
|
||||
/// Divide by a scalar
|
||||
inline void operator /= (double r) {
|
||||
x /= r; y /= r;
|
||||
}
|
||||
|
||||
/// Piecewise multiplication
|
||||
inline Vector2D mul(Vector2D p2) {
|
||||
return Vector2D(x * p2.x, y * p2.y);
|
||||
}
|
||||
/// Piecewise division
|
||||
inline Vector2D div(Vector2D p2) {
|
||||
return Vector2D(x / p2.x, y / p2.y);
|
||||
}
|
||||
|
||||
/// Returns the square of the length of this vector
|
||||
inline double lengthSqr() const {
|
||||
return x*x + y*y;
|
||||
}
|
||||
/// Returns the length of this vector
|
||||
inline double length() const {
|
||||
return sqrt(lengthSqr());
|
||||
}
|
||||
/// Returns a normalized version of this vector
|
||||
/** i.e. length() == 1 */
|
||||
inline Vector2D normalized() const {
|
||||
return *this / length();
|
||||
}
|
||||
/// Angle between this vector and the x axis
|
||||
inline Radians angle() const {
|
||||
return atan2(y,x);
|
||||
}
|
||||
|
||||
inline operator wxPoint() const {
|
||||
return wxPoint(to_int(x), to_int(y));
|
||||
}
|
||||
|
||||
// Vector at infinity
|
||||
static inline Vector2D infinity() {
|
||||
double inf = numeric_limits<double>::infinity();
|
||||
return Vector2D(inf, inf);
|
||||
}
|
||||
/// Coordinates of this vector
|
||||
double x, y;
|
||||
|
||||
/// Default contructor
|
||||
inline Vector2D()
|
||||
: x(0), y(0) {}
|
||||
/// Contructor with given x and y values
|
||||
inline Vector2D(double x, double y)
|
||||
: x(x), y(y) {}
|
||||
|
||||
/// Addition of two vectors
|
||||
inline Vector2D operator + (Vector2D p2) const {
|
||||
return Vector2D(x + p2.x, y + p2.y);
|
||||
}
|
||||
/// Addition of two vectors
|
||||
inline void operator += (Vector2D p2) {
|
||||
x += p2.x; y += p2.y;
|
||||
}
|
||||
/// Subtract two vectors
|
||||
inline Vector2D operator - (Vector2D p2) const {
|
||||
return Vector2D(x - p2.x, y - p2.y);
|
||||
}
|
||||
/// Subtract two vectors
|
||||
inline void operator -= (Vector2D p2) {
|
||||
x -= p2.x; y -= p2.y;
|
||||
}
|
||||
/// Invert a vector
|
||||
inline Vector2D operator - () const {
|
||||
return Vector2D(-x, -y);
|
||||
}
|
||||
|
||||
/// Multiply with a scalar
|
||||
inline Vector2D operator * (double r) const {
|
||||
return Vector2D(x * r, y * r);
|
||||
}
|
||||
inline Vector2D operator * (int r) const {
|
||||
return Vector2D(x * r, y * r);
|
||||
}
|
||||
/// Multiply with a scalar
|
||||
inline void operator *= (double r) {
|
||||
x *= r; y *= r;
|
||||
}
|
||||
/// Divide by a scalar
|
||||
inline Vector2D operator / (double r) const {
|
||||
return Vector2D(x / r, y / r);
|
||||
}
|
||||
/// Divide by a scalar
|
||||
inline void operator /= (double r) {
|
||||
x /= r; y /= r;
|
||||
}
|
||||
|
||||
/// Piecewise multiplication
|
||||
inline Vector2D mul(Vector2D p2) {
|
||||
return Vector2D(x * p2.x, y * p2.y);
|
||||
}
|
||||
/// Piecewise division
|
||||
inline Vector2D div(Vector2D p2) {
|
||||
return Vector2D(x / p2.x, y / p2.y);
|
||||
}
|
||||
|
||||
/// Returns the square of the length of this vector
|
||||
inline double lengthSqr() const {
|
||||
return x*x + y*y;
|
||||
}
|
||||
/// Returns the length of this vector
|
||||
inline double length() const {
|
||||
return sqrt(lengthSqr());
|
||||
}
|
||||
/// Returns a normalized version of this vector
|
||||
/** i.e. length() == 1 */
|
||||
inline Vector2D normalized() const {
|
||||
return *this / length();
|
||||
}
|
||||
/// Angle between this vector and the x axis
|
||||
inline Radians angle() const {
|
||||
return atan2(y,x);
|
||||
}
|
||||
|
||||
inline operator wxPoint() const {
|
||||
return wxPoint(to_int(x), to_int(y));
|
||||
}
|
||||
|
||||
// Vector at infinity
|
||||
static inline Vector2D infinity() {
|
||||
double inf = numeric_limits<double>::infinity();
|
||||
return Vector2D(inf, inf);
|
||||
}
|
||||
};
|
||||
|
||||
/// Inner product of two vectors
|
||||
inline double dot(const Vector2D& a, const Vector2D& b) {
|
||||
return (a.x * b.x) + (a.y * b.y);
|
||||
return (a.x * b.x) + (a.y * b.y);
|
||||
}
|
||||
/// Length of the outer product of two vectors
|
||||
inline double cross(const Vector2D& a, const Vector2D& b) {
|
||||
return (a.x * b.y) - (a.y * b.x);
|
||||
return (a.x * b.y) - (a.y * b.x);
|
||||
}
|
||||
|
||||
/// Piecewise minimum
|
||||
inline Vector2D piecewise_min(const Vector2D& a, const Vector2D& b) {
|
||||
return Vector2D(
|
||||
a.x < b.x ? a.x : b.x,
|
||||
a.y < b.y ? a.y : b.y
|
||||
);
|
||||
return Vector2D(
|
||||
a.x < b.x ? a.x : b.x,
|
||||
a.y < b.y ? a.y : b.y
|
||||
);
|
||||
}
|
||||
|
||||
/// Piecewise maximum
|
||||
inline Vector2D piecewise_max(const Vector2D& a, const Vector2D& b) {
|
||||
return Vector2D(
|
||||
a.x < b.x ? b.x : a.x,
|
||||
a.y < b.y ? b.y : a.y
|
||||
);
|
||||
return Vector2D(
|
||||
a.x < b.x ? b.x : a.x,
|
||||
a.y < b.y ? b.y : a.y
|
||||
);
|
||||
}
|
||||
|
||||
inline Vector2D operator * (double a, const Vector2D& b) { return b * a; }
|
||||
@@ -148,20 +148,20 @@ inline Vector2D operator * (double a, const Vector2D& b) { return b * a; }
|
||||
/// A two dimensional transformation matrix, simply two vectors
|
||||
class Matrix2D {
|
||||
public:
|
||||
Vector2D mx, my;
|
||||
|
||||
inline Matrix2D() : mx(1,0), my(0,1) {}
|
||||
inline Matrix2D(const Vector2D& mx, const Vector2D& my) : mx(mx), my(my) {}
|
||||
inline Matrix2D(double a, double b, double c, double d) : mx(a,b), my(c,d) {}
|
||||
Vector2D mx, my;
|
||||
|
||||
inline Matrix2D() : mx(1,0), my(0,1) {}
|
||||
inline Matrix2D(const Vector2D& mx, const Vector2D& my) : mx(mx), my(my) {}
|
||||
inline Matrix2D(double a, double b, double c, double d) : mx(a,b), my(c,d) {}
|
||||
};
|
||||
|
||||
/// vector-matrix product
|
||||
inline Vector2D operator * (const Vector2D& a, const Matrix2D& m) {
|
||||
return Vector2D(dot(a,m.mx), dot(a,m.my));
|
||||
return Vector2D(dot(a,m.mx), dot(a,m.my));
|
||||
}
|
||||
/// vector-matrix product
|
||||
inline Matrix2D operator * (const Matrix2D& a, const Matrix2D& m) {
|
||||
return Matrix2D(a.mx * m, a.my * m);
|
||||
return Matrix2D(a.mx * m, a.my * m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+19
-19
@@ -15,36 +15,36 @@
|
||||
UInt Version::toNumber() const { return version; }
|
||||
|
||||
String Version::toString() const {
|
||||
if (version > 20000000) {
|
||||
// major > 2000, the version is a date, use ISO notation
|
||||
return String::Format(_("%04d-%02d-%02d"),
|
||||
(version / 10000) ,
|
||||
(version / 100) % 100,
|
||||
(version / 1) % 100);
|
||||
} else {
|
||||
return String::Format(_("%d.%d.%d"),
|
||||
(version / 10000) ,
|
||||
(version / 100) % 100,
|
||||
(version / 1) % 100);
|
||||
}
|
||||
if (version > 20000000) {
|
||||
// major > 2000, the version is a date, use ISO notation
|
||||
return String::Format(_("%04d-%02d-%02d"),
|
||||
(version / 10000) ,
|
||||
(version / 100) % 100,
|
||||
(version / 1) % 100);
|
||||
} else {
|
||||
return String::Format(_("%d.%d.%d"),
|
||||
(version / 10000) ,
|
||||
(version / 100) % 100,
|
||||
(version / 1) % 100);
|
||||
}
|
||||
}
|
||||
|
||||
Version Version::fromString(const String& version) {
|
||||
UInt major = 0, minor = 0, build = 0;
|
||||
if (wxSscanf(version, _("%u.%u.%u"), &major, &minor, &build)<=1) // a.b.c style
|
||||
wxSscanf(version, _("%u-%u-%u"), &major, &minor, &build); // date style
|
||||
return Version(major * 10000 + minor * 100 + build);
|
||||
UInt major = 0, minor = 0, build = 0;
|
||||
if (wxSscanf(version, _("%u.%u.%u"), &major, &minor, &build)<=1) // a.b.c style
|
||||
wxSscanf(version, _("%u-%u-%u"), &major, &minor, &build); // date style
|
||||
return Version(major * 10000 + minor * 100 + build);
|
||||
}
|
||||
|
||||
|
||||
template <> void Reader::handle(Version& v) {
|
||||
v = Version::fromString(getValue());
|
||||
v = Version::fromString(getValue());
|
||||
}
|
||||
template <> void Writer::handle(const Version& v) {
|
||||
handle(v.toString());
|
||||
handle(v.toString());
|
||||
}
|
||||
template <> void GetDefaultMember::handle(const Version& v) {
|
||||
handle(v.toNumber());
|
||||
handle(v.toNumber());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Versions
|
||||
|
||||
+19
-19
@@ -22,26 +22,26 @@
|
||||
/// A version number
|
||||
struct Version {
|
||||
public:
|
||||
Version() : version(0) {}
|
||||
Version(UInt version) : version(version) {}
|
||||
|
||||
inline bool operator == (Version v) const { return version == v.version; }
|
||||
inline bool operator != (Version v) const { return version != v.version; }
|
||||
inline bool operator < (Version v) const { return version < v.version; }
|
||||
inline bool operator <= (Version v) const { return version <= v.version; }
|
||||
inline bool operator > (Version v) const { return version > v.version; }
|
||||
inline bool operator >= (Version v) const { return version >= v.version; }
|
||||
|
||||
/// Convert a version number to a string
|
||||
String toString() const;
|
||||
/// Get the version number as an integer number
|
||||
UInt toNumber() const;
|
||||
|
||||
/// Convert a string to a version number
|
||||
static Version fromString(const String& version);
|
||||
|
||||
Version() : version(0) {}
|
||||
Version(UInt version) : version(version) {}
|
||||
|
||||
inline bool operator == (Version v) const { return version == v.version; }
|
||||
inline bool operator != (Version v) const { return version != v.version; }
|
||||
inline bool operator < (Version v) const { return version < v.version; }
|
||||
inline bool operator <= (Version v) const { return version <= v.version; }
|
||||
inline bool operator > (Version v) const { return version > v.version; }
|
||||
inline bool operator >= (Version v) const { return version >= v.version; }
|
||||
|
||||
/// Convert a version number to a string
|
||||
String toString() const;
|
||||
/// Get the version number as an integer number
|
||||
UInt toNumber() const;
|
||||
|
||||
/// Convert a string to a version number
|
||||
static Version fromString(const String& version);
|
||||
|
||||
private:
|
||||
UInt version; ///< Version number encoded as aabbcc, where a=major, b=minor, c=revision
|
||||
UInt version; ///< Version number encoded as aabbcc, where a=major, b=minor, c=revision
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Versions
|
||||
|
||||
+237
-237
@@ -18,71 +18,71 @@
|
||||
|
||||
/// Window ids for menus and toolbars
|
||||
enum MenuID {
|
||||
|
||||
ID_MENU_MIN = 0
|
||||
, ID_MENU_MAX = 999
|
||||
|
||||
ID_MENU_MIN = 0
|
||||
, ID_MENU_MAX = 999
|
||||
|
||||
// File menu
|
||||
, ID_FILE_NEW = wxID_NEW
|
||||
, ID_FILE_OPEN = wxID_OPEN
|
||||
, ID_FILE_SAVE = wxID_SAVE
|
||||
, ID_FILE_SAVE_AS = wxID_SAVEAS
|
||||
, ID_FILE_STORE = 1
|
||||
, ID_FILE_EXIT = wxID_EXIT
|
||||
, ID_FILE_EXPORT = 2
|
||||
, ID_FILE_EXPORT_HTML = 3
|
||||
, ID_FILE_EXPORT_IMAGE = 4
|
||||
, ID_FILE_EXPORT_IMAGES = 5
|
||||
, ID_FILE_EXPORT_APPR = 6
|
||||
, ID_FILE_EXPORT_MWS = 7
|
||||
, ID_FILE_PRINT = wxID_PRINT
|
||||
, ID_FILE_PRINT_PREVIEW = wxID_PREVIEW
|
||||
, ID_FILE_INSPECT = 8
|
||||
, ID_FILE_RELOAD = 9
|
||||
, ID_FILE_RECENT = wxID_FILE1
|
||||
, ID_FILE_RECENT_MAX = wxID_FILE9
|
||||
, ID_FILE_CHECK_UPDATES = 10
|
||||
, ID_FILE_PROFILER = 11
|
||||
// File menu
|
||||
, ID_FILE_NEW = wxID_NEW
|
||||
, ID_FILE_OPEN = wxID_OPEN
|
||||
, ID_FILE_SAVE = wxID_SAVE
|
||||
, ID_FILE_SAVE_AS = wxID_SAVEAS
|
||||
, ID_FILE_STORE = 1
|
||||
, ID_FILE_EXIT = wxID_EXIT
|
||||
, ID_FILE_EXPORT = 2
|
||||
, ID_FILE_EXPORT_HTML = 3
|
||||
, ID_FILE_EXPORT_IMAGE = 4
|
||||
, ID_FILE_EXPORT_IMAGES = 5
|
||||
, ID_FILE_EXPORT_APPR = 6
|
||||
, ID_FILE_EXPORT_MWS = 7
|
||||
, ID_FILE_PRINT = wxID_PRINT
|
||||
, ID_FILE_PRINT_PREVIEW = wxID_PREVIEW
|
||||
, ID_FILE_INSPECT = 8
|
||||
, ID_FILE_RELOAD = 9
|
||||
, ID_FILE_RECENT = wxID_FILE1
|
||||
, ID_FILE_RECENT_MAX = wxID_FILE9
|
||||
, ID_FILE_CHECK_UPDATES = 10
|
||||
, ID_FILE_PROFILER = 11
|
||||
|
||||
// Edit menu
|
||||
, ID_EDIT_UNDO = wxID_UNDO
|
||||
, ID_EDIT_REDO = wxID_REDO
|
||||
, ID_EDIT_CUT = wxID_CUT
|
||||
, ID_EDIT_COPY = wxID_COPY
|
||||
, ID_EDIT_PASTE = wxID_PASTE
|
||||
, ID_EDIT_DELETE = 101
|
||||
, ID_EDIT_FIND = wxID_FIND
|
||||
, ID_EDIT_FIND_NEXT = 103
|
||||
, ID_EDIT_REPLACE = wxID_REPLACE
|
||||
, ID_EDIT_AUTO_REPLACE = 104
|
||||
, ID_EDIT_PREFERENCES = 105
|
||||
// Edit menu
|
||||
, ID_EDIT_UNDO = wxID_UNDO
|
||||
, ID_EDIT_REDO = wxID_REDO
|
||||
, ID_EDIT_CUT = wxID_CUT
|
||||
, ID_EDIT_COPY = wxID_COPY
|
||||
, ID_EDIT_PASTE = wxID_PASTE
|
||||
, ID_EDIT_DELETE = 101
|
||||
, ID_EDIT_FIND = wxID_FIND
|
||||
, ID_EDIT_FIND_NEXT = 103
|
||||
, ID_EDIT_REPLACE = wxID_REPLACE
|
||||
, ID_EDIT_AUTO_REPLACE = 104
|
||||
, ID_EDIT_PREFERENCES = 105
|
||||
|
||||
// Window menu (MainWindow)
|
||||
, ID_WINDOW_NEW = 201
|
||||
, ID_WINDOW_MIN = 202
|
||||
, ID_WINDOW_CARDS = ID_WINDOW_MIN + 0
|
||||
, ID_WINDOW_SET = ID_WINDOW_MIN + 1
|
||||
, ID_WINDOW_STYLE = ID_WINDOW_MIN + 2
|
||||
, ID_WINDOW_KEYWORDS = ID_WINDOW_MIN + 3
|
||||
, ID_WINDOW_STATS = ID_WINDOW_MIN + 4
|
||||
, ID_WINDOW_RANDOM_PACK = ID_WINDOW_MIN + 5
|
||||
, ID_WINDOW_MAX = 220
|
||||
// Window menu (MainWindow)
|
||||
, ID_WINDOW_NEW = 201
|
||||
, ID_WINDOW_MIN = 202
|
||||
, ID_WINDOW_CARDS = ID_WINDOW_MIN + 0
|
||||
, ID_WINDOW_SET = ID_WINDOW_MIN + 1
|
||||
, ID_WINDOW_STYLE = ID_WINDOW_MIN + 2
|
||||
, ID_WINDOW_KEYWORDS = ID_WINDOW_MIN + 3
|
||||
, ID_WINDOW_STATS = ID_WINDOW_MIN + 4
|
||||
, ID_WINDOW_RANDOM_PACK = ID_WINDOW_MIN + 5
|
||||
, ID_WINDOW_MAX = 220
|
||||
|
||||
// Help menu (MainWindow)
|
||||
, ID_HELP_INDEX = wxID_HELP_CONTENTS
|
||||
, ID_HELP_WEBSITE = 301
|
||||
, ID_HELP_DOCUMENTATION
|
||||
, ID_HELP_ABOUT = wxID_ABOUT
|
||||
// Help menu (MainWindow)
|
||||
, ID_HELP_INDEX = wxID_HELP_CONTENTS
|
||||
, ID_HELP_WEBSITE = 301
|
||||
, ID_HELP_DOCUMENTATION
|
||||
, ID_HELP_ABOUT = wxID_ABOUT
|
||||
|
||||
// Mode menu (SymbolWindow)
|
||||
, ID_MODE_MIN = 401
|
||||
, ID_MODE_SELECT = ID_MODE_MIN
|
||||
, ID_MODE_ROTATE
|
||||
, ID_MODE_POINTS
|
||||
, ID_MODE_SHAPES
|
||||
, ID_MODE_SYMMETRY
|
||||
, ID_MODE_PAINT
|
||||
, ID_MODE_MAX
|
||||
// Mode menu (SymbolWindow)
|
||||
, ID_MODE_MIN = 401
|
||||
, ID_MODE_SELECT = ID_MODE_MIN
|
||||
, ID_MODE_ROTATE
|
||||
, ID_MODE_POINTS
|
||||
, ID_MODE_SHAPES
|
||||
, ID_MODE_SYMMETRY
|
||||
, ID_MODE_PAINT
|
||||
, ID_MODE_MAX
|
||||
};
|
||||
|
||||
|
||||
@@ -91,132 +91,132 @@ enum MenuID {
|
||||
/// Ids for menus on child panels (MainWindowPanel / SymbolEditorBase)
|
||||
enum ChildMenuID {
|
||||
|
||||
ID_CHILD_MIN = 6000
|
||||
, ID_CHILD_MAX = 16999
|
||||
ID_CHILD_MIN = 6000
|
||||
, ID_CHILD_MAX = 16999
|
||||
|
||||
// Cards menu
|
||||
, ID_CARD_ADD = 6001
|
||||
, ID_CARD_ADD_MULT
|
||||
, ID_CARD_REMOVE
|
||||
, ID_CARD_PREV
|
||||
, ID_CARD_NEXT
|
||||
, ID_CARD_ROTATE
|
||||
, ID_CARD_ROTATE_0
|
||||
, ID_CARD_ROTATE_90
|
||||
, ID_CARD_ROTATE_180
|
||||
, ID_CARD_ROTATE_270
|
||||
// CardList
|
||||
, ID_SELECT_COLUMNS
|
||||
// Cards menu
|
||||
, ID_CARD_ADD = 6001
|
||||
, ID_CARD_ADD_MULT
|
||||
, ID_CARD_REMOVE
|
||||
, ID_CARD_PREV
|
||||
, ID_CARD_NEXT
|
||||
, ID_CARD_ROTATE
|
||||
, ID_CARD_ROTATE_0
|
||||
, ID_CARD_ROTATE_90
|
||||
, ID_CARD_ROTATE_180
|
||||
, ID_CARD_ROTATE_270
|
||||
// CardList
|
||||
, ID_SELECT_COLUMNS
|
||||
|
||||
// Keyword menu
|
||||
, ID_KEYWORD_ADD = 6101
|
||||
, ID_KEYWORD_REMOVE
|
||||
, ID_KEYWORD_PREV
|
||||
, ID_KEYWORD_NEXT
|
||||
// Keyword menu
|
||||
, ID_KEYWORD_ADD = 6101
|
||||
, ID_KEYWORD_REMOVE
|
||||
, ID_KEYWORD_PREV
|
||||
, ID_KEYWORD_NEXT
|
||||
|
||||
// Format menu
|
||||
, ID_FORMAT_BOLD = 6201
|
||||
, ID_FORMAT_ITALIC
|
||||
, ID_FORMAT_SYMBOL
|
||||
, ID_FORMAT_REMINDER
|
||||
, ID_INSERT_SYMBOL
|
||||
// Format menu
|
||||
, ID_FORMAT_BOLD = 6201
|
||||
, ID_FORMAT_ITALIC
|
||||
, ID_FORMAT_SYMBOL
|
||||
, ID_FORMAT_REMINDER
|
||||
, ID_INSERT_SYMBOL
|
||||
|
||||
// Spelling errors
|
||||
, ID_SPELLING_ADD_TO_DICT = 6301
|
||||
, ID_SPELLING_NO_SUGGEST
|
||||
, ID_SPELLING_SUGGEST
|
||||
, ID_SPELLING_SUGGEST_MAX = 6399
|
||||
// Spelling errors
|
||||
, ID_SPELLING_ADD_TO_DICT = 6301
|
||||
, ID_SPELLING_NO_SUGGEST
|
||||
, ID_SPELLING_SUGGEST
|
||||
, ID_SPELLING_SUGGEST_MAX = 6399
|
||||
|
||||
// Graph menu
|
||||
, ID_GRAPH_PIE = 6401 // corresponds to GraphType
|
||||
, ID_GRAPH_BAR
|
||||
, ID_GRAPH_STACK
|
||||
, ID_GRAPH_SCATTER
|
||||
, ID_GRAPH_SCATTER_PIE
|
||||
// Graph menu
|
||||
, ID_GRAPH_PIE = 6401 // corresponds to GraphType
|
||||
, ID_GRAPH_BAR
|
||||
, ID_GRAPH_STACK
|
||||
, ID_GRAPH_SCATTER
|
||||
, ID_GRAPH_SCATTER_PIE
|
||||
|
||||
// SymbolSelectEditor toolbar/menu
|
||||
, ID_SYMBOL_COMBINE = 7001
|
||||
, ID_SYMBOL_COMBINE_MERGE = ID_SYMBOL_COMBINE + 0 //SYMBOL_COMBINE_MERGE
|
||||
, ID_SYMBOL_COMBINE_SUBTRACT = ID_SYMBOL_COMBINE + 1 //SYMBOL_COMBINE_SUBTRACT
|
||||
, ID_SYMBOL_COMBINE_INTERSECTION = ID_SYMBOL_COMBINE + 2 //SYMBOL_COMBINE_INTERSECTION
|
||||
, ID_SYMBOL_COMBINE_DIFFERENCE = ID_SYMBOL_COMBINE + 3 //SYMBOL_COMBINE_DIFFERENCE
|
||||
, ID_SYMBOL_COMBINE_OVERLAP = ID_SYMBOL_COMBINE + 4 //SYMBOL_COMBINE_OVERLAP
|
||||
, ID_SYMBOL_COMBINE_BORDER = ID_SYMBOL_COMBINE + 5 //SYMBOL_COMBINE_BORDER
|
||||
, ID_SYMBOL_COMBINE_MAX
|
||||
, ID_EDIT_DUPLICATE // duplicating symbol parts
|
||||
, ID_EDIT_GROUP
|
||||
, ID_EDIT_UNGROUP
|
||||
, ID_VIEW_GRID
|
||||
, ID_VIEW_GRID_SNAP
|
||||
// SymbolSelectEditor toolbar/menu
|
||||
, ID_SYMBOL_COMBINE = 7001
|
||||
, ID_SYMBOL_COMBINE_MERGE = ID_SYMBOL_COMBINE + 0 //SYMBOL_COMBINE_MERGE
|
||||
, ID_SYMBOL_COMBINE_SUBTRACT = ID_SYMBOL_COMBINE + 1 //SYMBOL_COMBINE_SUBTRACT
|
||||
, ID_SYMBOL_COMBINE_INTERSECTION = ID_SYMBOL_COMBINE + 2 //SYMBOL_COMBINE_INTERSECTION
|
||||
, ID_SYMBOL_COMBINE_DIFFERENCE = ID_SYMBOL_COMBINE + 3 //SYMBOL_COMBINE_DIFFERENCE
|
||||
, ID_SYMBOL_COMBINE_OVERLAP = ID_SYMBOL_COMBINE + 4 //SYMBOL_COMBINE_OVERLAP
|
||||
, ID_SYMBOL_COMBINE_BORDER = ID_SYMBOL_COMBINE + 5 //SYMBOL_COMBINE_BORDER
|
||||
, ID_SYMBOL_COMBINE_MAX
|
||||
, ID_EDIT_DUPLICATE // duplicating symbol parts
|
||||
, ID_EDIT_GROUP
|
||||
, ID_EDIT_UNGROUP
|
||||
, ID_VIEW_GRID
|
||||
, ID_VIEW_GRID_SNAP
|
||||
|
||||
// SymbolPointEditor toolbar/menu
|
||||
, ID_SEGMENT = 7101
|
||||
, ID_SEGMENT_LINE = ID_SEGMENT + 0//SEGMENT_LINE
|
||||
, ID_SEGMENT_CURVE = ID_SEGMENT + 1//SEGMENT_CURVE
|
||||
, ID_SEGMENT_MAX
|
||||
, ID_LOCK = 7151
|
||||
, ID_LOCK_FREE = ID_LOCK + 0//LOCK_FREE
|
||||
, ID_LOCK_DIR = ID_LOCK + 1//LOCK_DIR
|
||||
, ID_LOCK_SIZE = ID_LOCK + 2//LOCK_SIZE
|
||||
, ID_LOCK_MAX
|
||||
// SymbolPointEditor toolbar/menu
|
||||
, ID_SEGMENT = 7101
|
||||
, ID_SEGMENT_LINE = ID_SEGMENT + 0//SEGMENT_LINE
|
||||
, ID_SEGMENT_CURVE = ID_SEGMENT + 1//SEGMENT_CURVE
|
||||
, ID_SEGMENT_MAX
|
||||
, ID_LOCK = 7151
|
||||
, ID_LOCK_FREE = ID_LOCK + 0//LOCK_FREE
|
||||
, ID_LOCK_DIR = ID_LOCK + 1//LOCK_DIR
|
||||
, ID_LOCK_SIZE = ID_LOCK + 2//LOCK_SIZE
|
||||
, ID_LOCK_MAX
|
||||
|
||||
// SymbolBasicShapeEditor toolbar/menu
|
||||
, ID_SHAPE = 7201
|
||||
, ID_SHAPE_CIRCLE = ID_SHAPE
|
||||
, ID_SHAPE_RECTANGLE
|
||||
, ID_SHAPE_POLYGON
|
||||
, ID_SHAPE_STAR
|
||||
, ID_SHAPE_MAX
|
||||
, ID_SIDES
|
||||
|
||||
// SymbolSymmetryEditor toolbar/menu
|
||||
, ID_SYMMETRY = 7301
|
||||
, ID_SYMMETRY_ROTATION = ID_SYMMETRY
|
||||
, ID_SYMMETRY_REFLECTION
|
||||
, ID_SYMMETRY_MAX
|
||||
, ID_ADD_SYMMETRY
|
||||
, ID_REMOVE_SYMMETRY
|
||||
, ID_COPIES
|
||||
|
||||
// On cards panel
|
||||
, ID_COLLAPSE_NOTES = 8001
|
||||
, ID_CARD_FILTER
|
||||
|
||||
// Style panel
|
||||
, ID_STYLE_USE_FOR_ALL = 8011
|
||||
, ID_STYLE_USE_CUSTOM
|
||||
// SymbolBasicShapeEditor toolbar/menu
|
||||
, ID_SHAPE = 7201
|
||||
, ID_SHAPE_CIRCLE = ID_SHAPE
|
||||
, ID_SHAPE_RECTANGLE
|
||||
, ID_SHAPE_POLYGON
|
||||
, ID_SHAPE_STAR
|
||||
, ID_SHAPE_MAX
|
||||
, ID_SIDES
|
||||
|
||||
// SymbolSymmetryEditor toolbar/menu
|
||||
, ID_SYMMETRY = 7301
|
||||
, ID_SYMMETRY_ROTATION = ID_SYMMETRY
|
||||
, ID_SYMMETRY_REFLECTION
|
||||
, ID_SYMMETRY_MAX
|
||||
, ID_ADD_SYMMETRY
|
||||
, ID_REMOVE_SYMMETRY
|
||||
, ID_COPIES
|
||||
|
||||
// On cards panel
|
||||
, ID_COLLAPSE_NOTES = 8001
|
||||
, ID_CARD_FILTER
|
||||
|
||||
// Style panel
|
||||
, ID_STYLE_USE_FOR_ALL = 8011
|
||||
, ID_STYLE_USE_CUSTOM
|
||||
|
||||
// Keywords panel
|
||||
, ID_KEYWORD_ADD_PARAM = 8021
|
||||
, ID_KEYWORD_REF_PARAM
|
||||
, ID_KEYWORD_MODE
|
||||
, ID_KEYWORD_FILTER
|
||||
, ID_PARAM_TYPE_MIN = 8101
|
||||
, ID_PARAM_TYPE_MAX = 8200
|
||||
, ID_PARAM_REF_MIN = 8201
|
||||
, ID_PARAM_REF_MAX = 8300
|
||||
|
||||
// Statistics panel
|
||||
, ID_FIELD_LIST = 8301
|
||||
|
||||
// Random pack panel
|
||||
, ID_PACK_AMOUNT = 8111
|
||||
, ID_PACK_TYPE
|
||||
, ID_SEED_RANDOM
|
||||
, ID_SEED_FIXED
|
||||
, ID_GENERATE_PACK
|
||||
, ID_CUSTOM_PACK
|
||||
|
||||
// Console panel
|
||||
, ID_EVALUATE
|
||||
|
||||
// SymbolFont (Format menu)
|
||||
, ID_INSERT_SYMBOL_MENU_MIN = 9001
|
||||
, ID_INSERT_SYMBOL_MENU_MAX = 10000
|
||||
|
||||
// AddCardsScript (Card menu)
|
||||
, ID_ADD_CARDS_MENU_MIN = 10001
|
||||
, ID_ADD_CARDS_MENU_MAX = 11000
|
||||
// Keywords panel
|
||||
, ID_KEYWORD_ADD_PARAM = 8021
|
||||
, ID_KEYWORD_REF_PARAM
|
||||
, ID_KEYWORD_MODE
|
||||
, ID_KEYWORD_FILTER
|
||||
, ID_PARAM_TYPE_MIN = 8101
|
||||
, ID_PARAM_TYPE_MAX = 8200
|
||||
, ID_PARAM_REF_MIN = 8201
|
||||
, ID_PARAM_REF_MAX = 8300
|
||||
|
||||
// Statistics panel
|
||||
, ID_FIELD_LIST = 8301
|
||||
|
||||
// Random pack panel
|
||||
, ID_PACK_AMOUNT = 8111
|
||||
, ID_PACK_TYPE
|
||||
, ID_SEED_RANDOM
|
||||
, ID_SEED_FIXED
|
||||
, ID_GENERATE_PACK
|
||||
, ID_CUSTOM_PACK
|
||||
|
||||
// Console panel
|
||||
, ID_EVALUATE
|
||||
|
||||
// SymbolFont (Format menu)
|
||||
, ID_INSERT_SYMBOL_MENU_MIN = 9001
|
||||
, ID_INSERT_SYMBOL_MENU_MAX = 10000
|
||||
|
||||
// AddCardsScript (Card menu)
|
||||
, ID_ADD_CARDS_MENU_MIN = 10001
|
||||
, ID_ADD_CARDS_MENU_MAX = 11000
|
||||
};
|
||||
|
||||
|
||||
@@ -225,67 +225,67 @@ enum ChildMenuID {
|
||||
/// Window ids for controls
|
||||
enum ControlID {
|
||||
|
||||
ID_CONTROL_MIN = 1000
|
||||
, ID_CONTROL_MAX = 1999
|
||||
ID_CONTROL_MIN = 1000
|
||||
, ID_CONTROL_MAX = 1999
|
||||
|
||||
// Controls
|
||||
, ID_VIEWER = 1001
|
||||
, ID_EDITOR
|
||||
, ID_CONTROL
|
||||
, ID_TAB_BAR
|
||||
, ID_CARD_LIST
|
||||
, ID_PART_LIST
|
||||
, ID_GAME_LIST
|
||||
, ID_STYLESHEET_LIST
|
||||
, ID_KEYWORD_LIST
|
||||
, ID_EXPORT_LIST
|
||||
, ID_NOTES
|
||||
, ID_KEYWORD
|
||||
, ID_MATCH
|
||||
, ID_REMINDER
|
||||
, ID_RULES
|
||||
, ID_MESSAGE_LIST
|
||||
// Card list column select
|
||||
, ID_MOVE_UP
|
||||
, ID_MOVE_DOWN
|
||||
, ID_SHOW
|
||||
, ID_HIDE
|
||||
// Card select
|
||||
, ID_SELECT_CARDS
|
||||
, ID_SELECTION_CHOICE
|
||||
, ID_SELECTION_CHOICE_MAX = ID_SELECTION_CHOICE + 100
|
||||
, ID_SELECT_ALL
|
||||
, ID_SELECT_NONE
|
||||
// Settings
|
||||
, ID_NOTEBOOK
|
||||
, ID_APPRENTICE_BROWSE
|
||||
, ID_CHECK_UPDATES_NOW
|
||||
// Image slicer
|
||||
, ID_PREVIEW
|
||||
, ID_SELECTOR
|
||||
, ID_SIZE
|
||||
, ID_LEFT
|
||||
, ID_TOP
|
||||
, ID_WIDTH
|
||||
, ID_HEIGHT
|
||||
, ID_FIX_ASPECT
|
||||
, ID_ZOOM
|
||||
, ID_ZOOM_X
|
||||
, ID_ZOOM_Y
|
||||
, ID_SHARPEN
|
||||
, ID_SHARPEN_AMOUNT
|
||||
// Updates window
|
||||
, ID_PACKAGE_LIST
|
||||
, ID_KEEP
|
||||
, ID_INSTALL
|
||||
, ID_UPGRADE
|
||||
, ID_REMOVE
|
||||
// Auto replace window
|
||||
, ID_USE_AUTO_REPLACE
|
||||
, ID_ITEM_VALUE
|
||||
, ID_ADD_ITEM
|
||||
, ID_REMOVE_ITEM
|
||||
, ID_DEFAULTS
|
||||
// Controls
|
||||
, ID_VIEWER = 1001
|
||||
, ID_EDITOR
|
||||
, ID_CONTROL
|
||||
, ID_TAB_BAR
|
||||
, ID_CARD_LIST
|
||||
, ID_PART_LIST
|
||||
, ID_GAME_LIST
|
||||
, ID_STYLESHEET_LIST
|
||||
, ID_KEYWORD_LIST
|
||||
, ID_EXPORT_LIST
|
||||
, ID_NOTES
|
||||
, ID_KEYWORD
|
||||
, ID_MATCH
|
||||
, ID_REMINDER
|
||||
, ID_RULES
|
||||
, ID_MESSAGE_LIST
|
||||
// Card list column select
|
||||
, ID_MOVE_UP
|
||||
, ID_MOVE_DOWN
|
||||
, ID_SHOW
|
||||
, ID_HIDE
|
||||
// Card select
|
||||
, ID_SELECT_CARDS
|
||||
, ID_SELECTION_CHOICE
|
||||
, ID_SELECTION_CHOICE_MAX = ID_SELECTION_CHOICE + 100
|
||||
, ID_SELECT_ALL
|
||||
, ID_SELECT_NONE
|
||||
// Settings
|
||||
, ID_NOTEBOOK
|
||||
, ID_APPRENTICE_BROWSE
|
||||
, ID_CHECK_UPDATES_NOW
|
||||
// Image slicer
|
||||
, ID_PREVIEW
|
||||
, ID_SELECTOR
|
||||
, ID_SIZE
|
||||
, ID_LEFT
|
||||
, ID_TOP
|
||||
, ID_WIDTH
|
||||
, ID_HEIGHT
|
||||
, ID_FIX_ASPECT
|
||||
, ID_ZOOM
|
||||
, ID_ZOOM_X
|
||||
, ID_ZOOM_Y
|
||||
, ID_SHARPEN
|
||||
, ID_SHARPEN_AMOUNT
|
||||
// Updates window
|
||||
, ID_PACKAGE_LIST
|
||||
, ID_KEEP
|
||||
, ID_INSTALL
|
||||
, ID_UPGRADE
|
||||
, ID_REMOVE
|
||||
// Auto replace window
|
||||
, ID_USE_AUTO_REPLACE
|
||||
, ID_ITEM_VALUE
|
||||
, ID_ADD_ITEM
|
||||
, ID_REMOVE_ITEM
|
||||
, ID_DEFAULTS
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
Reference in New Issue
Block a user