mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-12 21:47:00 -04:00
Symbol editor now has constraints on selection, but part list allows selection inside groups.
Added logical 'xor' operator for scripting. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@534 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
+52
-22
@@ -105,6 +105,7 @@ void SymbolPartMatrixAction::transform(SymbolPart& part, const Matrix2D& m) {
|
|||||||
// bounds change after transforming
|
// bounds change after transforming
|
||||||
s->calculateBounds();
|
s->calculateBounds();
|
||||||
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
|
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
|
||||||
|
s->center = (s->center - center) * m + center;
|
||||||
s->handle = s->handle * m;
|
s->handle = s->handle * m;
|
||||||
} else if (SymbolGroup* g = part.isSymbolGroup()) {
|
} else if (SymbolGroup* g = part.isSymbolGroup()) {
|
||||||
FOR_EACH(p, g->parts) {
|
FOR_EACH(p, g->parts) {
|
||||||
@@ -280,7 +281,8 @@ void SymbolPartScaleAction::transformPart(SymbolPart& part) {
|
|||||||
pnt->delta_after = pnt->delta_after .mul(scale);
|
pnt->delta_after = pnt->delta_after .mul(scale);
|
||||||
}
|
}
|
||||||
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
|
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
|
||||||
throw "TODO";
|
transform(s->center);
|
||||||
|
s->handle.mul(new_size.div(old_size));
|
||||||
} else if (SymbolGroup* g = part.isSymbolGroup()) {
|
} else if (SymbolGroup* g = part.isSymbolGroup()) {
|
||||||
FOR_EACH(p, g->parts) {
|
FOR_EACH(p, g->parts) {
|
||||||
transformPart(*p);
|
transformPart(*p);
|
||||||
@@ -450,8 +452,9 @@ void DuplicateSymbolPartsAction::getParts(set<SymbolPartP>& parts) {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Reorder symbol parts
|
// ----------------------------------------------------------------------------- : Reorder symbol parts
|
||||||
|
|
||||||
ReorderSymbolPartsAction::ReorderSymbolPartsAction(Symbol& symbol, size_t old_position, size_t new_position)
|
ReorderSymbolPartsAction::ReorderSymbolPartsAction(SymbolGroup& old_parent, size_t old_position, SymbolGroup& new_parent, size_t new_position)
|
||||||
: symbol(symbol), old_position(old_position), new_position(new_position)
|
: old_parent(&old_parent), new_parent(&new_parent)
|
||||||
|
, old_position(old_position), new_position(new_position)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
String ReorderSymbolPartsAction::getName(bool to_undo) const {
|
String ReorderSymbolPartsAction::getName(bool to_undo) const {
|
||||||
@@ -459,31 +462,58 @@ String ReorderSymbolPartsAction::getName(bool to_undo) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ReorderSymbolPartsAction::perform(bool to_undo) {
|
void ReorderSymbolPartsAction::perform(bool to_undo) {
|
||||||
assert(old_position < symbol.parts.size());
|
// remove from old
|
||||||
assert(new_position < symbol.parts.size());
|
assert(old_position < old_parent->parts.size());
|
||||||
SymbolPartP part = symbol.parts.at(old_position);
|
SymbolPartP part = old_parent->parts.at(old_position);
|
||||||
symbol.parts.erase( symbol.parts.begin() + old_position);
|
old_parent->parts.erase( old_parent->parts.begin() + old_position);
|
||||||
symbol.parts.insert(symbol.parts.begin() + new_position, part);
|
// add to new
|
||||||
|
assert(new_position <= new_parent->parts.size());
|
||||||
|
new_parent->parts.insert(new_parent->parts.begin() + new_position, part);
|
||||||
|
// next time the other way around
|
||||||
|
swap(old_parent, new_parent);
|
||||||
swap(old_position, new_position);
|
swap(old_position, new_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Group symbol parts
|
|
||||||
|
|
||||||
GroupSymbolPartsActionBase::GroupSymbolPartsActionBase(Symbol& symbol)
|
UngroupReorderSymbolPartsAction::UngroupReorderSymbolPartsAction(SymbolGroup& group_parent, size_t group_pos, SymbolGroup& target_parent, size_t target_pos)
|
||||||
: symbol(symbol)
|
: group_parent(group_parent), group_pos(group_pos)
|
||||||
{}
|
, target_parent(target_parent), target_pos(target_pos)
|
||||||
void GroupSymbolPartsActionBase::perform(bool to_undo) {
|
{
|
||||||
swap(symbol.parts, old_part_list);
|
group = dynamic_pointer_cast<SymbolGroup>(group_parent.parts.at(group_pos));
|
||||||
|
assert(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupSymbolPartsAction::GroupSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts)
|
String UngroupReorderSymbolPartsAction::getName(bool to_undo) const {
|
||||||
: GroupSymbolPartsActionBase(symbol)
|
return _ACTION_("reorder parts");
|
||||||
|
}
|
||||||
|
|
||||||
|
void UngroupReorderSymbolPartsAction::perform(bool to_undo) {
|
||||||
|
if (!to_undo) {
|
||||||
|
group_parent.parts.erase(group_parent.parts.begin() + group_pos);
|
||||||
|
target_parent.parts.insert(target_parent.parts.begin() + target_pos, group->parts.begin(), group->parts.end());
|
||||||
|
} else {
|
||||||
|
target_parent.parts.erase(target_parent.parts.begin() + target_pos, target_parent.parts.begin() + target_pos + group->parts.size());
|
||||||
|
group_parent.parts.insert(group_parent.parts.begin() + group_pos, group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Group symbol parts
|
||||||
|
|
||||||
|
GroupSymbolPartsActionBase::GroupSymbolPartsActionBase(SymbolGroup& root)
|
||||||
|
: root(root)
|
||||||
|
{}
|
||||||
|
void GroupSymbolPartsActionBase::perform(bool to_undo) {
|
||||||
|
swap(root.parts, old_part_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupSymbolPartsAction::GroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts, const SymbolGroupP& group)
|
||||||
|
: GroupSymbolPartsActionBase(root)
|
||||||
{
|
{
|
||||||
// group parts in the old parts list
|
// group parts in the old parts list
|
||||||
bool done = false;
|
bool done = false;
|
||||||
SymbolGroupP group(new SymbolGroup);
|
FOR_EACH(p, root.parts) {
|
||||||
group->name = _("Group");
|
assert(p != group);
|
||||||
FOR_EACH(p, symbol.parts) {
|
|
||||||
if (parts.find(p) != parts.end()) {
|
if (parts.find(p) != parts.end()) {
|
||||||
// add to group instead
|
// add to group instead
|
||||||
group->parts.push_back(p);
|
group->parts.push_back(p);
|
||||||
@@ -502,11 +532,11 @@ String GroupSymbolPartsAction::getName(bool to_undo) const {
|
|||||||
return _ACTION_("group parts");
|
return _ACTION_("group parts");
|
||||||
}
|
}
|
||||||
|
|
||||||
UngroupSymbolPartsAction::UngroupSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts)
|
UngroupSymbolPartsAction::UngroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts)
|
||||||
: GroupSymbolPartsActionBase(symbol)
|
: GroupSymbolPartsActionBase(root)
|
||||||
{
|
{
|
||||||
// break up the parts in the old parts list
|
// break up the parts in the old parts list
|
||||||
FOR_EACH(p, symbol.parts) {
|
FOR_EACH(p, root.parts) {
|
||||||
if (parts.find(p) != parts.end() && p->isSymbolGroup()) {
|
if (parts.find(p) != parts.end() && p->isSymbolGroup()) {
|
||||||
// break up the group
|
// break up the group
|
||||||
SymbolGroup* g = p->isSymbolGroup();
|
SymbolGroup* g = p->isSymbolGroup();
|
||||||
|
|||||||
@@ -240,36 +240,52 @@ class DuplicateSymbolPartsAction : public SymbolPartListAction {
|
|||||||
/// Change the position of a part in a symbol, by moving a part.
|
/// Change the position of a part in a symbol, by moving a part.
|
||||||
class ReorderSymbolPartsAction : public SymbolPartListAction {
|
class ReorderSymbolPartsAction : public SymbolPartListAction {
|
||||||
public:
|
public:
|
||||||
ReorderSymbolPartsAction(Symbol& symbol, size_t old_position, size_t new_position);
|
ReorderSymbolPartsAction(SymbolGroup& old_parent, size_t old_position, SymbolGroup& new_parent, size_t new_position);
|
||||||
|
|
||||||
virtual String getName(bool to_undo) const;
|
virtual String getName(bool to_undo) const;
|
||||||
virtual void perform(bool to_undo);
|
virtual void perform(bool to_undo);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Symbol& symbol; ///< Symbol to swap the parts in
|
SymbolGroup* old_parent, *new_parent;///< Parents to move from and to
|
||||||
public:
|
public:
|
||||||
size_t old_position, new_position; ///< Positions to move from and to
|
size_t old_position, new_position; ///< Positions to move from and to
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Break up a single group, and put its contents at a specific position
|
||||||
|
class UngroupReorderSymbolPartsAction : public SymbolPartListAction {
|
||||||
|
public:
|
||||||
|
/// Remove all the given groups
|
||||||
|
UngroupReorderSymbolPartsAction(SymbolGroup& group_parent, size_t group_pos, SymbolGroup& target_parent, size_t target_pos);
|
||||||
|
|
||||||
|
virtual String getName(bool to_undo) const;
|
||||||
|
virtual void perform(bool to_undo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SymbolGroup& group_parent;
|
||||||
|
size_t group_pos;
|
||||||
|
SymbolGroupP group; ///< Group to destroy
|
||||||
|
SymbolGroup& target_parent;
|
||||||
|
size_t target_pos;
|
||||||
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Group symbol parts
|
// ----------------------------------------------------------------------------- : Group symbol parts
|
||||||
|
|
||||||
/// Group multiple symbol parts together
|
/// Group multiple symbol parts together
|
||||||
class GroupSymbolPartsActionBase : public SymbolPartListAction {
|
class GroupSymbolPartsActionBase : public SymbolPartListAction {
|
||||||
public:
|
public:
|
||||||
GroupSymbolPartsActionBase(Symbol& symbol);
|
GroupSymbolPartsActionBase(SymbolGroup& root);
|
||||||
|
|
||||||
virtual void perform(bool to_undo);
|
virtual void perform(bool to_undo);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Symbol& symbol; ///< Symbol to group stuff in
|
SymbolGroup& root; ///< Symbol or group to group stuff in
|
||||||
vector<SymbolPartP> old_part_list; ///< Old part list of the symbol
|
vector<SymbolPartP> old_part_list; ///< Old part list of the symbol
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Group multiple symbol parts together
|
/// Group multiple symbol parts together
|
||||||
class GroupSymbolPartsAction : public GroupSymbolPartsActionBase {
|
class GroupSymbolPartsAction : public GroupSymbolPartsActionBase {
|
||||||
public:
|
public:
|
||||||
GroupSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts);
|
GroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts, const SymbolGroupP& group);
|
||||||
|
|
||||||
virtual String getName(bool to_undo) const;
|
virtual String getName(bool to_undo) const;
|
||||||
};
|
};
|
||||||
@@ -277,11 +293,11 @@ class GroupSymbolPartsAction : public GroupSymbolPartsActionBase {
|
|||||||
/// Break up one or more SymbolGroups
|
/// Break up one or more SymbolGroups
|
||||||
class UngroupSymbolPartsAction : public GroupSymbolPartsActionBase {
|
class UngroupSymbolPartsAction : public GroupSymbolPartsActionBase {
|
||||||
public:
|
public:
|
||||||
UngroupSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& groups);
|
/// Remove all the given groups
|
||||||
|
UngroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& groups);
|
||||||
|
|
||||||
virtual String getName(bool to_undo) const;
|
virtual String getName(bool to_undo) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : EOF
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+22
-11
@@ -204,7 +204,12 @@ String SymbolSymmetry::typeName() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SymbolPartP SymbolSymmetry::clone() const {
|
SymbolPartP SymbolSymmetry::clone() const {
|
||||||
return new_intrusive1<SymbolSymmetry>(*this);
|
SymbolSymmetryP part(new SymbolSymmetry(*this));
|
||||||
|
// also clone the parts inside
|
||||||
|
FOR_EACH(p, part->parts) {
|
||||||
|
p = p->clone();
|
||||||
|
}
|
||||||
|
return part;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMPLEMENT_REFLECTION(SymbolSymmetry) {
|
IMPLEMENT_REFLECTION(SymbolSymmetry) {
|
||||||
@@ -213,20 +218,16 @@ IMPLEMENT_REFLECTION(SymbolSymmetry) {
|
|||||||
REFLECT(copies);
|
REFLECT(copies);
|
||||||
REFLECT(center);
|
REFLECT(center);
|
||||||
REFLECT(handle);
|
REFLECT(handle);
|
||||||
// Fixes after reading
|
REFLECT(parts);
|
||||||
REFLECT_IF_READING {
|
REFLECT_IF_READING calculateBoundsNonRec();
|
||||||
if (name.empty()) {
|
|
||||||
if (kind == SYMMETRY_REFLECTION) {
|
|
||||||
name = _("Mirror");
|
|
||||||
} else {
|
|
||||||
name = _("Symmetry");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : SymbolGroup
|
// ----------------------------------------------------------------------------- : SymbolGroup
|
||||||
|
|
||||||
|
SymbolGroup::SymbolGroup() {
|
||||||
|
name = capitalize(_TYPE_("group"));
|
||||||
|
}
|
||||||
|
|
||||||
String SymbolGroup::typeName() const {
|
String SymbolGroup::typeName() const {
|
||||||
return _("group");
|
return _("group");
|
||||||
}
|
}
|
||||||
@@ -240,6 +241,14 @@ SymbolPartP SymbolGroup::clone() const {
|
|||||||
return part;
|
return part;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SymbolGroup::isAncestor(const SymbolPart& that) const {
|
||||||
|
if (this == &that) return true;
|
||||||
|
FOR_EACH_CONST(p, parts) {
|
||||||
|
if (p->isAncestor(that)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void SymbolGroup::calculateBounds() {
|
void SymbolGroup::calculateBounds() {
|
||||||
FOR_EACH(p, parts) p->calculateBounds();
|
FOR_EACH(p, parts) p->calculateBounds();
|
||||||
calculateBoundsNonRec();
|
calculateBoundsNonRec();
|
||||||
@@ -256,12 +265,14 @@ void SymbolGroup::calculateBoundsNonRec() {
|
|||||||
IMPLEMENT_REFLECTION(SymbolGroup) {
|
IMPLEMENT_REFLECTION(SymbolGroup) {
|
||||||
REFLECT_BASE(SymbolPart);
|
REFLECT_BASE(SymbolPart);
|
||||||
REFLECT(parts);
|
REFLECT(parts);
|
||||||
|
REFLECT_IF_READING calculateBoundsNonRec();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Symbol
|
// ----------------------------------------------------------------------------- : Symbol
|
||||||
|
|
||||||
IMPLEMENT_REFLECTION(Symbol) {
|
IMPLEMENT_REFLECTION(Symbol) {
|
||||||
REFLECT(parts);
|
REFLECT(parts);
|
||||||
|
REFLECT_IF_READING calculateBoundsNonRec();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Default symbol
|
// ----------------------------------------------------------------------------- : Default symbol
|
||||||
|
|||||||
+30
-22
@@ -133,6 +133,10 @@ class SymbolPart : public IntrusivePtrVirtualBase {
|
|||||||
virtual SymbolGroup* isSymbolGroup() { return nullptr; }
|
virtual SymbolGroup* isSymbolGroup() { return nullptr; }
|
||||||
virtual const SymbolGroup* isSymbolGroup() const { return nullptr; }
|
virtual const SymbolGroup* isSymbolGroup() const { return nullptr; }
|
||||||
|
|
||||||
|
/// Does this part contain another?
|
||||||
|
/** also true if this==that*/
|
||||||
|
virtual bool isAncestor(const SymbolPart& that) const { return this == &that; }
|
||||||
|
|
||||||
/// Calculate the position and size of the part (min_pos and max_pos)
|
/// Calculate the position and size of the part (min_pos and max_pos)
|
||||||
virtual void calculateBounds();
|
virtual void calculateBounds();
|
||||||
|
|
||||||
@@ -191,6 +195,30 @@ class SymbolShape : public SymbolPart {
|
|||||||
DECLARE_REFLECTION();
|
DECLARE_REFLECTION();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : SymbolGroup
|
||||||
|
|
||||||
|
/// A group of symbol parts
|
||||||
|
class SymbolGroup : public SymbolPart {
|
||||||
|
public:
|
||||||
|
vector<SymbolPartP> parts; ///< The parts in this group, first item is on top
|
||||||
|
|
||||||
|
SymbolGroup();
|
||||||
|
|
||||||
|
virtual String typeName() const;
|
||||||
|
virtual SymbolPartP clone() const;
|
||||||
|
virtual int icon() const { return SYMBOL_COMBINE_BORDER + 3; }
|
||||||
|
virtual SymbolGroup* isSymbolGroup() { return this; }
|
||||||
|
virtual const SymbolGroup* isSymbolGroup() const { return this; }
|
||||||
|
|
||||||
|
virtual bool isAncestor(const SymbolPart& that) const;
|
||||||
|
|
||||||
|
virtual void calculateBounds();
|
||||||
|
/// re-calculate the bounds, but not of the contained parts
|
||||||
|
void calculateBoundsNonRec();
|
||||||
|
|
||||||
|
DECLARE_REFLECTION();
|
||||||
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : SymbolSymmetry
|
// ----------------------------------------------------------------------------- : SymbolSymmetry
|
||||||
|
|
||||||
enum SymbolSymmetryType
|
enum SymbolSymmetryType
|
||||||
@@ -198,9 +226,9 @@ enum SymbolSymmetryType
|
|||||||
, SYMMETRY_REFLECTION
|
, SYMMETRY_REFLECTION
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A mirror, reflecting part of the symbol
|
/// A mirror, reflecting the part of the symbol in the group
|
||||||
/** Can handle rotation symmetry with any number of reflections */
|
/** Can handle rotation symmetry with any number of reflections */
|
||||||
class SymbolSymmetry : public SymbolPart {
|
class SymbolSymmetry : public SymbolGroup {
|
||||||
public:
|
public:
|
||||||
SymbolSymmetryType kind; ///< What kind of symmetry
|
SymbolSymmetryType kind; ///< What kind of symmetry
|
||||||
int copies; ///< How many times is the orignal reflected (including the original itself)
|
int copies; ///< How many times is the orignal reflected (including the original itself)
|
||||||
@@ -219,26 +247,6 @@ class SymbolSymmetry : public SymbolPart {
|
|||||||
DECLARE_REFLECTION();
|
DECLARE_REFLECTION();
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : SymbolGroup
|
|
||||||
|
|
||||||
/// A group of symbol parts
|
|
||||||
class SymbolGroup : public SymbolPart {
|
|
||||||
public:
|
|
||||||
vector<SymbolPartP> parts; ///< The parts in this group, first item is on top
|
|
||||||
|
|
||||||
virtual String typeName() const;
|
|
||||||
virtual SymbolPartP clone() const;
|
|
||||||
virtual int icon() const { return SYMMETRY_REFLECTION + 1; }
|
|
||||||
virtual SymbolGroup* isSymbolGroup() { return this; }
|
|
||||||
virtual const SymbolGroup* isSymbolGroup() const { return this; }
|
|
||||||
|
|
||||||
virtual void calculateBounds();
|
|
||||||
/// re-calculate the bounds, but not of the contained parts
|
|
||||||
void calculateBoundsNonRec();
|
|
||||||
|
|
||||||
DECLARE_REFLECTION();
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Symbol
|
// ----------------------------------------------------------------------------- : Symbol
|
||||||
|
|
||||||
/// An editable symbol, consists of any number of SymbolParts
|
/// An editable symbol, consists of any number of SymbolParts
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ SymbolBasicShapeEditor::SymbolBasicShapeEditor(SymbolControl* control)
|
|||||||
|
|
||||||
void SymbolBasicShapeEditor::draw(DC& dc) {
|
void SymbolBasicShapeEditor::draw(DC& dc) {
|
||||||
// highlight the part we are drawing
|
// highlight the part we are drawing
|
||||||
if (drawing) {
|
if (shape) {
|
||||||
control.highlightPart(dc, *shape, HIGHLIGHT_BORDER);
|
control.highlightPart(dc, *shape, HIGHLIGHT_BORDER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,6 +64,7 @@ void SymbolBasicShapeEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
|
|||||||
delete sides;
|
delete sides;
|
||||||
delete sidesL;
|
delete sidesL;
|
||||||
#endif
|
#endif
|
||||||
|
stopActions(); // set status text
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolBasicShapeEditor::onUpdateUI(wxUpdateUIEvent& ev) {
|
void SymbolBasicShapeEditor::onUpdateUI(wxUpdateUIEvent& ev) {
|
||||||
@@ -80,6 +81,7 @@ void SymbolBasicShapeEditor::onCommand(int id) {
|
|||||||
if (id >= ID_SHAPE && id < ID_SHAPE_MAX) {
|
if (id >= ID_SHAPE && id < ID_SHAPE_MAX) {
|
||||||
// change shape mode
|
// change shape mode
|
||||||
mode = id;
|
mode = id;
|
||||||
|
stopActions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-24
@@ -12,6 +12,7 @@
|
|||||||
#include <gui/symbol/select_editor.hpp>
|
#include <gui/symbol/select_editor.hpp>
|
||||||
#include <gui/symbol/point_editor.hpp>
|
#include <gui/symbol/point_editor.hpp>
|
||||||
#include <gui/symbol/basic_shape_editor.hpp>
|
#include <gui/symbol/basic_shape_editor.hpp>
|
||||||
|
#include <gui/symbol/symmetry_editor.hpp>
|
||||||
#include <gui/util.hpp>
|
#include <gui/util.hpp>
|
||||||
#include <data/action/symbol.hpp>
|
#include <data/action/symbol.hpp>
|
||||||
#include <data/settings.hpp>
|
#include <data/settings.hpp>
|
||||||
@@ -36,7 +37,7 @@ void SymbolControl::switchEditor(const SymbolEditorBaseP& e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SymbolControl::onChangeSymbol() {
|
void SymbolControl::onChangeSymbol() {
|
||||||
selected_parts.clear();
|
selected_parts.setSymbol(symbol);
|
||||||
switchEditor(new_intrusive2<SymbolSelectEditor>(this, false));
|
switchEditor(new_intrusive2<SymbolSelectEditor>(this, false));
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
}
|
}
|
||||||
@@ -51,7 +52,7 @@ void SymbolControl::onModeChange(wxCommandEvent& ev) {
|
|||||||
break;
|
break;
|
||||||
case ID_MODE_POINTS:
|
case ID_MODE_POINTS:
|
||||||
if (selected_parts.size() == 1) {
|
if (selected_parts.size() == 1) {
|
||||||
selected_shape = dynamic_pointer_cast<SymbolShape>(*selected_parts.begin());
|
selected_shape = selected_parts.getAShape();
|
||||||
if (selected_shape) {
|
if (selected_shape) {
|
||||||
switchEditor(new_intrusive2<SymbolPointEditor>(this, selected_shape));
|
switchEditor(new_intrusive2<SymbolPointEditor>(this, selected_shape));
|
||||||
}
|
}
|
||||||
@@ -64,6 +65,9 @@ void SymbolControl::onModeChange(wxCommandEvent& ev) {
|
|||||||
}
|
}
|
||||||
switchEditor(new_intrusive1<SymbolBasicShapeEditor>(this));
|
switchEditor(new_intrusive1<SymbolBasicShapeEditor>(this));
|
||||||
break;
|
break;
|
||||||
|
case ID_MODE_SYMMETRY:
|
||||||
|
switchEditor(new_intrusive1<SymbolSymmetryEditor>(this));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,25 +96,17 @@ void SymbolControl::onUpdateSelection() {
|
|||||||
switch(editor->modeToolId()) {
|
switch(editor->modeToolId()) {
|
||||||
case ID_MODE_POINTS: {
|
case ID_MODE_POINTS: {
|
||||||
// can only select a single part!
|
// can only select a single part!
|
||||||
if (selected_parts.size() > 1) {
|
SymbolShapeP shape = selected_parts.getAShape();
|
||||||
// TODO: find a part that is a shape
|
|
||||||
SymbolPartP part = *selected_parts.begin();
|
|
||||||
selected_parts.clear();
|
|
||||||
selected_parts.insert(part);
|
|
||||||
signalSelectionChange();
|
|
||||||
} else if (selected_parts.empty()) {
|
|
||||||
selected_parts.insert(selected_shape);
|
|
||||||
signalSelectionChange();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
SymbolShapeP shape = dynamic_pointer_cast<SymbolShape>(*selected_parts.begin());
|
|
||||||
if (!shape) {
|
if (!shape) {
|
||||||
selected_parts.clear();
|
if (selected_parts.select(selected_shape)) {
|
||||||
selected_parts.insert(selected_shape);
|
signalSelectionChange();
|
||||||
signalSelectionChange();
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (shape != selected_shape) {
|
if (shape != selected_shape) {
|
||||||
|
if (selected_parts.select(shape)) {
|
||||||
|
signalSelectionChange();
|
||||||
|
}
|
||||||
// begin editing another part
|
// begin editing another part
|
||||||
selected_shape = shape;
|
selected_shape = shape;
|
||||||
editor = new_intrusive2<SymbolPointEditor>(this, selected_shape);
|
editor = new_intrusive2<SymbolPointEditor>(this, selected_shape);
|
||||||
@@ -131,16 +127,14 @@ void SymbolControl::onUpdateSelection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SymbolControl::selectPart(const SymbolPartP& part) {
|
void SymbolControl::selectPart(const SymbolPartP& part) {
|
||||||
selected_parts.clear();
|
selected_parts.select(part);
|
||||||
selected_parts.insert(part);
|
|
||||||
switchEditor(new_intrusive2<SymbolSelectEditor>(this, false));
|
switchEditor(new_intrusive2<SymbolSelectEditor>(this, false));
|
||||||
signalSelectionChange();
|
signalSelectionChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolControl::activatePart(const SymbolPartP& part) {
|
void SymbolControl::activatePart(const SymbolPartP& part) {
|
||||||
if (part->isSymbolShape()) {
|
if (part->isSymbolShape()) {
|
||||||
selected_parts.clear();
|
selected_parts.select(part);
|
||||||
selected_parts.insert(part);
|
|
||||||
switchEditor(new_intrusive2<SymbolPointEditor>(this, static_pointer_cast<SymbolShape>(part)));
|
switchEditor(new_intrusive2<SymbolPointEditor>(this, static_pointer_cast<SymbolShape>(part)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,11 +241,12 @@ void SymbolControl::onSize(wxSizeEvent& ev) {
|
|||||||
void SymbolControl::onUpdateUI(wxUpdateUIEvent& ev) {
|
void SymbolControl::onUpdateUI(wxUpdateUIEvent& ev) {
|
||||||
if (!editor) return;
|
if (!editor) return;
|
||||||
switch (ev.GetId()) {
|
switch (ev.GetId()) {
|
||||||
case ID_MODE_SELECT: case ID_MODE_ROTATE: case ID_MODE_POINTS: case ID_MODE_SHAPES: //case ID_MODE_PAINT:
|
case ID_MODE_SELECT: case ID_MODE_ROTATE: case ID_MODE_POINTS:
|
||||||
|
case ID_MODE_SHAPES: case ID_MODE_SYMMETRY: //case ID_MODE_PAINT:
|
||||||
ev.Check(editor->modeToolId() == ev.GetId());
|
ev.Check(editor->modeToolId() == ev.GetId());
|
||||||
if (ev.GetId() == ID_MODE_POINTS) {
|
if (ev.GetId() == ID_MODE_POINTS) {
|
||||||
// can only edit points when a single part is selected <TODO?>
|
// can only edit points when a shape is available
|
||||||
ev.Enable(selected_parts.size() == 1 && (*selected_parts.begin())->isSymbolShape());
|
ev.Enable(selected_parts.getAShape());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ID_MODE_PAINT:
|
case ID_MODE_PAINT:
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include <util/prec.hpp>
|
#include <util/prec.hpp>
|
||||||
#include <data/symbol.hpp>
|
#include <data/symbol.hpp>
|
||||||
|
#include <gui/symbol/selection.hpp>
|
||||||
#include <render/symbol/viewer.hpp>
|
#include <render/symbol/viewer.hpp>
|
||||||
|
|
||||||
class SymbolWindow;
|
class SymbolWindow;
|
||||||
@@ -71,9 +72,9 @@ class SymbolControl : public wxControl, public SymbolViewer {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
/// What parts are selected?
|
/// What parts are selected?
|
||||||
set<SymbolPartP> selected_parts;
|
SymbolPartsSelection selected_parts;
|
||||||
SymbolPartP highlight_part; ///< part the mouse cursor is over
|
SymbolPartP highlight_part; ///< part the mouse cursor is over
|
||||||
SymbolShapeP selected_shape; ///< if there is a single selection
|
SymbolShapeP selected_shape; ///< if there is a single selection
|
||||||
|
|
||||||
/// Parent window
|
/// Parent window
|
||||||
SymbolWindow* parent;
|
SymbolWindow* parent;
|
||||||
|
|||||||
+181
-102
@@ -7,10 +7,12 @@
|
|||||||
// ----------------------------------------------------------------------------- : Includes
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
#include <gui/symbol/part_list.hpp>
|
#include <gui/symbol/part_list.hpp>
|
||||||
|
#include <gui/symbol/selection.hpp>
|
||||||
#include <gui/util.hpp>
|
#include <gui/util.hpp>
|
||||||
#include <data/action/symbol.hpp>
|
#include <data/action/symbol.hpp>
|
||||||
#include <gfx/gfx.hpp>
|
#include <gfx/gfx.hpp>
|
||||||
#include <render/symbol/filter.hpp>
|
#include <render/symbol/filter.hpp>
|
||||||
|
#include <util/error.hpp>
|
||||||
#include <wx/dcbuffer.h>
|
#include <wx/dcbuffer.h>
|
||||||
#include <wx/caret.h>
|
#include <wx/caret.h>
|
||||||
|
|
||||||
@@ -24,11 +26,12 @@ DEFINE_EVENT_TYPE(EVENT_PART_ACTIVATE);
|
|||||||
// ----------------------------------------------------------------------------- : SymbolPartList
|
// ----------------------------------------------------------------------------- : SymbolPartList
|
||||||
|
|
||||||
|
|
||||||
SymbolPartList::SymbolPartList(Window* parent, int id, set<SymbolPartP>& selection, SymbolP symbol)
|
SymbolPartList::SymbolPartList(Window* parent, int id, SymbolPartsSelection& selection, SymbolP symbol)
|
||||||
: wxScrolledWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxVSCROLL)
|
: wxScrolledWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxVSCROLL)
|
||||||
, selection(selection)
|
, selection(selection)
|
||||||
, state_icons(9,8)
|
, state_icons(9,8)
|
||||||
{
|
{
|
||||||
|
SetScrollRate(0, ITEM_HEIGHT+1);
|
||||||
// NOTE: this is based on the order of the SymbolShapeCombine and SymbolSymmetryType enums!
|
// NOTE: this is based on the order of the SymbolShapeCombine and SymbolSymmetryType enums!
|
||||||
state_icons.Add(load_resource_image(_("icon_combine_merge")));
|
state_icons.Add(load_resource_image(_("icon_combine_merge")));
|
||||||
state_icons.Add(load_resource_image(_("icon_combine_subtract")));
|
state_icons.Add(load_resource_image(_("icon_combine_subtract")));
|
||||||
@@ -69,7 +72,7 @@ void SymbolPartList::onAction(const Action& action, bool undone) {
|
|||||||
TYPE_CASE_(action, SymbolPartAction) {
|
TYPE_CASE_(action, SymbolPartAction) {
|
||||||
symbol_preview.up_to_date = false;
|
symbol_preview.up_to_date = false;
|
||||||
// some part changed, but we don't know which one, assume it is the selection
|
// some part changed, but we don't know which one, assume it is the selection
|
||||||
updateParts(selection);
|
updateParts(selection.get());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,7 +87,7 @@ wxSize SymbolPartList::DoGetBestSize() const {
|
|||||||
void SymbolPartList::update() {
|
void SymbolPartList::update() {
|
||||||
// count items
|
// count items
|
||||||
number_of_items = childCount(symbol);
|
number_of_items = childCount(symbol);
|
||||||
SetVirtualSize(110, number_of_items * (ITEM_HEIGHT+1));
|
SetVirtualSize(110, number_of_items * (ITEM_HEIGHT + 1));
|
||||||
// invalidate previews
|
// invalidate previews
|
||||||
symbol_preview.up_to_date = false;
|
symbol_preview.up_to_date = false;
|
||||||
for (size_t i = 0 ; i < part_previews.size() ; ++i) {
|
for (size_t i = 0 ; i < part_previews.size() ; ++i) {
|
||||||
@@ -118,23 +121,19 @@ void SymbolPartList::updatePart(const set<SymbolPartP>& parts, int& i, bool pare
|
|||||||
// ----------------------------------------------------------------------------- : Events
|
// ----------------------------------------------------------------------------- : Events
|
||||||
|
|
||||||
void SymbolPartList::onLeftDown(wxMouseEvent& ev) {
|
void SymbolPartList::onLeftDown(wxMouseEvent& ev) {
|
||||||
|
int top; GetViewStart(0, &top);
|
||||||
// find item under cursor
|
// find item under cursor
|
||||||
if (ev.GetX() < 0 || ev.GetX() >= GetClientSize().x) return;
|
if (ev.GetX() < 0 || ev.GetX() >= GetClientSize().x) return;
|
||||||
SymbolPartP part = findItem(ev.GetY() / (ITEM_HEIGHT + 1));
|
int pos = top + ev.GetY() / (ITEM_HEIGHT + 1);
|
||||||
|
SymbolPartP part = findItem(pos, ev.GetX());
|
||||||
if (part) {
|
if (part) {
|
||||||
// toggle/select
|
// toggle/select
|
||||||
if (!ev.ShiftDown()) {
|
selection.select(part, ev.ShiftDown() ? SELECT_TOGGLE : SELECT_OVERRIDE);
|
||||||
selection.clear();
|
if (!ev.ShiftDown() && selection.selected(part)) {
|
||||||
}
|
|
||||||
if (selection.find(part) != selection.end()) {
|
|
||||||
selection.erase(part);
|
|
||||||
} else {
|
|
||||||
selection.insert(part);
|
|
||||||
}
|
|
||||||
if (!ev.ShiftDown()) {
|
|
||||||
// drag item
|
// drag item
|
||||||
mouse_down_on = part;
|
drag = part;
|
||||||
drop_position = -1;
|
findParent(*part, drag_parent, drag_position);
|
||||||
|
drop_parent = SymbolGroupP();
|
||||||
CaptureMouse();
|
CaptureMouse();
|
||||||
}
|
}
|
||||||
sendEvent(EVENT_PART_SELECT);
|
sendEvent(EVENT_PART_SELECT);
|
||||||
@@ -144,43 +143,65 @@ void SymbolPartList::onLeftDown(wxMouseEvent& ev) {
|
|||||||
}
|
}
|
||||||
void SymbolPartList::onLeftUp(wxMouseEvent& ev) {
|
void SymbolPartList::onLeftUp(wxMouseEvent& ev) {
|
||||||
if (HasCapture()) ReleaseMouse();
|
if (HasCapture()) ReleaseMouse();
|
||||||
if (mouse_down_on && drop_position != -1) {
|
if (drag_parent && drop_parent) {
|
||||||
// move part
|
// move part
|
||||||
// find old position
|
if (drag_parent == drop_parent && drag_position < drop_position) {
|
||||||
vector<SymbolPartP>::const_iterator it = find(symbol->parts.begin(), symbol->parts.end(), mouse_down_on);
|
drop_position -= 1; // adjust for removal of the dragged part
|
||||||
mouse_down_on = SymbolPartP();
|
|
||||||
if (it == symbol->parts.end()) {
|
|
||||||
Refresh(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size_t old_position = it - symbol->parts.begin();
|
|
||||||
// find new position
|
|
||||||
size_t new_position;
|
|
||||||
SymbolPartP drop_before = findItem(drop_position);
|
|
||||||
it = find(symbol->parts.begin(), symbol->parts.end(), drop_before);
|
|
||||||
if (it == symbol->parts.end()) {
|
|
||||||
new_position = number_of_items - 1;
|
|
||||||
} else {
|
|
||||||
new_position = it - symbol->parts.begin();
|
|
||||||
if (old_position < new_position) new_position -= 1;
|
|
||||||
}
|
}
|
||||||
// move part
|
// move part
|
||||||
if (old_position != new_position) {
|
SymbolGroupP par = drag_parent; drag_parent = SymbolGroupP();
|
||||||
symbol->actions.add(new ReorderSymbolPartsAction(*symbol, old_position, new_position));
|
if (par != drop_parent || drag_position != drop_position) {
|
||||||
|
if (par != drop_parent && par->parts.size() == 1 && !par->isSymbolSymmetry()) {
|
||||||
|
// this leaves a group without elements, remove it
|
||||||
|
findParent(*par, par, drag_position); // parent of the group
|
||||||
|
symbol->actions.add(new UngroupReorderSymbolPartsAction(*par, drag_position, *drop_parent, drop_position));
|
||||||
|
} else {
|
||||||
|
symbol->actions.add(new ReorderSymbolPartsAction(*par, drag_position, *drop_parent, drop_position));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mouse_down_on = SymbolPartP();
|
drag_parent = SymbolGroupP();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void SymbolPartList::onMotion(wxMouseEvent& ev) {
|
void SymbolPartList::onMotion(wxMouseEvent& ev) {
|
||||||
if (mouse_down_on) {
|
int top; GetViewStart(0, &top);
|
||||||
int new_drop_position = (ev.GetY() + ITEM_HEIGHT/2) / (ITEM_HEIGHT + 1);
|
if (drag_parent) {
|
||||||
if (new_drop_position < 0 || new_drop_position > number_of_items) new_drop_position = -1;
|
int pos = top + (ev.GetY() + ITEM_HEIGHT/2) / (ITEM_HEIGHT + 1);
|
||||||
// TODO: make sure it is not in a group
|
if (pos < 0) pos = 0;
|
||||||
if (drop_position != new_drop_position) {
|
if (pos >= number_of_items) pos = number_of_items;
|
||||||
drop_position = new_drop_position;
|
bool before = ev.GetY() < (pos - top) * (ITEM_HEIGHT + 1);
|
||||||
|
// old stuff
|
||||||
|
SymbolGroupP old_drop_parent = drop_parent;
|
||||||
|
size_t old_drop_position = drop_position;
|
||||||
|
bool old_drop_inside = drop_inside;
|
||||||
|
// find drop target
|
||||||
|
drop_parent = SymbolGroupP();
|
||||||
|
findDropTarget(symbol, pos, before);
|
||||||
|
// the drop parent must be an ancestor or sibling of ancestor of the drag_parent
|
||||||
|
// i.e. the drop parent's parent must be an ancestor of drag_parent
|
||||||
|
if (drop_parent) {
|
||||||
|
drop_inside = !drop_parent->isAncestor(*drag_parent);
|
||||||
|
while(drop_parent != symbol) {
|
||||||
|
// is drop_parent a sibling of an ancestor of drag_parent?
|
||||||
|
SymbolGroupP drop_parent_parent;
|
||||||
|
size_t drop_parent_position;
|
||||||
|
findParent(*drop_parent, drop_parent_parent, drop_parent_position);
|
||||||
|
if (!drop_parent_parent->isAncestor(*drag_parent)) {
|
||||||
|
// move up one level
|
||||||
|
drop_parent = drop_parent_parent;
|
||||||
|
drop_position = drop_parent_position;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (drop_parent == symbol) {
|
||||||
|
drop_inside = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// refresh?
|
||||||
|
if (drop_parent != old_drop_parent || drop_position != old_drop_position || drop_inside != old_drop_inside) {
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,26 +283,76 @@ void SymbolPartList::sendEvent(int type) {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Items
|
// ----------------------------------------------------------------------------- : Items
|
||||||
|
|
||||||
SymbolPartP SymbolPartList::findItem(int i) const {
|
SymbolPartP SymbolPartList::findItem(int i, int x) const {
|
||||||
FOR_EACH(p, symbol->parts) {
|
FOR_EACH(p, symbol->parts) {
|
||||||
SymbolPartP f = findItem(i, p);
|
SymbolPartP f = findItem(i, x, p);
|
||||||
if (f) return f;
|
if (f) return f;
|
||||||
}
|
}
|
||||||
return SymbolPartP();
|
return SymbolPartP();
|
||||||
}
|
}
|
||||||
SymbolPartP SymbolPartList::findItem(int& i, const SymbolPartP& part) {
|
SymbolPartP SymbolPartList::findItem(int& i, int x, const SymbolPartP& part) {
|
||||||
if (i < 0 ) return SymbolPartP();
|
if (i < 0 ) return SymbolPartP();
|
||||||
if (i == 0) return part;
|
if (i == 0) return part;
|
||||||
i -= 1;
|
i -= 1;
|
||||||
// sub item?
|
// sub item?
|
||||||
if (SymbolGroup* g = part->isSymbolGroup()) {
|
if (SymbolGroup* g = part->isSymbolGroup()) {
|
||||||
FOR_EACH(p, g->parts) {
|
FOR_EACH(p, g->parts) {
|
||||||
if (findItem(i, p)) return part;
|
SymbolPartP f = findItem(i, x - 5, p);
|
||||||
|
if (f) return x < 5 ? part : f; // clicked on bar at the left of group?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SymbolPartP();
|
return SymbolPartP();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SymbolPartList::findParent(const SymbolPart& of, SymbolGroupP& parent_out, size_t& pos_out) {
|
||||||
|
if (!findParent(of, symbol, parent_out, pos_out)) {
|
||||||
|
throw InternalError(_("Symbol part without a parent"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool SymbolPartList::findParent(const SymbolPart& of, const SymbolGroupP& g, SymbolGroupP& parent_out, size_t& pos_out) {
|
||||||
|
if (!g) return false;
|
||||||
|
for (size_t i = 0 ; i < g->parts.size() ; ++i) {
|
||||||
|
if (g->parts[i].get() == &of) {
|
||||||
|
parent_out = g;
|
||||||
|
pos_out = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (findParent(of, dynamic_pointer_cast<SymbolGroup>(g->parts[i]), parent_out, pos_out)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SymbolPartList::findDropTarget(const SymbolPartP& parent, int& i, bool before) {
|
||||||
|
if (parent != symbol) --i;
|
||||||
|
if (SymbolGroup* g = parent->isSymbolGroup()) {
|
||||||
|
size_t pos = 0;
|
||||||
|
FOR_EACH(p, g->parts) {
|
||||||
|
if (i <= 0) {
|
||||||
|
// drop before this part
|
||||||
|
drop_parent = static_pointer_cast<SymbolGroup>(parent);
|
||||||
|
drop_position = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (p == drag) {
|
||||||
|
i -= childCount(p) + 1; // don't drop inside
|
||||||
|
} else {
|
||||||
|
if (findDropTarget(p, i, before)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
if (i <= 0 && (parent == symbol || before)) {
|
||||||
|
// drop at the end
|
||||||
|
drop_parent = static_pointer_cast<SymbolGroup>(parent);
|
||||||
|
drop_position = g->parts.size();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int SymbolPartList::childCount(const SymbolPartP& part) {
|
int SymbolPartList::childCount(const SymbolPartP& part) {
|
||||||
if (SymbolGroup* g = part->isSymbolGroup()) {
|
if (SymbolGroup* g = part->isSymbolGroup()) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@@ -292,9 +363,6 @@ int SymbolPartList::childCount(const SymbolPartP& part) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Text editor
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Drawing
|
// ----------------------------------------------------------------------------- : Drawing
|
||||||
|
|
||||||
|
|
||||||
@@ -307,23 +375,13 @@ void SymbolPartList::OnDraw(DC& dc) {
|
|||||||
// init
|
// init
|
||||||
dc.SetFont(*wxNORMAL_FONT);
|
dc.SetFont(*wxNORMAL_FONT);
|
||||||
// clear background
|
// clear background
|
||||||
wxSize size = GetClientSize();
|
wxSize size = piecewise_max(GetVirtualSize() + RealSize(0,ITEM_HEIGHT+1), GetClientSize());
|
||||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||||
dc.DrawRectangle(0,0,size.x,size.y);
|
dc.DrawRectangle(0,0,size.x,size.y);
|
||||||
// items
|
// items
|
||||||
int i = 0;
|
int i = 0;
|
||||||
FOR_EACH(p, symbol->parts) {
|
drawItem(dc, 0, i, false, symbol);
|
||||||
drawItem(dc, 0, i, false, p);
|
|
||||||
}
|
|
||||||
// drag/drop indicator
|
|
||||||
if (mouse_down_on && drop_position != -1) {
|
|
||||||
dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT), 3));
|
|
||||||
int y = drop_position * (ITEM_HEIGHT + 1) - 1;
|
|
||||||
dc.DrawLine(0,y,size.x,y);
|
|
||||||
dc.DrawLine(0,y-3,0,y+3);
|
|
||||||
dc.DrawLine(size.x-1,y-3,size.x-1,y+3);
|
|
||||||
}
|
|
||||||
// hide caret
|
// hide caret
|
||||||
if (selection.size() != 1) {
|
if (selection.size() != 1) {
|
||||||
typing_in = SymbolPartP();
|
typing_in = SymbolPartP();
|
||||||
@@ -339,7 +397,7 @@ void SymbolPartList::drawItem(DC& dc, int x, int& i, bool parent_active, const S
|
|||||||
// draw item : highlight
|
// draw item : highlight
|
||||||
Color background;
|
Color background;
|
||||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||||
bool active = selection.find(part) != selection.end();
|
bool active = selection.selected(part);
|
||||||
if (active) {
|
if (active) {
|
||||||
background = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
|
background = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
|
||||||
dc.SetBrush(background);
|
dc.SetBrush(background);
|
||||||
@@ -352,47 +410,67 @@ void SymbolPartList::drawItem(DC& dc, int x, int& i, bool parent_active, const S
|
|||||||
background = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
background = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||||
}
|
}
|
||||||
// draw item : name
|
|
||||||
int h = dc.GetCharHeight();
|
|
||||||
dc.DrawText(part->name, ITEM_HEIGHT + x + 3, y + (ITEM_HEIGHT - h) / 2);
|
|
||||||
// draw item : icon
|
|
||||||
dc.SetBrush(lerp(background,wxColour(0,128,0),0.7));
|
|
||||||
dc.DrawRectangle(x,y,ITEM_HEIGHT,ITEM_HEIGHT);
|
|
||||||
dc.DrawBitmap(symbolPreview(), x, y);
|
|
||||||
dc.DrawBitmap(itemPreview(i,part), x, y);
|
|
||||||
// draw item : border
|
|
||||||
wxPen line_pen = lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),
|
wxPen line_pen = lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),
|
||||||
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.5);
|
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.5);
|
||||||
dc.SetPen(line_pen);
|
if (part != symbol) {
|
||||||
dc.DrawLine(x+ITEM_HEIGHT, y, x+ITEM_HEIGHT, y + ITEM_HEIGHT + 1); // line after image
|
// draw item : name
|
||||||
dc.DrawLine(x, y + ITEM_HEIGHT, size.x, y + ITEM_HEIGHT); // line below
|
int h = dc.GetCharHeight();
|
||||||
// update caret
|
dc.DrawText(part->name, ITEM_HEIGHT + x + 3, y + (ITEM_HEIGHT - h) / 2);
|
||||||
if (selection.size() == 1 && active) {
|
// draw item : icon
|
||||||
updateCaret(dc, x + ITEM_HEIGHT + 3, y + (ITEM_HEIGHT - h) / 2, h, part);
|
dc.SetBrush(lerp(background,wxColour(0,128,0),0.7));
|
||||||
}
|
dc.DrawRectangle(x,y,ITEM_HEIGHT,ITEM_HEIGHT);
|
||||||
// move down
|
dc.DrawBitmap(symbolPreview(), x, y);
|
||||||
i += 1;
|
dc.DrawBitmap(itemPreview(i,part), x, y);
|
||||||
// draw more?
|
// draw item : border
|
||||||
if (SymbolShape* s = part->isSymbolShape()) {
|
dc.SetPen(line_pen);
|
||||||
// combine state
|
dc.DrawLine(x+ITEM_HEIGHT, y, x+ITEM_HEIGHT, y + ITEM_HEIGHT + 1); // line after image
|
||||||
state_icons.Draw(s->combine, dc, size.x - 10, y + 1);
|
dc.DrawLine(x, y + ITEM_HEIGHT, size.x, y + ITEM_HEIGHT); // line below
|
||||||
} else if (SymbolSymmetry* s = part->isSymbolSymmetry()) {
|
// update caret
|
||||||
// kind of symmetry
|
if (selection.size() == 1 && active) {
|
||||||
state_icons.Draw(s->kind, dc, size.x - 10, y + 1);
|
updateCaret(dc, x + ITEM_HEIGHT + 3, y + (ITEM_HEIGHT - h) / 2, h, part);
|
||||||
// TODO: show clip mode?
|
}
|
||||||
} else if (SymbolGroup* g = part->isSymbolGroup()) {
|
// draw icon
|
||||||
state_icons.Draw(SYMMETRY_REFLECTION + 1, dc, size.x - 10, y + 1);
|
state_icons.Draw(part->icon(), dc, size.x - 10, y + 1);
|
||||||
FOR_EACH(p, g->parts) drawItem(dc, x + 5, i, active || parent_active, p);
|
// move down
|
||||||
// draw bar on the left
|
i += 1;
|
||||||
int new_y = i * (ITEM_HEIGHT + 1);
|
}
|
||||||
y += ITEM_HEIGHT+1;
|
// Draw children
|
||||||
if (y != new_y) {
|
int child_x = part == symbol ? x : x + 5;
|
||||||
dc.SetPen(line_pen);
|
if (SymbolGroup* g = part->isSymbolGroup()) {
|
||||||
dc.SetBrush(background);
|
FOR_EACH(p, g->parts) drawItem(dc, child_x, i, active || parent_active, p);
|
||||||
dc.DrawRectangle(x-1,y-1,5+1,new_y-y+1);
|
}
|
||||||
|
// draw bar on the left?
|
||||||
|
int old_y = y + ITEM_HEIGHT+1; // after part itself
|
||||||
|
int new_y = i * (ITEM_HEIGHT + 1); // after children
|
||||||
|
if (old_y != new_y && part != symbol) {
|
||||||
|
dc.SetPen(line_pen);
|
||||||
|
dc.SetBrush(background);
|
||||||
|
dc.DrawRectangle(x-1,old_y-1,5+1,new_y-old_y+1);
|
||||||
|
}
|
||||||
|
// Drop indicator?
|
||||||
|
if (drag_parent && drop_parent) {
|
||||||
|
dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT), 3));
|
||||||
|
if (drop_inside) {
|
||||||
|
if (part == drop_parent) {
|
||||||
|
// drop inside part
|
||||||
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||||
|
dc.DrawRectangle(x, y, w, new_y - y);
|
||||||
|
}
|
||||||
|
} else if (drop_position < drop_parent->parts.size()) {
|
||||||
|
if (part == drop_parent->parts[drop_position]) {
|
||||||
|
// drop before part
|
||||||
|
dc.DrawLine(x,y,size.x,y);
|
||||||
|
dc.DrawLine(x,y-3,x,y+3);
|
||||||
|
dc.DrawLine(size.x-1,y-3,size.x-1,y+3);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (part == drop_parent) {
|
||||||
|
// drop after part
|
||||||
|
dc.DrawLine(child_x,new_y,size.x,new_y);
|
||||||
|
dc.DrawLine(child_x,new_y-3,child_x,new_y+3);
|
||||||
|
dc.DrawLine(size.x-1,new_y-3,size.x-1,new_y+3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
throw "Unknown symbol part type";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,6 +510,7 @@ const Image& SymbolPartList::symbolPreview() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SymbolPartList::updateCaret(DC& dc, int x, int y, int h, const SymbolPartP& part) {
|
void SymbolPartList::updateCaret(DC& dc, int x, int y, int h, const SymbolPartP& part) {
|
||||||
|
int top; GetViewStart(0, &top);
|
||||||
// make caret
|
// make caret
|
||||||
wxCaret* caret = GetCaret();
|
wxCaret* caret = GetCaret();
|
||||||
if (!caret) {
|
if (!caret) {
|
||||||
@@ -446,7 +525,7 @@ void SymbolPartList::updateCaret(DC& dc, int x, int y, int h, const SymbolPartP&
|
|||||||
cursor = min(cursor, typing_in->name.size());
|
cursor = min(cursor, typing_in->name.size());
|
||||||
int w;
|
int w;
|
||||||
dc.GetTextExtent(typing_in->name.substr(0,cursor), &w, nullptr);
|
dc.GetTextExtent(typing_in->name.substr(0,cursor), &w, nullptr);
|
||||||
caret->Move(x+w,y);
|
caret->Move(x+w, y - top*(ITEM_HEIGHT+1));
|
||||||
if (!caret->IsVisible()) caret->Show();
|
if (!caret->IsVisible()) caret->Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include <util/prec.hpp>
|
#include <util/prec.hpp>
|
||||||
#include <data/symbol.hpp>
|
#include <data/symbol.hpp>
|
||||||
|
|
||||||
|
class SymbolPartsSelection;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Events
|
// ----------------------------------------------------------------------------- : Events
|
||||||
|
|
||||||
DECLARE_EVENT_TYPE(EVENT_PART_SELECT, <not used>)
|
DECLARE_EVENT_TYPE(EVENT_PART_SELECT, <not used>)
|
||||||
@@ -26,7 +28,7 @@ DECLARE_EVENT_TYPE(EVENT_PART_ACTIVATE, <not used>)
|
|||||||
|
|
||||||
class SymbolPartList : public wxScrolledWindow, public SymbolView {
|
class SymbolPartList : public wxScrolledWindow, public SymbolView {
|
||||||
public:
|
public:
|
||||||
SymbolPartList(Window* parent, int id, set<SymbolPartP>& selection, SymbolP symbol = SymbolP());
|
SymbolPartList(Window* parent, int id, SymbolPartsSelection& selection, SymbolP symbol = SymbolP());
|
||||||
|
|
||||||
/// Another symbol is being viewed
|
/// Another symbol is being viewed
|
||||||
virtual void onChangeSymbol();
|
virtual void onChangeSymbol();
|
||||||
@@ -41,12 +43,14 @@ class SymbolPartList : public wxScrolledWindow, public SymbolView {
|
|||||||
protected:
|
protected:
|
||||||
virtual wxSize DoGetBestSize() const;
|
virtual wxSize DoGetBestSize() const;
|
||||||
private:
|
private:
|
||||||
set<SymbolPartP>& selection; ///< Store selection here
|
SymbolPartsSelection& selection; ///< Store selection here
|
||||||
|
|
||||||
SymbolPartP mouse_down_on;
|
|
||||||
int drop_position;
|
|
||||||
int number_of_items;
|
int number_of_items;
|
||||||
|
|
||||||
|
SymbolPartP drag;
|
||||||
|
SymbolGroupP drag_parent, drop_parent;
|
||||||
|
size_t drag_position, drop_position;
|
||||||
|
bool drop_inside; // drop inside the drop parent, not at a specific position
|
||||||
|
|
||||||
SymbolPartP typing_in;
|
SymbolPartP typing_in;
|
||||||
size_t cursor;
|
size_t cursor;
|
||||||
|
|
||||||
@@ -79,8 +83,20 @@ class SymbolPartList : public wxScrolledWindow, public SymbolView {
|
|||||||
const Image& symbolPreview();
|
const Image& symbolPreview();
|
||||||
void updatePart(const set<SymbolPartP>& parts, int& i, bool parent_updated, const SymbolPartP& part);
|
void updatePart(const set<SymbolPartP>& parts, int& i, bool parent_updated, const SymbolPartP& part);
|
||||||
|
|
||||||
SymbolPartP findItem(int i) const;
|
/// find item by position
|
||||||
static SymbolPartP findItem(int& i, const SymbolPartP& part);
|
SymbolPartP findItem(int i, int x) const;
|
||||||
|
static SymbolPartP findItem(int& i, int x, const SymbolPartP& part);
|
||||||
|
|
||||||
|
/// parent of 'of' and the position of 'of' in that parent
|
||||||
|
void findParent(const SymbolPart& of, SymbolGroupP& parent_out, size_t& pos_out);
|
||||||
|
static bool findParent(const SymbolPart& of, const SymbolGroupP& in, SymbolGroupP& parent_out, size_t& pos_out);
|
||||||
|
|
||||||
|
/// Where is the drop position?
|
||||||
|
/** i = index before which the cursor is
|
||||||
|
* before = is the cursor before or after the separator line?
|
||||||
|
* Returns whether a drop position was found, sets drop_...
|
||||||
|
*/
|
||||||
|
bool findDropTarget(const SymbolPartP& parent, int& i, bool before);
|
||||||
|
|
||||||
static int childCount(const SymbolPartP& part);
|
static int childCount(const SymbolPartP& part);
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ DECLARE_TYPEOF_COLLECTION(SymbolPartP);
|
|||||||
|
|
||||||
SymbolSelectEditor::SymbolSelectEditor(SymbolControl* control, bool rotate)
|
SymbolSelectEditor::SymbolSelectEditor(SymbolControl* control, bool rotate)
|
||||||
: SymbolEditorBase(control)
|
: SymbolEditorBase(control)
|
||||||
|
, click_mode(CLICK_NONE)
|
||||||
, rotate(rotate)
|
, rotate(rotate)
|
||||||
, cursorRotate(load_resource_cursor(_("rotate")))
|
, cursorRotate(load_resource_cursor(_("rotate")))
|
||||||
, cursorShearX(load_resource_cursor(_("shear_x")))
|
, cursorShearX(load_resource_cursor(_("shear_x")))
|
||||||
@@ -45,19 +46,27 @@ SymbolSelectEditor::SymbolSelectEditor(SymbolControl* control, bool rotate)
|
|||||||
|
|
||||||
void SymbolSelectEditor::draw(DC& dc) {
|
void SymbolSelectEditor::draw(DC& dc) {
|
||||||
// highlight selected parts
|
// highlight selected parts
|
||||||
FOR_EACH(p, control.selected_parts) {
|
FOR_EACH(p, control.selected_parts.get()) {
|
||||||
control.highlightPart(dc, *p, HIGHLIGHT_INTERIOR);
|
control.highlightPart(dc, *p, HIGHLIGHT_INTERIOR);
|
||||||
}
|
}
|
||||||
// highlight the part under the cursor
|
// highlight the part under the cursor
|
||||||
if (highlightPart) {
|
if (highlightPart) {
|
||||||
control.highlightPart(dc, *highlightPart, HIGHLIGHT_BORDER);
|
control.highlightPart(dc, *highlightPart, HIGHLIGHT_BORDER);
|
||||||
}
|
}
|
||||||
// draw handles
|
if (click_mode == CLICK_RECT) {
|
||||||
drawHandles(dc);
|
// draw selection rectangle
|
||||||
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||||
|
dc.SetPen(wxPen(*wxBLUE,1,wxDOT));
|
||||||
|
RealRect rect = control.rotation.tr(RealRect(selection_rect_a, RealSize(selection_rect_b - selection_rect_a)));
|
||||||
|
dc.DrawRectangle(rect);
|
||||||
|
} else {
|
||||||
|
// draw handles
|
||||||
|
drawHandles(dc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolSelectEditor::drawHandles(DC& dc) {
|
void SymbolSelectEditor::drawHandles(DC& dc) {
|
||||||
if (control.selected_parts.empty()) return;
|
if (control.selected_parts.empty()) return;
|
||||||
if (rotateAction) return; // not when rotating
|
if (rotateAction) return; // not when rotating
|
||||||
updateBoundingBox();
|
updateBoundingBox();
|
||||||
// Draw handles on all sides
|
// Draw handles on all sides
|
||||||
@@ -130,7 +139,7 @@ void SymbolSelectEditor::onUpdateUI(wxUpdateUIEvent& ev) {
|
|||||||
if (ev.GetId() >= ID_SYMBOL_COMBINE && ev.GetId() < ID_SYMBOL_COMBINE_MAX) {
|
if (ev.GetId() >= ID_SYMBOL_COMBINE && ev.GetId() < ID_SYMBOL_COMBINE_MAX) {
|
||||||
bool enable = false;
|
bool enable = false;
|
||||||
bool check = true;
|
bool check = true;
|
||||||
FOR_EACH(p, control.selected_parts) {
|
FOR_EACH(p, control.selected_parts.get()) {
|
||||||
if (SymbolShape* s = p->isSymbolShape()) {
|
if (SymbolShape* s = p->isSymbolShape()) {
|
||||||
enable = true;
|
enable = true;
|
||||||
if (s->combine != ev.GetId() - ID_SYMBOL_COMBINE) {
|
if (s->combine != ev.GetId() - ID_SYMBOL_COMBINE) {
|
||||||
@@ -147,8 +156,8 @@ void SymbolSelectEditor::onUpdateUI(wxUpdateUIEvent& ev) {
|
|||||||
ev.Enable(control.selected_parts.size() >= 2);
|
ev.Enable(control.selected_parts.size() >= 2);
|
||||||
} else if (ev.GetId() == ID_EDIT_UNGROUP) {
|
} else if (ev.GetId() == ID_EDIT_UNGROUP) {
|
||||||
// is a group selected
|
// is a group selected
|
||||||
FOR_EACH(p, control.selected_parts) {
|
FOR_EACH(p, control.selected_parts.get()) {
|
||||||
if (p->isSymbolGroup()) {
|
if (p->isSymbolGroup() && !p->isSymbolSymmetry()) {
|
||||||
ev.Enable(true);
|
ev.Enable(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -163,21 +172,21 @@ void SymbolSelectEditor::onCommand(int id) {
|
|||||||
if (id >= ID_SYMBOL_COMBINE && id < ID_SYMBOL_COMBINE_MAX) {
|
if (id >= ID_SYMBOL_COMBINE && id < ID_SYMBOL_COMBINE_MAX) {
|
||||||
// change combine mode
|
// change combine mode
|
||||||
getSymbol()->actions.add(new CombiningModeAction(
|
getSymbol()->actions.add(new CombiningModeAction(
|
||||||
control.selected_parts,
|
control.selected_parts.get(),
|
||||||
static_cast<SymbolShapeCombine>(id - ID_SYMBOL_COMBINE)
|
static_cast<SymbolShapeCombine>(id - ID_SYMBOL_COMBINE)
|
||||||
));
|
));
|
||||||
control.Refresh(false);
|
control.Refresh(false);
|
||||||
} else if (id == ID_EDIT_DUPLICATE && !isEditing()) {
|
} else if (id == ID_EDIT_DUPLICATE && !isEditing()) {
|
||||||
// duplicate selection, not when dragging
|
// duplicate selection, not when dragging
|
||||||
getSymbol()->actions.add(new DuplicateSymbolPartsAction(*getSymbol(), control.selected_parts));
|
getSymbol()->actions.add(new DuplicateSymbolPartsAction(*getSymbol(), control.selected_parts.get()));
|
||||||
control.Refresh(false);
|
control.Refresh(false);
|
||||||
} else if (id == ID_EDIT_GROUP && !isEditing()) {
|
} else if (id == ID_EDIT_GROUP && !isEditing()) {
|
||||||
// group selection, not when dragging
|
// group selection, not when dragging
|
||||||
getSymbol()->actions.add(new GroupSymbolPartsAction(*getSymbol(), control.selected_parts));
|
getSymbol()->actions.add(new GroupSymbolPartsAction(*getSymbol(), control.selected_parts.get(), new_intrusive<SymbolGroup>()));
|
||||||
control.Refresh(false);
|
control.Refresh(false);
|
||||||
} else if (id == ID_EDIT_UNGROUP && !isEditing()) {
|
} else if (id == ID_EDIT_UNGROUP && !isEditing()) {
|
||||||
// ungroup selection, not when dragging
|
// ungroup selection, not when dragging
|
||||||
getSymbol()->actions.add(new UngroupSymbolPartsAction(*getSymbol(), control.selected_parts));
|
getSymbol()->actions.add(new UngroupSymbolPartsAction(*getSymbol(), control.selected_parts.get()));
|
||||||
control.Refresh(false);
|
control.Refresh(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,35 +198,27 @@ int SymbolSelectEditor::modeToolId() {
|
|||||||
// ----------------------------------------------------------------------------- : Mouse Events
|
// ----------------------------------------------------------------------------- : Mouse Events
|
||||||
|
|
||||||
void SymbolSelectEditor::onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {
|
void SymbolSelectEditor::onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {
|
||||||
have_dragged = true;
|
|
||||||
// change selection
|
// change selection
|
||||||
// Are we on a handle?
|
// Are we on a handle?
|
||||||
int dx, dy;
|
int dx, dy;
|
||||||
if (onAnyHandle(pos, &dx, &dy)) return; // don't change the selection
|
if (onAnyHandle(pos, &dx, &dy)) {
|
||||||
|
click_mode = CLICK_HANDLE;
|
||||||
|
return; // don't change the selection
|
||||||
|
}
|
||||||
// Select the part under the cursor
|
// Select the part under the cursor
|
||||||
SymbolPartP part = findPart(pos);
|
SymbolPartP part = control.selected_parts.find(pos);
|
||||||
if (part) {
|
if (part) {
|
||||||
if (ev.ShiftDown()) {
|
click_mode = control.selected_parts.select(part, ev.ShiftDown() ? SELECT_TOGGLE : SELECT_IF_OUTSIDE)
|
||||||
// toggle selection
|
? (ev.ShiftDown() ? CLICK_NONE : CLICK_MOVE)
|
||||||
set<SymbolPartP>::iterator it = control.selected_parts.find(part);
|
: CLICK_TOGGLE;
|
||||||
if (it != control.selected_parts.end()) {
|
} else {
|
||||||
control.selected_parts.erase(it);
|
// selection rectangle
|
||||||
} else {
|
click_mode = CLICK_RECT;
|
||||||
control.selected_parts.insert(part);
|
selection_rect_a = selection_rect_b = pos;
|
||||||
}
|
if (!ev.ShiftDown()) {
|
||||||
} else {
|
// select nothing
|
||||||
if (control.selected_parts.find(part) != control.selected_parts.end()) {
|
control.selected_parts.clear();
|
||||||
// already selected, do nothing
|
|
||||||
have_dragged = false; // we haven't done anything
|
|
||||||
} else {
|
|
||||||
// select the part under the cursor
|
|
||||||
control.selected_parts.clear();
|
|
||||||
control.selected_parts.insert(part);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (!ev.ShiftDown()) {
|
|
||||||
// select nothing
|
|
||||||
control.selected_parts.clear();
|
|
||||||
}
|
}
|
||||||
// selection has changed
|
// selection has changed
|
||||||
updateBoundingBox();
|
updateBoundingBox();
|
||||||
@@ -231,24 +232,18 @@ void SymbolSelectEditor::onLeftUp (const Vector2D& pos, wxMouseEvent& ev) {
|
|||||||
resetActions();
|
resetActions();
|
||||||
} else {
|
} else {
|
||||||
// mouse not moved -> change selection
|
// mouse not moved -> change selection
|
||||||
if (!have_dragged && !ev.ShiftDown()) {
|
if (click_mode == CLICK_TOGGLE) {
|
||||||
int dx, dy;
|
// switch between rotate and resize mode
|
||||||
if (onAnyHandle(pos, &dx, &dy)) return; // don't change the selection
|
rotate = !rotate;
|
||||||
// Find the part under the cursor
|
|
||||||
SymbolPartP part = findPart(pos);
|
|
||||||
if (control.selected_parts.find(part) != control.selected_parts.end()) {
|
|
||||||
// already selected, don't change selection
|
|
||||||
// instead switch between rotate and resize mode
|
|
||||||
rotate = !rotate;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
click_mode = CLICK_NONE;
|
||||||
control.Refresh(false);
|
control.Refresh(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolSelectEditor::onLeftDClick(const Vector2D& pos, wxMouseEvent& ev) {
|
void SymbolSelectEditor::onLeftDClick(const Vector2D& pos, wxMouseEvent& ev) {
|
||||||
// start editing the points of the clicked part
|
// start editing the points of the clicked part
|
||||||
highlightPart = findPart(pos);
|
highlightPart = control.selected_parts.find(pos);
|
||||||
if (highlightPart) {
|
if (highlightPart) {
|
||||||
control.activatePart(highlightPart);
|
control.activatePart(highlightPart);
|
||||||
}
|
}
|
||||||
@@ -256,7 +251,7 @@ void SymbolSelectEditor::onLeftDClick(const Vector2D& pos, wxMouseEvent& ev) {
|
|||||||
|
|
||||||
void SymbolSelectEditor::onMouseMove (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
|
void SymbolSelectEditor::onMouseMove (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
|
||||||
// can we highlight a part?
|
// can we highlight a part?
|
||||||
highlightPart = findPart(to);
|
highlightPart = control.selected_parts.find(to);
|
||||||
// are we on a handle?
|
// are we on a handle?
|
||||||
int dx, dy;
|
int dx, dy;
|
||||||
if (!control.selected_parts.empty() && onAnyHandle(to, &dx, &dy)) {
|
if (!control.selected_parts.empty() && onAnyHandle(to, &dx, &dy)) {
|
||||||
@@ -296,31 +291,39 @@ template <typename Event> int snap(Event& ev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SymbolSelectEditor::onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
|
void SymbolSelectEditor::onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
|
||||||
have_dragged = true;
|
if (click_mode == CLICK_NONE) return;
|
||||||
if (control.selected_parts.empty()) return;
|
if (control.selected_parts.empty()) return;
|
||||||
|
if (click_mode == CLICK_RECT) {
|
||||||
|
// rectangle
|
||||||
|
control.selected_parts.selectRect(selection_rect_a, selection_rect_b, to, SELECT_TOGGLE);
|
||||||
|
selection_rect_b = to;
|
||||||
|
control.Refresh(false);
|
||||||
|
}
|
||||||
if (!isEditing()) {
|
if (!isEditing()) {
|
||||||
// we don't have an action yet, determine what to do
|
// we don't have an action yet, determine what to do
|
||||||
// note: base it on the from position, which is the position where dragging started
|
// note: base it on the from position, which is the position where dragging started
|
||||||
if (onAnyHandle(from, &scaleX, &scaleY)) {
|
if (onAnyHandle(from, &scaleX, &scaleY)) {
|
||||||
|
click_mode = CLICK_HANDLE;
|
||||||
if (rotate) {
|
if (rotate) {
|
||||||
if (scaleX == 0 || scaleY == 0) {
|
if (scaleX == 0 || scaleY == 0) {
|
||||||
// shear, center/fixed point on the opposite side
|
// shear, center/fixed point on the opposite side
|
||||||
shearAction = new SymbolPartShearAction(control.selected_parts, handlePos(-scaleX, -scaleY));
|
shearAction = new SymbolPartShearAction(control.selected_parts.get(), handlePos(-scaleX, -scaleY));
|
||||||
getSymbol()->actions.add(shearAction);
|
getSymbol()->actions.add(shearAction);
|
||||||
} else {
|
} else {
|
||||||
// rotate
|
// rotate
|
||||||
rotateAction = new SymbolPartRotateAction(control.selected_parts, center);
|
rotateAction = new SymbolPartRotateAction(control.selected_parts.get(), center);
|
||||||
getSymbol()->actions.add(rotateAction);
|
getSymbol()->actions.add(rotateAction);
|
||||||
startAngle = angleTo(to);
|
startAngle = angleTo(to);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// we are on a handle; start scaling
|
// we are on a handle; start scaling
|
||||||
scaleAction = new SymbolPartScaleAction(control.selected_parts, scaleX, scaleY);
|
scaleAction = new SymbolPartScaleAction(control.selected_parts.get(), scaleX, scaleY);
|
||||||
getSymbol()->actions.add(scaleAction);
|
getSymbol()->actions.add(scaleAction);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// move
|
// move
|
||||||
moveAction = new SymbolPartMoveAction(control.selected_parts);
|
click_mode = CLICK_MOVE;
|
||||||
|
moveAction = new SymbolPartMoveAction(control.selected_parts.get());
|
||||||
getSymbol()->actions.add(moveAction);
|
getSymbol()->actions.add(moveAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,8 +394,8 @@ void SymbolSelectEditor::onKeyChange (wxKeyEvent& ev) {
|
|||||||
void SymbolSelectEditor::onChar(wxKeyEvent& ev) {
|
void SymbolSelectEditor::onChar(wxKeyEvent& ev) {
|
||||||
if (ev.GetKeyCode() == WXK_DELETE) {
|
if (ev.GetKeyCode() == WXK_DELETE) {
|
||||||
// delete selected parts
|
// delete selected parts
|
||||||
getSymbol()->actions.add(new RemoveSymbolPartsAction(*getSymbol(), control.selected_parts));
|
getSymbol()->actions.add(new RemoveSymbolPartsAction(*getSymbol(), control.selected_parts.get()));
|
||||||
if (control.selected_parts.find(highlightPart) != control.selected_parts.end()) highlightPart = SymbolPartP(); // deleted it
|
if (control.selected_parts.selected(highlightPart)) highlightPart = SymbolPartP(); // deleted it
|
||||||
control.selected_parts.clear();
|
control.selected_parts.clear();
|
||||||
resetActions();
|
resetActions();
|
||||||
control.Refresh(false);
|
control.Refresh(false);
|
||||||
@@ -408,7 +411,7 @@ void SymbolSelectEditor::onChar(wxKeyEvent& ev) {
|
|||||||
ev.Skip();
|
ev.Skip();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getSymbol()->actions.add(new SymbolPartMoveAction(control.selected_parts, delta));
|
getSymbol()->actions.add(new SymbolPartMoveAction(control.selected_parts.get(), delta));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,35 +453,11 @@ double SymbolSelectEditor::angleTo(const Vector2D& pos) {
|
|||||||
return atan2(center.x - pos.x, center.y - pos.y);
|
return atan2(center.x - pos.x, center.y - pos.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SymbolPartP find_part(const SymbolPartP& part, const Vector2D& pos) {
|
|
||||||
if (SymbolShape* s = part->isSymbolShape()) {
|
|
||||||
if (point_in_shape(pos, *s)) return part;
|
|
||||||
} else if (SymbolSymmetry* s = part->isSymbolSymmetry()) {
|
|
||||||
// TODO
|
|
||||||
} else if (SymbolGroup* g = part->isSymbolGroup()) {
|
|
||||||
FOR_EACH(p, g->parts) {
|
|
||||||
if (find_part(p,pos)) return part;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw InternalError(_("Invalid symbol part type"));
|
|
||||||
}
|
|
||||||
return SymbolPartP();
|
|
||||||
}
|
|
||||||
|
|
||||||
SymbolPartP SymbolSelectEditor::findPart(const Vector2D& pos) {
|
|
||||||
FOR_EACH(p, getSymbol()->parts) {
|
|
||||||
SymbolPartP found = find_part(p, pos);
|
|
||||||
if (found) return found;
|
|
||||||
}
|
|
||||||
return SymbolPartP();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SymbolSelectEditor::updateBoundingBox() {
|
void SymbolSelectEditor::updateBoundingBox() {
|
||||||
// Find min and max coordinates
|
// Find min and max coordinates
|
||||||
minV = Vector2D::infinity();
|
minV = Vector2D::infinity();
|
||||||
maxV = -Vector2D::infinity();
|
maxV = -Vector2D::infinity();
|
||||||
FOR_EACH(p, control.selected_parts) {
|
FOR_EACH(p, control.selected_parts.get()) {
|
||||||
minV = piecewise_min(minV, p->min_pos);
|
minV = piecewise_min(minV, p->min_pos);
|
||||||
maxV = piecewise_max(maxV, p->max_pos);
|
maxV = piecewise_max(maxV, p->max_pos);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,15 @@ class SymbolSelectEditor : public SymbolEditorBase {
|
|||||||
Vector2D minV, maxV;
|
Vector2D minV, maxV;
|
||||||
// Where is the rotation center?
|
// Where is the rotation center?
|
||||||
Vector2D center;
|
Vector2D center;
|
||||||
|
// What kind of clicking/dragging are we doing
|
||||||
|
enum ClickMode {
|
||||||
|
CLICK_NONE,
|
||||||
|
CLICK_MOVE, // moving parts around
|
||||||
|
CLICK_HANDLE, // dragging a handle
|
||||||
|
CLICK_CENTER, // dragging the rotation center
|
||||||
|
CLICK_RECT, // selection rectangle
|
||||||
|
CLICK_TOGGLE, // same selection, not moved -> switch to rotate mode
|
||||||
|
} click_mode;
|
||||||
// At what angle is the handle we started draging for rotation
|
// At what angle is the handle we started draging for rotation
|
||||||
double startAngle;
|
double startAngle;
|
||||||
// what side are we dragging/rotating on?
|
// what side are we dragging/rotating on?
|
||||||
@@ -82,6 +91,8 @@ class SymbolSelectEditor : public SymbolEditorBase {
|
|||||||
bool have_dragged;
|
bool have_dragged;
|
||||||
// Do we want to rotate?
|
// Do we want to rotate?
|
||||||
bool rotate;
|
bool rotate;
|
||||||
|
// selection rectangle
|
||||||
|
Vector2D selection_rect_a, selection_rect_b;
|
||||||
// Graphics assets
|
// Graphics assets
|
||||||
wxCursor cursorRotate;
|
wxCursor cursorRotate;
|
||||||
wxCursor cursorShearX;
|
wxCursor cursorShearX;
|
||||||
@@ -104,9 +115,6 @@ class SymbolSelectEditor : public SymbolEditorBase {
|
|||||||
/// Return the position of a handle, dx,dy in <-1, 0, 1>
|
/// Return the position of a handle, dx,dy in <-1, 0, 1>
|
||||||
Vector2D handlePos(int dx, int dy);
|
Vector2D handlePos(int dx, int dy);
|
||||||
|
|
||||||
/// Find the first part at the given position
|
|
||||||
SymbolPartP findPart(const Vector2D& pos);
|
|
||||||
|
|
||||||
/// Update minV and maxV to be the bounding box of the selected_parts
|
/// Update minV and maxV to be the bounding box of the selected_parts
|
||||||
/// Updates center to be the rotation center of the parts
|
/// Updates center to be the rotation center of the parts
|
||||||
void updateBoundingBox();
|
void updateBoundingBox();
|
||||||
|
|||||||
@@ -108,27 +108,27 @@ void SymbolWindow::init(Window* parent, SymbolP symbol) {
|
|||||||
|
|
||||||
// Edit mode toolbar
|
// Edit mode toolbar
|
||||||
wxPanel* emp = new wxPanel(this, wxID_ANY);
|
wxPanel* emp = new wxPanel(this, wxID_ANY);
|
||||||
wxToolBar* em = new wxToolBar(emp, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_FLAT | wxTB_VERTICAL | wxTB_TEXT | wxTB_HORZ_LAYOUT);
|
wxToolBar* em = new wxToolBar(emp, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_FLAT | wxTB_VERTICAL | wxTB_HORZ_TEXT);
|
||||||
|
em->SetToolBitmapSize(wxSize(17,17));
|
||||||
em->AddTool(ID_MODE_SELECT, _TOOL_("select"), load_resource_tool_image(_("mode_select")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("select"), _HELP_("select"));
|
em->AddTool(ID_MODE_SELECT, _TOOL_("select"), load_resource_tool_image(_("mode_select")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("select"), _HELP_("select"));
|
||||||
em->AddTool(ID_MODE_ROTATE, _TOOL_("rotate"), load_resource_tool_image(_("mode_rotate")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("rotate"), _HELP_("rotate"));
|
em->AddTool(ID_MODE_ROTATE, _TOOL_("rotate"), load_resource_tool_image(_("mode_rotate")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("rotate"), _HELP_("rotate"));
|
||||||
em->AddSeparator();
|
em->AddSeparator();
|
||||||
em->AddTool(ID_MODE_POINTS, _TOOL_("points"), load_resource_tool_image(_("mode_curve")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("points"), _HELP_("points"));
|
em->AddTool(ID_MODE_POINTS, _TOOL_("points"), load_resource_tool_image(_("mode_curve")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("points"), _HELP_("points"));
|
||||||
em->AddSeparator();
|
em->AddSeparator();
|
||||||
em->AddTool(ID_MODE_SHAPES, _TOOL_("basic shapes"), load_resource_tool_image(_("circle")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("basic shapes"), _HELP_("basic shapes"));
|
em->AddTool(ID_MODE_SHAPES, _TOOL_("basic shapes"), load_resource_tool_image(_("circle")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("basic shapes"), _HELP_("basic shapes"));
|
||||||
em->AddSeparator();
|
em->AddTool(ID_MODE_SYMMETRY, _TOOL_("symmetry"), load_resource_tool_image(_("mode_symmetry")),wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("symmetry"), _HELP_("symmetry"));
|
||||||
em->AddTool(ID_MODE_PAINT, _TOOL_("paint"), load_resource_tool_image(_("mode_paint")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("paint"), _HELP_("paint"));
|
//em->AddTool(ID_MODE_PAINT, _TOOL_("paint"), load_resource_tool_image(_("mode_paint")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("paint"), _HELP_("paint"));
|
||||||
em->AddSeparator();
|
|
||||||
em->Realize();
|
em->Realize();
|
||||||
|
|
||||||
|
// Lay out
|
||||||
|
wxSizer* es = new wxBoxSizer(wxVERTICAL);
|
||||||
|
es->Add(em, 1, wxEXPAND | wxBOTTOM | wxALIGN_CENTER, 5);
|
||||||
|
emp->SetSizer(es);
|
||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
control = new SymbolControl (this, ID_CONTROL, symbol);
|
control = new SymbolControl (this, ID_CONTROL, symbol);
|
||||||
parts = new SymbolPartList(this, ID_PART_LIST, control->selected_parts, symbol);
|
parts = new SymbolPartList(this, ID_PART_LIST, control->selected_parts, symbol);
|
||||||
|
|
||||||
// Lay out
|
|
||||||
wxSizer* es = new wxBoxSizer(wxHORIZONTAL);
|
|
||||||
es->Add(em, 1, wxEXPAND | wxTOP | wxBOTTOM | wxALIGN_CENTER, 1);
|
|
||||||
emp->SetSizer(es);
|
|
||||||
|
|
||||||
wxSizer* s = new wxBoxSizer(wxHORIZONTAL);
|
wxSizer* s = new wxBoxSizer(wxHORIZONTAL);
|
||||||
wxSizer* v = new wxBoxSizer(wxVERTICAL);
|
wxSizer* v = new wxBoxSizer(wxVERTICAL);
|
||||||
v->Add(emp, 0, wxEXPAND);
|
v->Add(emp, 0, wxEXPAND);
|
||||||
@@ -137,6 +137,52 @@ void SymbolWindow::init(Window* parent, SymbolP symbol) {
|
|||||||
s->Add(control, 1, wxEXPAND);
|
s->Add(control, 1, wxEXPAND);
|
||||||
SetSizer(s);
|
SetSizer(s);
|
||||||
|
|
||||||
|
#ifdef __WXMSW__
|
||||||
|
// only tested on msw, may not even be needed on other platforms
|
||||||
|
#define USE_HORRIBLE_HORRIBLE_HACK_TO_MAKE_TOOLBAR_THE_RIGHT_SIZE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_HORRIBLE_HORRIBLE_HACK_TO_MAKE_TOOLBAR_THE_RIGHT_SIZE
|
||||||
|
// Welcome to HackWorld
|
||||||
|
|
||||||
|
// Prevent clipping of the bottom tool
|
||||||
|
Layout();
|
||||||
|
em->SetSize(emp->GetSize());
|
||||||
|
|
||||||
|
// HACK: force edit mode toolbar to be wide enough by adding spaces to tool names
|
||||||
|
int n = 0;
|
||||||
|
while (em->GetSize().x + 5 < emp->GetSize().x) {
|
||||||
|
++n;
|
||||||
|
for (int id = ID_MODE_SELECT ; id <= ID_MODE_SYMMETRY ; ++id) {
|
||||||
|
wxToolBarToolBase* tool = em->FindById(id);
|
||||||
|
tool->SetLabel(tool->GetLabel() + _(" "));
|
||||||
|
}
|
||||||
|
em->Realize();
|
||||||
|
}
|
||||||
|
// And now rebuild it, because the above meddling broke the toolbar for some unknown reason
|
||||||
|
em->Destroy();
|
||||||
|
em = new wxToolBar(emp, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_FLAT | wxTB_VERTICAL | wxTB_HORZ_TEXT);
|
||||||
|
em->SetToolBitmapSize(wxSize(17,17));
|
||||||
|
String spaces(max(0,n-1), _(' '));
|
||||||
|
em->AddTool(ID_MODE_SELECT, _TOOL_("select") + spaces, load_resource_tool_image(_("mode_select")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("select"), _HELP_("select"));
|
||||||
|
em->AddTool(ID_MODE_ROTATE, _TOOL_("rotate") + spaces, load_resource_tool_image(_("mode_rotate")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("rotate"), _HELP_("rotate"));
|
||||||
|
em->AddSeparator();
|
||||||
|
em->AddTool(ID_MODE_POINTS, _TOOL_("points") + spaces, load_resource_tool_image(_("mode_curve")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("points"), _HELP_("points"));
|
||||||
|
em->AddSeparator();
|
||||||
|
em->AddTool(ID_MODE_SHAPES, _TOOL_("basic shapes") + spaces, load_resource_tool_image(_("circle")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("basic shapes"),_HELP_("basic shapes"));
|
||||||
|
em->AddTool(ID_MODE_SYMMETRY, _TOOL_("symmetry") + spaces, load_resource_tool_image(_("mode_symmetry")),wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("symmetry"), _HELP_("symmetry"));
|
||||||
|
//em->AddTool(ID_MODE_PAINT, _TOOL_("paint") + spaces, load_resource_tool_image(_("mode_paint")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("paint"), _HELP_("paint"));
|
||||||
|
em->Realize();
|
||||||
|
|
||||||
|
es = new wxBoxSizer(wxVERTICAL);
|
||||||
|
es->Add(em, 1, wxEXPAND | wxBOTTOM | wxALIGN_CENTER, 5);
|
||||||
|
emp->SetSizer(es);
|
||||||
|
|
||||||
|
// Prevent clipping of the bottom tool
|
||||||
|
Layout();
|
||||||
|
em->SetSize(emp->GetSize());
|
||||||
|
#endif
|
||||||
|
|
||||||
// we want update ui events
|
// we want update ui events
|
||||||
wxUpdateUIEvent::SetMode(wxUPDATE_UI_PROCESS_SPECIFIED);
|
wxUpdateUIEvent::SetMode(wxUPDATE_UI_PROCESS_SPECIFIED);
|
||||||
SetExtraStyle(wxWS_EX_PROCESS_UI_UPDATES);
|
SetExtraStyle(wxWS_EX_PROCESS_UI_UPDATES);
|
||||||
|
|||||||
@@ -877,6 +877,18 @@
|
|||||||
<File
|
<File
|
||||||
RelativePath=".\gui\symbol\select_editor.hpp">
|
RelativePath=".\gui\symbol\select_editor.hpp">
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\selection.cpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\selection.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\symmetry_editor.cpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\symmetry_editor.hpp">
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\gui\symbol\window.cpp">
|
RelativePath=".\gui\symbol\window.cpp">
|
||||||
<FileConfiguration
|
<FileConfiguration
|
||||||
|
|||||||
@@ -164,7 +164,25 @@ void SymbolViewer::highlightPart(DC& dc, const SymbolShape& shape, HighlightStyl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void SymbolViewer::highlightPart(DC& dc, const SymbolSymmetry& sym) {
|
void SymbolViewer::highlightPart(DC& dc, const SymbolSymmetry& sym) {
|
||||||
// TODO
|
// center
|
||||||
|
RealPoint center = rotation.tr(sym.center);
|
||||||
|
// draw 'spokes'
|
||||||
|
double angle = atan2(sym.handle.y, sym.handle.x);
|
||||||
|
dc.SetPen(wxPen(Color(255,200,0),3));
|
||||||
|
for (int i = 0; i < sym.copies ; ++i) {
|
||||||
|
double a = angle + (i + 0.5) * 2 * M_PI / sym.copies;
|
||||||
|
Vector2D dir(cos(a), sin(a));
|
||||||
|
Vector2D dir2 = rotation.tr(sym.center + 2 * dir);
|
||||||
|
dc.DrawLine(center.x, center.y, dir2.x, dir2.y);
|
||||||
|
}
|
||||||
|
// draw center
|
||||||
|
dc.SetPen(*wxBLACK_PEN);
|
||||||
|
dc.SetBrush(Color(255,200,0));
|
||||||
|
dc.DrawCircle(center.x, center.y, 5);
|
||||||
|
// draw handle
|
||||||
|
Vector2D dir2 = rotation.tr(sym.center + sym.handle);
|
||||||
|
dc.SetPen(wxPen(Color(255,200,0),1,wxDOT));
|
||||||
|
dc.DrawLine(center.x, center.y, dir2.x, dir2.y);
|
||||||
}
|
}
|
||||||
void SymbolViewer::highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style) {
|
void SymbolViewer::highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style) {
|
||||||
if (style == HIGHLIGHT_BORDER) {
|
if (style == HIGHLIGHT_BORDER) {
|
||||||
|
|||||||
@@ -352,6 +352,7 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP&
|
|||||||
break;
|
break;
|
||||||
case I_AND: OPERATOR_I(&&);
|
case I_AND: OPERATOR_I(&&);
|
||||||
case I_OR: OPERATOR_I(||);
|
case I_OR: OPERATOR_I(||);
|
||||||
|
case I_XOR: a = to_script((bool)*a != (bool)*b); break;
|
||||||
case I_EQ: OPERATOR_SDI(==);
|
case I_EQ: OPERATOR_SDI(==);
|
||||||
case I_NEQ: OPERATOR_SDI(!=);
|
case I_NEQ: OPERATOR_SDI(!=);
|
||||||
case I_LT: OPERATOR_DI(<);
|
case I_LT: OPERATOR_DI(<);
|
||||||
|
|||||||
@@ -582,6 +582,7 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
|
|||||||
parseOper(input, script, PREC_CMP, I_BINARY, I_OR);
|
parseOper(input, script, PREC_CMP, I_BINARY, I_OR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (minPrec <= PREC_AND && token==_("xor")) parseOper(input, script, PREC_CMP, I_BINARY, I_XOR);
|
||||||
else if (minPrec <= PREC_CMP && token==_("=")) {
|
else if (minPrec <= PREC_CMP && token==_("=")) {
|
||||||
if (minPrec <= PREC_SET) {
|
if (minPrec <= PREC_SET) {
|
||||||
input.add_error(_("Use of '=', did you mean ':=' or '=='?"));
|
input.add_error(_("Use of '=', did you mean ':=' or '=='?"));
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ String Script::dumpInstr(unsigned int pos, Instruction i) const {
|
|||||||
case I_MOD: ret += _("mod"); break;
|
case I_MOD: ret += _("mod"); break;
|
||||||
case I_AND: ret += _("and"); break;
|
case I_AND: ret += _("and"); break;
|
||||||
case I_OR: ret += _("or"); break;
|
case I_OR: ret += _("or"); break;
|
||||||
|
case I_XOR: ret += _("xor"); break;
|
||||||
case I_EQ: ret += _("=="); break;
|
case I_EQ: ret += _("=="); break;
|
||||||
case I_NEQ: ret += _("!="); break;
|
case I_NEQ: ret += _("!="); break;
|
||||||
case I_LT: ret += _("<"); break;
|
case I_LT: ret += _("<"); break;
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ enum BinaryInstructionType
|
|||||||
// Logical
|
// Logical
|
||||||
, I_AND ///< logical and
|
, I_AND ///< logical and
|
||||||
, I_OR ///< logical or
|
, I_OR ///< logical or
|
||||||
|
, I_XOR ///< logical xor
|
||||||
// Comparison
|
// Comparison
|
||||||
, I_EQ ///< operator ==
|
, I_EQ ///< operator ==
|
||||||
, I_NEQ ///< operator !=
|
, I_NEQ ///< operator !=
|
||||||
|
|||||||
@@ -152,6 +152,13 @@ enum ChildMenuID {
|
|||||||
, ID_SHAPE_MAX
|
, ID_SHAPE_MAX
|
||||||
, ID_SIDES
|
, ID_SIDES
|
||||||
|
|
||||||
|
// SymbolSymmetryEditor toolbar/menu
|
||||||
|
, ID_SYMMETRY = 2201
|
||||||
|
, ID_SYMMETRY_ROTATION = ID_SHAPE
|
||||||
|
, ID_SYMMETRY_REFLECTION
|
||||||
|
, ID_SYMMETRY_MAX
|
||||||
|
, ID_COPIES
|
||||||
|
|
||||||
// On cards panel
|
// On cards panel
|
||||||
, ID_COLLAPSE_NOTES = 3001
|
, ID_COLLAPSE_NOTES = 3001
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user