Change tabs to two spaces.

This commit is contained in:
Lymia Aluysia
2017-01-18 08:43:21 -06:00
parent d7f5f0dc3b
commit d2c635f739
329 changed files with 41307 additions and 41496 deletions
+70 -70
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+29 -29
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+2 -2
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+171 -171
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+5 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 = &regex;
results.begin = str.begin();
return regex.Matches(str);
}
inline bool matches(Results& results, const Char* begin, const Char* end) const {
results.regex = &regex;
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 = &regex;
results.begin = str.begin();
return regex.Matches(str);
}
inline bool matches(Results& results, const Char* begin, const Char* end) const {
results.regex = &regex;
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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+5 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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