From b40918801337e5f135ce709061ba594d558d3cdb Mon Sep 17 00:00:00 2001 From: twanvl Date: Mon, 9 Jul 2007 20:43:16 +0000 Subject: [PATCH] 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 --- src/data/action/symbol.cpp | 74 +++++-- src/data/action/symbol.hpp | 30 ++- src/data/symbol.cpp | 33 ++- src/data/symbol.hpp | 52 +++-- src/gui/symbol/basic_shape_editor.cpp | 4 +- src/gui/symbol/control.cpp | 43 ++-- src/gui/symbol/control.hpp | 7 +- src/gui/symbol/part_list.cpp | 283 ++++++++++++++++---------- src/gui/symbol/part_list.hpp | 30 ++- src/gui/symbol/select_editor.cpp | 137 ++++++------- src/gui/symbol/select_editor.hpp | 14 +- src/gui/symbol/window.cpp | 64 +++++- src/mse.vcproj | 12 ++ src/render/symbol/viewer.cpp | 20 +- src/script/context.cpp | 1 + src/script/parser.cpp | 1 + src/script/script.cpp | 1 + src/script/script.hpp | 1 + src/util/window_id.hpp | 7 + 19 files changed, 523 insertions(+), 291 deletions(-) diff --git a/src/data/action/symbol.cpp b/src/data/action/symbol.cpp index ed4dfd55..b2111023 100644 --- a/src/data/action/symbol.cpp +++ b/src/data/action/symbol.cpp @@ -105,6 +105,7 @@ void SymbolPartMatrixAction::transform(SymbolPart& part, const Matrix2D& m) { // bounds change after transforming s->calculateBounds(); } else if (SymbolSymmetry* s = part.isSymbolSymmetry()) { + s->center = (s->center - center) * m + center; s->handle = s->handle * m; } else if (SymbolGroup* g = part.isSymbolGroup()) { FOR_EACH(p, g->parts) { @@ -280,7 +281,8 @@ void SymbolPartScaleAction::transformPart(SymbolPart& part) { pnt->delta_after = pnt->delta_after .mul(scale); } } 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()) { FOR_EACH(p, g->parts) { transformPart(*p); @@ -450,8 +452,9 @@ void DuplicateSymbolPartsAction::getParts(set& parts) { // ----------------------------------------------------------------------------- : Reorder symbol parts -ReorderSymbolPartsAction::ReorderSymbolPartsAction(Symbol& symbol, size_t old_position, size_t new_position) - : symbol(symbol), old_position(old_position), new_position(new_position) +ReorderSymbolPartsAction::ReorderSymbolPartsAction(SymbolGroup& old_parent, size_t old_position, SymbolGroup& new_parent, size_t 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 { @@ -459,31 +462,58 @@ String ReorderSymbolPartsAction::getName(bool to_undo) const { } void ReorderSymbolPartsAction::perform(bool to_undo) { - assert(old_position < symbol.parts.size()); - assert(new_position < symbol.parts.size()); - SymbolPartP part = symbol.parts.at(old_position); - symbol.parts.erase( symbol.parts.begin() + old_position); - symbol.parts.insert(symbol.parts.begin() + new_position, part); + // remove from old + assert(old_position < old_parent->parts.size()); + SymbolPartP part = old_parent->parts.at(old_position); + old_parent->parts.erase( old_parent->parts.begin() + old_position); + // 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); } -// ----------------------------------------------------------------------------- : Group symbol parts -GroupSymbolPartsActionBase::GroupSymbolPartsActionBase(Symbol& symbol) - : symbol(symbol) -{} -void GroupSymbolPartsActionBase::perform(bool to_undo) { - swap(symbol.parts, old_part_list); +UngroupReorderSymbolPartsAction::UngroupReorderSymbolPartsAction(SymbolGroup& group_parent, size_t group_pos, SymbolGroup& target_parent, size_t target_pos) + : group_parent(group_parent), group_pos(group_pos) + , target_parent(target_parent), target_pos(target_pos) +{ + group = dynamic_pointer_cast(group_parent.parts.at(group_pos)); + assert(group); } -GroupSymbolPartsAction::GroupSymbolPartsAction(Symbol& symbol, const set& parts) - : GroupSymbolPartsActionBase(symbol) +String UngroupReorderSymbolPartsAction::getName(bool to_undo) const { + 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& parts, const SymbolGroupP& group) + : GroupSymbolPartsActionBase(root) { // group parts in the old parts list bool done = false; - SymbolGroupP group(new SymbolGroup); - group->name = _("Group"); - FOR_EACH(p, symbol.parts) { + FOR_EACH(p, root.parts) { + assert(p != group); if (parts.find(p) != parts.end()) { // add to group instead group->parts.push_back(p); @@ -502,11 +532,11 @@ String GroupSymbolPartsAction::getName(bool to_undo) const { return _ACTION_("group parts"); } -UngroupSymbolPartsAction::UngroupSymbolPartsAction(Symbol& symbol, const set& parts) - : GroupSymbolPartsActionBase(symbol) +UngroupSymbolPartsAction::UngroupSymbolPartsAction(SymbolGroup& root, const set& parts) + : GroupSymbolPartsActionBase(root) { // 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()) { // break up the group SymbolGroup* g = p->isSymbolGroup(); diff --git a/src/data/action/symbol.hpp b/src/data/action/symbol.hpp index 71ad4fc9..647c3555 100644 --- a/src/data/action/symbol.hpp +++ b/src/data/action/symbol.hpp @@ -240,36 +240,52 @@ class DuplicateSymbolPartsAction : public SymbolPartListAction { /// Change the position of a part in a symbol, by moving a part. class ReorderSymbolPartsAction : public SymbolPartListAction { 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 void perform(bool to_undo); private: - Symbol& symbol; ///< Symbol to swap the parts in + SymbolGroup* old_parent, *new_parent;///< Parents to move from and to public: 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 multiple symbol parts together class GroupSymbolPartsActionBase : public SymbolPartListAction { public: - GroupSymbolPartsActionBase(Symbol& symbol); + GroupSymbolPartsActionBase(SymbolGroup& root); virtual void perform(bool to_undo); protected: - Symbol& symbol; ///< Symbol to group stuff in + SymbolGroup& root; ///< Symbol or group to group stuff in vector old_part_list; ///< Old part list of the symbol }; /// Group multiple symbol parts together class GroupSymbolPartsAction : public GroupSymbolPartsActionBase { public: - GroupSymbolPartsAction(Symbol& symbol, const set& parts); + GroupSymbolPartsAction(SymbolGroup& root, const set& parts, const SymbolGroupP& group); virtual String getName(bool to_undo) const; }; @@ -277,11 +293,11 @@ class GroupSymbolPartsAction : public GroupSymbolPartsActionBase { /// Break up one or more SymbolGroups class UngroupSymbolPartsAction : public GroupSymbolPartsActionBase { public: - UngroupSymbolPartsAction(Symbol& symbol, const set& groups); + /// Remove all the given groups + UngroupSymbolPartsAction(SymbolGroup& root, const set& groups); virtual String getName(bool to_undo) const; }; - // ----------------------------------------------------------------------------- : EOF #endif diff --git a/src/data/symbol.cpp b/src/data/symbol.cpp index 2ef1cbfe..d6b223e3 100644 --- a/src/data/symbol.cpp +++ b/src/data/symbol.cpp @@ -204,7 +204,12 @@ String SymbolSymmetry::typeName() const { } SymbolPartP SymbolSymmetry::clone() const { - return new_intrusive1(*this); + SymbolSymmetryP part(new SymbolSymmetry(*this)); + // also clone the parts inside + FOR_EACH(p, part->parts) { + p = p->clone(); + } + return part; } IMPLEMENT_REFLECTION(SymbolSymmetry) { @@ -213,20 +218,16 @@ IMPLEMENT_REFLECTION(SymbolSymmetry) { REFLECT(copies); REFLECT(center); REFLECT(handle); - // Fixes after reading - REFLECT_IF_READING { - if (name.empty()) { - if (kind == SYMMETRY_REFLECTION) { - name = _("Mirror"); - } else { - name = _("Symmetry"); - } - } - } + REFLECT(parts); + REFLECT_IF_READING calculateBoundsNonRec(); } // ----------------------------------------------------------------------------- : SymbolGroup +SymbolGroup::SymbolGroup() { + name = capitalize(_TYPE_("group")); +} + String SymbolGroup::typeName() const { return _("group"); } @@ -240,6 +241,14 @@ SymbolPartP SymbolGroup::clone() const { 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() { FOR_EACH(p, parts) p->calculateBounds(); calculateBoundsNonRec(); @@ -256,12 +265,14 @@ void SymbolGroup::calculateBoundsNonRec() { IMPLEMENT_REFLECTION(SymbolGroup) { REFLECT_BASE(SymbolPart); REFLECT(parts); + REFLECT_IF_READING calculateBoundsNonRec(); } // ----------------------------------------------------------------------------- : Symbol IMPLEMENT_REFLECTION(Symbol) { REFLECT(parts); + REFLECT_IF_READING calculateBoundsNonRec(); } // ----------------------------------------------------------------------------- : Default symbol diff --git a/src/data/symbol.hpp b/src/data/symbol.hpp index 0375c3a8..5de54202 100644 --- a/src/data/symbol.hpp +++ b/src/data/symbol.hpp @@ -133,6 +133,10 @@ class SymbolPart : public IntrusivePtrVirtualBase { virtual SymbolGroup* isSymbolGroup() { 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) virtual void calculateBounds(); @@ -191,6 +195,30 @@ class SymbolShape : public SymbolPart { DECLARE_REFLECTION(); }; +// ----------------------------------------------------------------------------- : SymbolGroup + +/// A group of symbol parts +class SymbolGroup : public SymbolPart { + public: + vector 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 enum SymbolSymmetryType @@ -198,9 +226,9 @@ enum SymbolSymmetryType , 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 */ -class SymbolSymmetry : public SymbolPart { +class SymbolSymmetry : public SymbolGroup { public: SymbolSymmetryType kind; ///< What kind of symmetry int copies; ///< How many times is the orignal reflected (including the original itself) @@ -219,26 +247,6 @@ class SymbolSymmetry : public SymbolPart { DECLARE_REFLECTION(); }; -// ----------------------------------------------------------------------------- : SymbolGroup - -/// A group of symbol parts -class SymbolGroup : public SymbolPart { - public: - vector 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 /// An editable symbol, consists of any number of SymbolParts diff --git a/src/gui/symbol/basic_shape_editor.cpp b/src/gui/symbol/basic_shape_editor.cpp index 602d24da..747d0672 100644 --- a/src/gui/symbol/basic_shape_editor.cpp +++ b/src/gui/symbol/basic_shape_editor.cpp @@ -28,7 +28,7 @@ SymbolBasicShapeEditor::SymbolBasicShapeEditor(SymbolControl* control) void SymbolBasicShapeEditor::draw(DC& dc) { // highlight the part we are drawing - if (drawing) { + if (shape) { control.highlightPart(dc, *shape, HIGHLIGHT_BORDER); } } @@ -64,6 +64,7 @@ void SymbolBasicShapeEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) { delete sides; delete sidesL; #endif + stopActions(); // set status text } void SymbolBasicShapeEditor::onUpdateUI(wxUpdateUIEvent& ev) { @@ -80,6 +81,7 @@ void SymbolBasicShapeEditor::onCommand(int id) { if (id >= ID_SHAPE && id < ID_SHAPE_MAX) { // change shape mode mode = id; + stopActions(); } } diff --git a/src/gui/symbol/control.cpp b/src/gui/symbol/control.cpp index 39a57c68..cb96e082 100644 --- a/src/gui/symbol/control.cpp +++ b/src/gui/symbol/control.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,7 @@ void SymbolControl::switchEditor(const SymbolEditorBaseP& e) { } void SymbolControl::onChangeSymbol() { - selected_parts.clear(); + selected_parts.setSymbol(symbol); switchEditor(new_intrusive2(this, false)); Refresh(false); } @@ -51,7 +52,7 @@ void SymbolControl::onModeChange(wxCommandEvent& ev) { break; case ID_MODE_POINTS: if (selected_parts.size() == 1) { - selected_shape = dynamic_pointer_cast(*selected_parts.begin()); + selected_shape = selected_parts.getAShape(); if (selected_shape) { switchEditor(new_intrusive2(this, selected_shape)); } @@ -64,6 +65,9 @@ void SymbolControl::onModeChange(wxCommandEvent& ev) { } switchEditor(new_intrusive1(this)); break; + case ID_MODE_SYMMETRY: + switchEditor(new_intrusive1(this)); + break; } } @@ -92,25 +96,17 @@ void SymbolControl::onUpdateSelection() { switch(editor->modeToolId()) { case ID_MODE_POINTS: { // can only select a single part! - if (selected_parts.size() > 1) { - // 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(*selected_parts.begin()); + SymbolShapeP shape = selected_parts.getAShape(); if (!shape) { - selected_parts.clear(); - selected_parts.insert(selected_shape); - signalSelectionChange(); + if (selected_parts.select(selected_shape)) { + signalSelectionChange(); + } break; } if (shape != selected_shape) { + if (selected_parts.select(shape)) { + signalSelectionChange(); + } // begin editing another part selected_shape = shape; editor = new_intrusive2(this, selected_shape); @@ -131,16 +127,14 @@ void SymbolControl::onUpdateSelection() { } void SymbolControl::selectPart(const SymbolPartP& part) { - selected_parts.clear(); - selected_parts.insert(part); + selected_parts.select(part); switchEditor(new_intrusive2(this, false)); signalSelectionChange(); } void SymbolControl::activatePart(const SymbolPartP& part) { if (part->isSymbolShape()) { - selected_parts.clear(); - selected_parts.insert(part); + selected_parts.select(part); switchEditor(new_intrusive2(this, static_pointer_cast(part))); } } @@ -247,11 +241,12 @@ void SymbolControl::onSize(wxSizeEvent& ev) { void SymbolControl::onUpdateUI(wxUpdateUIEvent& ev) { if (!editor) return; 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()); if (ev.GetId() == ID_MODE_POINTS) { - // can only edit points when a single part is selected - ev.Enable(selected_parts.size() == 1 && (*selected_parts.begin())->isSymbolShape()); + // can only edit points when a shape is available + ev.Enable(selected_parts.getAShape()); } break; case ID_MODE_PAINT: diff --git a/src/gui/symbol/control.hpp b/src/gui/symbol/control.hpp index bd43ffdb..a64116fb 100644 --- a/src/gui/symbol/control.hpp +++ b/src/gui/symbol/control.hpp @@ -11,6 +11,7 @@ #include #include +#include #include class SymbolWindow; @@ -71,9 +72,9 @@ class SymbolControl : public wxControl, public SymbolViewer { public: /// What parts are selected? - set selected_parts; - SymbolPartP highlight_part; ///< part the mouse cursor is over - SymbolShapeP selected_shape; ///< if there is a single selection + SymbolPartsSelection selected_parts; + SymbolPartP highlight_part; ///< part the mouse cursor is over + SymbolShapeP selected_shape; ///< if there is a single selection /// Parent window SymbolWindow* parent; diff --git a/src/gui/symbol/part_list.cpp b/src/gui/symbol/part_list.cpp index 89115fbf..c5ae0d80 100644 --- a/src/gui/symbol/part_list.cpp +++ b/src/gui/symbol/part_list.cpp @@ -7,10 +7,12 @@ // ----------------------------------------------------------------------------- : Includes #include +#include #include #include #include #include +#include #include #include @@ -24,11 +26,12 @@ DEFINE_EVENT_TYPE(EVENT_PART_ACTIVATE); // ----------------------------------------------------------------------------- : SymbolPartList -SymbolPartList::SymbolPartList(Window* parent, int id, set& selection, SymbolP symbol) +SymbolPartList::SymbolPartList(Window* parent, int id, SymbolPartsSelection& selection, SymbolP symbol) : wxScrolledWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxVSCROLL) , selection(selection) , state_icons(9,8) { + SetScrollRate(0, ITEM_HEIGHT+1); // 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_subtract"))); @@ -69,7 +72,7 @@ void SymbolPartList::onAction(const Action& action, bool undone) { TYPE_CASE_(action, SymbolPartAction) { symbol_preview.up_to_date = false; // some part changed, but we don't know which one, assume it is the selection - updateParts(selection); + updateParts(selection.get()); return; } } @@ -84,7 +87,7 @@ wxSize SymbolPartList::DoGetBestSize() const { void SymbolPartList::update() { // count items number_of_items = childCount(symbol); - SetVirtualSize(110, number_of_items * (ITEM_HEIGHT+1)); + SetVirtualSize(110, number_of_items * (ITEM_HEIGHT + 1)); // invalidate previews symbol_preview.up_to_date = false; for (size_t i = 0 ; i < part_previews.size() ; ++i) { @@ -118,23 +121,19 @@ void SymbolPartList::updatePart(const set& parts, int& i, bool pare // ----------------------------------------------------------------------------- : Events void SymbolPartList::onLeftDown(wxMouseEvent& ev) { + int top; GetViewStart(0, &top); // find item under cursor 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) { // toggle/select - if (!ev.ShiftDown()) { - selection.clear(); - } - if (selection.find(part) != selection.end()) { - selection.erase(part); - } else { - selection.insert(part); - } - if (!ev.ShiftDown()) { + selection.select(part, ev.ShiftDown() ? SELECT_TOGGLE : SELECT_OVERRIDE); + if (!ev.ShiftDown() && selection.selected(part)) { // drag item - mouse_down_on = part; - drop_position = -1; + drag = part; + findParent(*part, drag_parent, drag_position); + drop_parent = SymbolGroupP(); CaptureMouse(); } sendEvent(EVENT_PART_SELECT); @@ -144,43 +143,65 @@ void SymbolPartList::onLeftDown(wxMouseEvent& ev) { } void SymbolPartList::onLeftUp(wxMouseEvent& ev) { if (HasCapture()) ReleaseMouse(); - if (mouse_down_on && drop_position != -1) { + if (drag_parent && drop_parent) { // move part - // find old position - vector::const_iterator it = find(symbol->parts.begin(), symbol->parts.end(), mouse_down_on); - 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; + if (drag_parent == drop_parent && drag_position < drop_position) { + drop_position -= 1; // adjust for removal of the dragged part } // move part - if (old_position != new_position) { - symbol->actions.add(new ReorderSymbolPartsAction(*symbol, old_position, new_position)); + SymbolGroupP par = drag_parent; drag_parent = SymbolGroupP(); + 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 { Refresh(false); } } else { - mouse_down_on = SymbolPartP(); + drag_parent = SymbolGroupP(); } } void SymbolPartList::onMotion(wxMouseEvent& ev) { - if (mouse_down_on) { - int new_drop_position = (ev.GetY() + ITEM_HEIGHT/2) / (ITEM_HEIGHT + 1); - if (new_drop_position < 0 || new_drop_position > number_of_items) new_drop_position = -1; - // TODO: make sure it is not in a group - if (drop_position != new_drop_position) { - drop_position = new_drop_position; + int top; GetViewStart(0, &top); + if (drag_parent) { + int pos = top + (ev.GetY() + ITEM_HEIGHT/2) / (ITEM_HEIGHT + 1); + if (pos < 0) pos = 0; + if (pos >= number_of_items) pos = number_of_items; + 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); } } @@ -262,26 +283,76 @@ void SymbolPartList::sendEvent(int type) { // ----------------------------------------------------------------------------- : Items -SymbolPartP SymbolPartList::findItem(int i) const { +SymbolPartP SymbolPartList::findItem(int i, int x) const { FOR_EACH(p, symbol->parts) { - SymbolPartP f = findItem(i, p); + SymbolPartP f = findItem(i, x, p); if (f) return f; } 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 part; i -= 1; // sub item? if (SymbolGroup* g = part->isSymbolGroup()) { 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(); } +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(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(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(parent); + drop_position = g->parts.size(); + return true; + } + } + return false; +} + + int SymbolPartList::childCount(const SymbolPartP& part) { if (SymbolGroup* g = part->isSymbolGroup()) { int count = 0; @@ -292,9 +363,6 @@ int SymbolPartList::childCount(const SymbolPartP& part) { } } -// ----------------------------------------------------------------------------- : Text editor - - // ----------------------------------------------------------------------------- : Drawing @@ -307,23 +375,13 @@ void SymbolPartList::OnDraw(DC& dc) { // init dc.SetFont(*wxNORMAL_FONT); // clear background - wxSize size = GetClientSize(); + wxSize size = piecewise_max(GetVirtualSize() + RealSize(0,ITEM_HEIGHT+1), GetClientSize()); dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); dc.DrawRectangle(0,0,size.x,size.y); // items int i = 0; - FOR_EACH(p, symbol->parts) { - 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); - } + drawItem(dc, 0, i, false, symbol); // hide caret if (selection.size() != 1) { typing_in = SymbolPartP(); @@ -339,7 +397,7 @@ void SymbolPartList::drawItem(DC& dc, int x, int& i, bool parent_active, const S // draw item : highlight Color background; dc.SetPen(*wxTRANSPARENT_PEN); - bool active = selection.find(part) != selection.end(); + bool active = selection.selected(part); if (active) { background = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); 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); 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), - wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.5); - dc.SetPen(line_pen); - dc.DrawLine(x+ITEM_HEIGHT, y, x+ITEM_HEIGHT, y + ITEM_HEIGHT + 1); // line after image - dc.DrawLine(x, y + ITEM_HEIGHT, size.x, y + ITEM_HEIGHT); // line below - // update caret - if (selection.size() == 1 && active) { - updateCaret(dc, x + ITEM_HEIGHT + 3, y + (ITEM_HEIGHT - h) / 2, h, part); - } - // move down - i += 1; - // draw more? - if (SymbolShape* s = part->isSymbolShape()) { - // combine state - state_icons.Draw(s->combine, dc, size.x - 10, y + 1); - } else if (SymbolSymmetry* s = part->isSymbolSymmetry()) { - // kind of symmetry - state_icons.Draw(s->kind, dc, size.x - 10, y + 1); - // TODO: show clip mode? - } else if (SymbolGroup* g = part->isSymbolGroup()) { - state_icons.Draw(SYMMETRY_REFLECTION + 1, dc, size.x - 10, y + 1); - FOR_EACH(p, g->parts) drawItem(dc, x + 5, i, active || parent_active, p); - // draw bar on the left - int new_y = i * (ITEM_HEIGHT + 1); - y += ITEM_HEIGHT+1; - if (y != new_y) { - dc.SetPen(line_pen); - dc.SetBrush(background); - dc.DrawRectangle(x-1,y-1,5+1,new_y-y+1); + wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.5); + if (part != symbol) { + // 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 + dc.SetPen(line_pen); + dc.DrawLine(x+ITEM_HEIGHT, y, x+ITEM_HEIGHT, y + ITEM_HEIGHT + 1); // line after image + dc.DrawLine(x, y + ITEM_HEIGHT, size.x, y + ITEM_HEIGHT); // line below + // update caret + if (selection.size() == 1 && active) { + updateCaret(dc, x + ITEM_HEIGHT + 3, y + (ITEM_HEIGHT - h) / 2, h, part); + } + // draw icon + state_icons.Draw(part->icon(), dc, size.x - 10, y + 1); + // move down + i += 1; + } + // Draw children + int child_x = part == symbol ? x : x + 5; + if (SymbolGroup* g = part->isSymbolGroup()) { + FOR_EACH(p, g->parts) drawItem(dc, child_x, i, active || parent_active, p); + } + // 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) { + int top; GetViewStart(0, &top); // make caret wxCaret* caret = GetCaret(); 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()); int w; 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(); } diff --git a/src/gui/symbol/part_list.hpp b/src/gui/symbol/part_list.hpp index 15dd8144..17ab0981 100644 --- a/src/gui/symbol/part_list.hpp +++ b/src/gui/symbol/part_list.hpp @@ -12,6 +12,8 @@ #include #include +class SymbolPartsSelection; + // ----------------------------------------------------------------------------- : Events DECLARE_EVENT_TYPE(EVENT_PART_SELECT, ) @@ -26,7 +28,7 @@ DECLARE_EVENT_TYPE(EVENT_PART_ACTIVATE, ) class SymbolPartList : public wxScrolledWindow, public SymbolView { public: - SymbolPartList(Window* parent, int id, set& selection, SymbolP symbol = SymbolP()); + SymbolPartList(Window* parent, int id, SymbolPartsSelection& selection, SymbolP symbol = SymbolP()); /// Another symbol is being viewed virtual void onChangeSymbol(); @@ -41,12 +43,14 @@ class SymbolPartList : public wxScrolledWindow, public SymbolView { protected: virtual wxSize DoGetBestSize() const; private: - set& selection; ///< Store selection here - - SymbolPartP mouse_down_on; - int drop_position; + SymbolPartsSelection& selection; ///< Store selection here 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; size_t cursor; @@ -79,8 +83,20 @@ class SymbolPartList : public wxScrolledWindow, public SymbolView { const Image& symbolPreview(); void updatePart(const set& parts, int& i, bool parent_updated, const SymbolPartP& part); - SymbolPartP findItem(int i) const; - static SymbolPartP findItem(int& i, const SymbolPartP& part); + /// find item by position + 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); diff --git a/src/gui/symbol/select_editor.cpp b/src/gui/symbol/select_editor.cpp index 2477f192..73f4c31c 100644 --- a/src/gui/symbol/select_editor.cpp +++ b/src/gui/symbol/select_editor.cpp @@ -21,6 +21,7 @@ DECLARE_TYPEOF_COLLECTION(SymbolPartP); SymbolSelectEditor::SymbolSelectEditor(SymbolControl* control, bool rotate) : SymbolEditorBase(control) + , click_mode(CLICK_NONE) , rotate(rotate) , cursorRotate(load_resource_cursor(_("rotate"))) , cursorShearX(load_resource_cursor(_("shear_x"))) @@ -45,19 +46,27 @@ SymbolSelectEditor::SymbolSelectEditor(SymbolControl* control, bool rotate) void SymbolSelectEditor::draw(DC& dc) { // highlight selected parts - FOR_EACH(p, control.selected_parts) { + FOR_EACH(p, control.selected_parts.get()) { control.highlightPart(dc, *p, HIGHLIGHT_INTERIOR); } // highlight the part under the cursor if (highlightPart) { control.highlightPart(dc, *highlightPart, HIGHLIGHT_BORDER); } - // draw handles - drawHandles(dc); + if (click_mode == CLICK_RECT) { + // 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) { - if (control.selected_parts.empty()) return; + if (control.selected_parts.empty()) return; if (rotateAction) return; // not when rotating updateBoundingBox(); // 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) { bool enable = false; bool check = true; - FOR_EACH(p, control.selected_parts) { + FOR_EACH(p, control.selected_parts.get()) { if (SymbolShape* s = p->isSymbolShape()) { enable = true; if (s->combine != ev.GetId() - ID_SYMBOL_COMBINE) { @@ -147,8 +156,8 @@ void SymbolSelectEditor::onUpdateUI(wxUpdateUIEvent& ev) { ev.Enable(control.selected_parts.size() >= 2); } else if (ev.GetId() == ID_EDIT_UNGROUP) { // is a group selected - FOR_EACH(p, control.selected_parts) { - if (p->isSymbolGroup()) { + FOR_EACH(p, control.selected_parts.get()) { + if (p->isSymbolGroup() && !p->isSymbolSymmetry()) { ev.Enable(true); return; } @@ -163,21 +172,21 @@ void SymbolSelectEditor::onCommand(int id) { if (id >= ID_SYMBOL_COMBINE && id < ID_SYMBOL_COMBINE_MAX) { // change combine mode getSymbol()->actions.add(new CombiningModeAction( - control.selected_parts, + control.selected_parts.get(), static_cast(id - ID_SYMBOL_COMBINE) )); control.Refresh(false); } else if (id == ID_EDIT_DUPLICATE && !isEditing()) { // 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); } else if (id == ID_EDIT_GROUP && !isEditing()) { // 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())); control.Refresh(false); } else if (id == ID_EDIT_UNGROUP && !isEditing()) { // 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); } } @@ -189,35 +198,27 @@ int SymbolSelectEditor::modeToolId() { // ----------------------------------------------------------------------------- : Mouse Events void SymbolSelectEditor::onLeftDown (const Vector2D& pos, wxMouseEvent& ev) { - have_dragged = true; // change selection // Are we on a handle? 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 - SymbolPartP part = findPart(pos); + SymbolPartP part = control.selected_parts.find(pos); if (part) { - if (ev.ShiftDown()) { - // toggle selection - set::iterator it = control.selected_parts.find(part); - if (it != control.selected_parts.end()) { - control.selected_parts.erase(it); - } else { - control.selected_parts.insert(part); - } - } else { - if (control.selected_parts.find(part) != control.selected_parts.end()) { - // 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); - } + click_mode = control.selected_parts.select(part, ev.ShiftDown() ? SELECT_TOGGLE : SELECT_IF_OUTSIDE) + ? (ev.ShiftDown() ? CLICK_NONE : CLICK_MOVE) + : CLICK_TOGGLE; + } else { + // selection rectangle + click_mode = CLICK_RECT; + selection_rect_a = selection_rect_b = pos; + if (!ev.ShiftDown()) { + // select nothing + control.selected_parts.clear(); } - } else if (!ev.ShiftDown()) { - // select nothing - control.selected_parts.clear(); } // selection has changed updateBoundingBox(); @@ -231,24 +232,18 @@ void SymbolSelectEditor::onLeftUp (const Vector2D& pos, wxMouseEvent& ev) { resetActions(); } else { // mouse not moved -> change selection - if (!have_dragged && !ev.ShiftDown()) { - int dx, dy; - if (onAnyHandle(pos, &dx, &dy)) return; // don't change the selection - // 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; - } + if (click_mode == CLICK_TOGGLE) { + // switch between rotate and resize mode + rotate = !rotate; } } + click_mode = CLICK_NONE; control.Refresh(false); } void SymbolSelectEditor::onLeftDClick(const Vector2D& pos, wxMouseEvent& ev) { // start editing the points of the clicked part - highlightPart = findPart(pos); + highlightPart = control.selected_parts.find(pos); if (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) { // can we highlight a part? - highlightPart = findPart(to); + highlightPart = control.selected_parts.find(to); // are we on a handle? int dx, dy; if (!control.selected_parts.empty() && onAnyHandle(to, &dx, &dy)) { @@ -296,31 +291,39 @@ template int snap(Event& 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 (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()) { // 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 if (onAnyHandle(from, &scaleX, &scaleY)) { + click_mode = CLICK_HANDLE; if (rotate) { if (scaleX == 0 || scaleY == 0) { // 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); } else { // rotate - rotateAction = new SymbolPartRotateAction(control.selected_parts, center); + rotateAction = new SymbolPartRotateAction(control.selected_parts.get(), center); getSymbol()->actions.add(rotateAction); startAngle = angleTo(to); } } else { // 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); } } else { // move - moveAction = new SymbolPartMoveAction(control.selected_parts); + click_mode = CLICK_MOVE; + moveAction = new SymbolPartMoveAction(control.selected_parts.get()); getSymbol()->actions.add(moveAction); } } @@ -391,8 +394,8 @@ void SymbolSelectEditor::onKeyChange (wxKeyEvent& ev) { void SymbolSelectEditor::onChar(wxKeyEvent& ev) { if (ev.GetKeyCode() == WXK_DELETE) { // delete selected parts - getSymbol()->actions.add(new RemoveSymbolPartsAction(*getSymbol(), control.selected_parts)); - if (control.selected_parts.find(highlightPart) != control.selected_parts.end()) highlightPart = SymbolPartP(); // deleted it + getSymbol()->actions.add(new RemoveSymbolPartsAction(*getSymbol(), control.selected_parts.get())); + if (control.selected_parts.selected(highlightPart)) highlightPart = SymbolPartP(); // deleted it control.selected_parts.clear(); resetActions(); control.Refresh(false); @@ -408,7 +411,7 @@ void SymbolSelectEditor::onChar(wxKeyEvent& ev) { ev.Skip(); 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); } - -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() { // Find min and max coordinates minV = 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); maxV = piecewise_max(maxV, p->max_pos); } diff --git a/src/gui/symbol/select_editor.hpp b/src/gui/symbol/select_editor.hpp index ac4e23ee..4f835f23 100644 --- a/src/gui/symbol/select_editor.hpp +++ b/src/gui/symbol/select_editor.hpp @@ -74,6 +74,15 @@ class SymbolSelectEditor : public SymbolEditorBase { Vector2D minV, maxV; // Where is the rotation 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 double startAngle; // what side are we dragging/rotating on? @@ -82,6 +91,8 @@ class SymbolSelectEditor : public SymbolEditorBase { bool have_dragged; // Do we want to rotate? bool rotate; + // selection rectangle + Vector2D selection_rect_a, selection_rect_b; // Graphics assets wxCursor cursorRotate; wxCursor cursorShearX; @@ -104,9 +115,6 @@ class SymbolSelectEditor : public SymbolEditorBase { /// Return the position of a handle, dx,dy in <-1, 0, 1> 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 /// Updates center to be the rotation center of the parts void updateBoundingBox(); diff --git a/src/gui/symbol/window.cpp b/src/gui/symbol/window.cpp index 98d76265..7bd3fa05 100644 --- a/src/gui/symbol/window.cpp +++ b/src/gui/symbol/window.cpp @@ -108,27 +108,27 @@ void SymbolWindow::init(Window* parent, SymbolP symbol) { // Edit mode toolbar 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_ROTATE, _TOOL_("rotate"), load_resource_tool_image(_("mode_rotate")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("rotate"), _HELP_("rotate")); em->AddSeparator(); em->AddTool(ID_MODE_POINTS, _TOOL_("points"), load_resource_tool_image(_("mode_curve")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("points"), _HELP_("points")); 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->AddSeparator(); - em->AddTool(ID_MODE_PAINT, _TOOL_("paint"), load_resource_tool_image(_("mode_paint")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("paint"), _HELP_("paint")); - 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->Realize(); + // Lay out + wxSizer* es = new wxBoxSizer(wxVERTICAL); + es->Add(em, 1, wxEXPAND | wxBOTTOM | wxALIGN_CENTER, 5); + emp->SetSizer(es); + // Controls control = new SymbolControl (this, ID_CONTROL, 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* v = new wxBoxSizer(wxVERTICAL); v->Add(emp, 0, wxEXPAND); @@ -137,6 +137,52 @@ void SymbolWindow::init(Window* parent, SymbolP symbol) { s->Add(control, 1, wxEXPAND); 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 wxUpdateUIEvent::SetMode(wxUPDATE_UI_PROCESS_SPECIFIED); SetExtraStyle(wxWS_EX_PROCESS_UI_UPDATES); diff --git a/src/mse.vcproj b/src/mse.vcproj index f61c932c..eb9c156f 100644 --- a/src/mse.vcproj +++ b/src/mse.vcproj @@ -877,6 +877,18 @@ + + + + + + + +