mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
Implemented selection using rectangles in symbol editor
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@535 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
|
||||
DECLARE_TYPEOF_COLLECTION(pair<SymbolShapeP COMMA SymbolShapeCombine>);
|
||||
DECLARE_TYPEOF_COLLECTION(pair<SymbolPartP COMMA size_t >);
|
||||
DECLARE_TYPEOF_COLLECTION(RemoveSymbolPartsAction::Removal);
|
||||
DECLARE_TYPEOF_COLLECTION(SymbolPartP);
|
||||
DECLARE_TYPEOF_COLLECTION(ControlPointP);
|
||||
|
||||
@@ -375,13 +376,25 @@ void AddSymbolPartAction::perform(bool to_undo) {
|
||||
RemoveSymbolPartsAction::RemoveSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts)
|
||||
: symbol(symbol)
|
||||
{
|
||||
check(symbol, parts);
|
||||
}
|
||||
|
||||
void RemoveSymbolPartsAction::check(SymbolGroup& group, const set<SymbolPartP>& parts) {
|
||||
size_t index = 0;
|
||||
FOR_EACH(p, symbol.parts) {
|
||||
size_t removed = 0;
|
||||
FOR_EACH(p, group.parts) {
|
||||
if (parts.find(p) != parts.end()) {
|
||||
removals.push_back(make_pair(p, index)); // remove this part
|
||||
removals.push_back(Removal(group, index, p)); // remove this part
|
||||
++ removed;
|
||||
} else if (SymbolGroup* g = p->isSymbolGroup()) {
|
||||
check(*g, parts);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
if (!group.isSymbolSymmetry() && &group != &symbol) {
|
||||
// remove empty groups
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
String RemoveSymbolPartsAction::getName(bool to_undo) const {
|
||||
@@ -393,15 +406,15 @@ void RemoveSymbolPartsAction::perform(bool to_undo) {
|
||||
// reinsert the parts
|
||||
// ascending order, this is the reverse of removal
|
||||
FOR_EACH(r, removals) {
|
||||
assert(r.second <= symbol.parts.size());
|
||||
symbol.parts.insert(symbol.parts.begin() + r.second, r.first);
|
||||
assert(r.pos <= r.parent->parts.size());
|
||||
r.parent->parts.insert(r.parent->parts.begin() + r.pos, r.removed);
|
||||
}
|
||||
} else {
|
||||
// remove the parts
|
||||
// descending order, because earlier removals shift the rest of the vector
|
||||
FOR_EACH_REVERSE(r, removals) {
|
||||
assert(r.second < symbol.parts.size());
|
||||
symbol.parts.erase(symbol.parts.begin() + r.second);
|
||||
assert(r.pos < r.parent->parts.size());
|
||||
r.parent->parts.erase(r.parent->parts.begin() + r.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,8 +211,21 @@ class RemoveSymbolPartsAction : public SymbolPartListAction {
|
||||
|
||||
private:
|
||||
Symbol& symbol;
|
||||
/// Removed parts and their positions, sorted by ascending pos
|
||||
vector<pair<SymbolPartP, size_t> > removals;
|
||||
/// Check for removals in a group
|
||||
void check(SymbolGroup& group, const set<SymbolPartP>& parts);
|
||||
public:
|
||||
/// A removal step
|
||||
struct Removal {
|
||||
inline Removal(SymbolGroup& parent, size_t pos, const SymbolPartP& removed)
|
||||
: parent(&parent), pos(pos), removed(removed)
|
||||
{}
|
||||
SymbolGroup* parent;
|
||||
size_t pos;
|
||||
SymbolPartP removed;
|
||||
};
|
||||
private:
|
||||
/// Removed parts, sorted by ascending pos
|
||||
vector<Removal> removals;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Duplicate symbol parts
|
||||
|
||||
@@ -56,7 +56,7 @@ void SymbolSelectEditor::draw(DC& dc) {
|
||||
if (click_mode == CLICK_RECT) {
|
||||
// draw selection rectangle
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen(wxPen(*wxBLUE,1,wxDOT));
|
||||
dc.SetPen(wxPen(*wxCYAN,1,wxDOT));
|
||||
RealRect rect = control.rotation.tr(RealRect(selection_rect_a, RealSize(selection_rect_b - selection_rect_a)));
|
||||
dc.DrawRectangle(rect);
|
||||
} else {
|
||||
@@ -292,13 +292,16 @@ template <typename Event> int snap(Event& ev) {
|
||||
|
||||
void SymbolSelectEditor::onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
|
||||
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);
|
||||
if (control.selected_parts.selectRect(selection_rect_a, selection_rect_b, to)) {
|
||||
control.signalSelectionChange();
|
||||
}
|
||||
selection_rect_b = to;
|
||||
control.Refresh(false);
|
||||
return;
|
||||
}
|
||||
if (control.selected_parts.empty()) return;
|
||||
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
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2007 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <gui/symbol/selection.hpp>
|
||||
#include <data/symbol.hpp>
|
||||
#include <gfx/bezier.hpp>
|
||||
|
||||
DECLARE_TYPEOF_COLLECTION(SymbolPartP);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Selection
|
||||
|
||||
void SymbolPartsSelection::setSymbol(const SymbolP& symbol) {
|
||||
root = symbol.get();
|
||||
clear();
|
||||
}
|
||||
|
||||
void SymbolPartsSelection::clear() {
|
||||
selection.clear();
|
||||
}
|
||||
|
||||
bool SymbolPartsSelection::select(const SymbolPartP& part, SelectMode mode) {
|
||||
// make sure part is not the decendent of a part that is already selected
|
||||
if (mode != SELECT_OVERRIDE) {
|
||||
FOR_EACH(s, selection) {
|
||||
if (isAncestor(s.get(), part.get())) return false;
|
||||
}
|
||||
}
|
||||
// select
|
||||
if (mode == SELECT_OVERRIDE) {
|
||||
if (selection.size() == 1 && *selection.begin() == part) return false; // already selected
|
||||
selection.clear();
|
||||
selection.insert(part);
|
||||
} else if (mode == SELECT_IF_OUTSIDE) {
|
||||
if (selected(part)) {
|
||||
return false;
|
||||
} else {
|
||||
selection.clear();
|
||||
selection.insert(part);
|
||||
}
|
||||
} else {
|
||||
if (selected(part)) {
|
||||
selection.erase(part);
|
||||
} else {
|
||||
selection.insert(part);
|
||||
// make part is not the ancestor of a part that is already selected
|
||||
clearChildren(part.get());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SymbolShapeP SymbolPartsSelection::getAShape() const {
|
||||
FOR_EACH(s, selection) {
|
||||
if (s->isSymbolShape()) return static_pointer_cast<SymbolShape>(s);
|
||||
}
|
||||
return SymbolShapeP();
|
||||
}
|
||||
|
||||
void SymbolPartsSelection::clearChildren(SymbolPart* part) {
|
||||
if (SymbolGroup* g = part->isSymbolGroup()) {
|
||||
FOR_EACH(p, g->parts) {
|
||||
if (selected(p)) selection.erase(p);
|
||||
clearChildren(p.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SymbolPartsSelection::isAncestor(SymbolPart* ancestor, SymbolPart* part) {
|
||||
if (SymbolGroup* g = ancestor->isSymbolGroup()) {
|
||||
FOR_EACH(p, g->parts) {
|
||||
if (part == p.get()) return true;
|
||||
if (isAncestor(p.get(), part)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Position based
|
||||
|
||||
SymbolPartP SymbolPartsSelection::find(const SymbolPartP& part, const Vector2D& pos) const {
|
||||
if (SymbolShape* s = part->isSymbolShape()) {
|
||||
if (point_in_shape(pos, *s)) return part;
|
||||
}
|
||||
if (SymbolGroup* g = part->isSymbolGroup()) {
|
||||
FOR_EACH(p, g->parts) {
|
||||
SymbolPartP found = find(p, pos);
|
||||
if (found) {
|
||||
if (part->isSymbolSymmetry() || selected(found)) {
|
||||
return found;
|
||||
} else {
|
||||
return part; // don't select inside groups
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return SymbolPartP();
|
||||
}
|
||||
|
||||
SymbolPartP SymbolPartsSelection::find(const Vector2D& position) const {
|
||||
FOR_EACH(p, root->parts) {
|
||||
SymbolPartP found = find(p, position);
|
||||
if (found) return found;
|
||||
}
|
||||
return SymbolPartP();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Rectangle based
|
||||
|
||||
bool SymbolPartsSelection::selectRect(const Vector2D& a, const Vector2D& b, const Vector2D& c) {
|
||||
return selectRect(*root, a, b, c);
|
||||
}
|
||||
bool SymbolPartsSelection::selectRect(const SymbolGroup& parent, const Vector2D& a, const Vector2D& b, const Vector2D& c) {
|
||||
bool changes = false;
|
||||
FOR_EACH_CONST(p, root->parts) {
|
||||
bool in_ab = (p->min_pos.x >= min(a.x, b.x) && p->min_pos.y >= min(a.y, b.y) && p->max_pos.x <= max(a.x, b.x) && p->max_pos.y <= max(a.y, b.y));
|
||||
bool in_bc = (p->min_pos.x >= min(a.x, c.x) && p->min_pos.y >= min(a.y, c.y) && p->max_pos.x <= max(a.x, c.x) && p->max_pos.y <= max(a.y, c.y));
|
||||
if (in_ab != in_bc) {
|
||||
select(p, SELECT_TOGGLE);
|
||||
changes = true;
|
||||
} else if (SymbolGroup* g = p->isSymbolGroup()) {
|
||||
if (p->isSymbolSymmetry() || selected(p)) {
|
||||
selectRect(*g, a, b, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2007 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
#ifndef HEADER_GUI_SYMBOL_SELECTION
|
||||
#define HEADER_GUI_SYMBOL_SELECTION
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
|
||||
class Vector2D;
|
||||
DECLARE_POINTER_TYPE(Symbol);
|
||||
DECLARE_POINTER_TYPE(SymbolPart);
|
||||
DECLARE_POINTER_TYPE(SymbolShape);
|
||||
class SymbolGroup;
|
||||
|
||||
// ----------------------------------------------------------------------------- : Selection
|
||||
|
||||
enum SelectMode
|
||||
{ SELECT_OVERRIDE // give a completely new selection
|
||||
, SELECT_IF_OUTSIDE // define a new selection if the affected part is not already selected
|
||||
, SELECT_TOGGLE // toggle selection of affected part
|
||||
};
|
||||
|
||||
/// The selected parts of a symbol, enforcing constraints
|
||||
class SymbolPartsSelection {
|
||||
public:
|
||||
inline SymbolPartsSelection() : root(nullptr) {}
|
||||
|
||||
void setSymbol(const SymbolP& symbol);
|
||||
|
||||
/// Clear selection
|
||||
void clear();
|
||||
/// Select a part or toggle its selection
|
||||
/** Return true if the selection changed */
|
||||
bool select(const SymbolPartP& part, SelectMode mode = SELECT_OVERRIDE);
|
||||
/// Toggle the selection of the parts in a rectangle (a,b) or (a,c) but not in both
|
||||
/** Return true if the selection changed */
|
||||
bool selectRect(const Vector2D& a, const Vector2D& b, const Vector2D& c);
|
||||
|
||||
/// Find a part by position (not just in the selection!)
|
||||
/** Returns SymbolPartP() if nothing is found.
|
||||
* Does not select inside groups unless the part in question is already selected.
|
||||
*/
|
||||
SymbolPartP find(const Vector2D& position) const;
|
||||
|
||||
/// Get the selection
|
||||
inline const set<SymbolPartP>& get() const { return selection; }
|
||||
|
||||
/// Is the selection empty?
|
||||
inline bool empty() const { return selection.empty(); }
|
||||
/// Number of items selected
|
||||
inline size_t size() const { return selection.size(); }
|
||||
/// Is a part selected?
|
||||
inline bool selected(const SymbolPartP& part) const {
|
||||
return selection.find(part) != selection.end();
|
||||
}
|
||||
|
||||
/// Get any SymbolShape if there is one selected
|
||||
SymbolShapeP getAShape() const;
|
||||
|
||||
private:
|
||||
Symbol* root;
|
||||
set<SymbolPartP> selection;
|
||||
|
||||
/// Find a part, in some root
|
||||
SymbolPartP find(const SymbolPartP& part, const Vector2D& pos) const;
|
||||
/// Select rect for some parent
|
||||
bool selectRect(const SymbolGroup& parent, const Vector2D& a, const Vector2D& b, const Vector2D& c);
|
||||
|
||||
/// Make sure not both a parent and its child/decendant are selected
|
||||
void clearChildren (SymbolPart* part);
|
||||
/// Is a part another's ancestor?
|
||||
static bool isAncestor(SymbolPart* ancestor, SymbolPart* part);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
@@ -0,0 +1,160 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2007 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <gui/symbol/symmetry_editor.hpp>
|
||||
#include <gui/util.hpp>
|
||||
#include <util/window_id.hpp>
|
||||
#include <data/settings.hpp>
|
||||
#include <data/action/symbol.hpp>
|
||||
#include <data/action/symbol_part.hpp>
|
||||
#include <wx/spinctrl.h>
|
||||
|
||||
// ----------------------------------------------------------------------------- : SymbolSymmetryEditor
|
||||
|
||||
SymbolSymmetryEditor::SymbolSymmetryEditor(SymbolControl* control)
|
||||
: SymbolEditorBase(control)
|
||||
, mode(ID_SYMMETRY_ROTATION)
|
||||
, drawing(false)
|
||||
{
|
||||
control->SetCursor(*wxCROSS_CURSOR);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Drawing
|
||||
|
||||
void SymbolSymmetryEditor::draw(DC& dc) {
|
||||
if (symmetry) {
|
||||
control.highlightPart(dc, *symmetry);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : UI
|
||||
|
||||
void SymbolSymmetryEditor::initUI(wxToolBar* tb, wxMenuBar* mb) {
|
||||
copies = new wxSpinCtrl( tb, ID_COPIES, _("2"), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 2, 10, 2);
|
||||
copies->SetHelpText(_HELP_("copies"));
|
||||
copies->SetSize(50, -1);
|
||||
tb->AddSeparator();
|
||||
tb->AddTool(ID_SYMMETRY_ROTATION, _TOOL_("rotation"), load_resource_image(_("symmetry_rotation")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("rotation"), _HELP_("rotation"));
|
||||
tb->AddTool(ID_SYMMETRY_REFLECTION, _TOOL_("reflection"), load_resource_image(_("symmetry_reflection")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("reflection"), _HELP_("reflection"));
|
||||
tb->AddControl(copies);
|
||||
tb->Realize();
|
||||
control.SetCursor(*wxCROSS_CURSOR);
|
||||
stopActions(); // set status text
|
||||
}
|
||||
|
||||
void SymbolSymmetryEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
|
||||
tb->DeleteTool(ID_SYMMETRY_REFLECTION);
|
||||
tb->DeleteTool(ID_SYMMETRY_ROTATION);
|
||||
// HACK: hardcoded size of rest of toolbar
|
||||
tb->DeleteToolByPos(7); // delete separator
|
||||
tb->DeleteTool(ID_COPIES); // delete sides
|
||||
#if wxVERSION_NUMBER < 2600
|
||||
delete sides;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SymbolSymmetryEditor::onUpdateUI(wxUpdateUIEvent& ev) {
|
||||
if (ev.GetId() >= ID_SYMMETRY && ev.GetId() < ID_SYMMETRY_MAX) {
|
||||
ev.Check(ev.GetId() == mode);
|
||||
} else if (ev.GetId() == ID_COPIES) {
|
||||
ev.Enable(true);
|
||||
} else {
|
||||
ev.Enable(false); // we don't know about this item
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolSymmetryEditor::onCommand(int id) {
|
||||
if (id >= ID_SYMMETRY && id < ID_SYMMETRY_MAX) {
|
||||
mode = id;
|
||||
stopActions();
|
||||
}
|
||||
}
|
||||
|
||||
int SymbolSymmetryEditor::modeToolId() { return ID_MODE_SYMMETRY; }
|
||||
|
||||
// ----------------------------------------------------------------------------- : Mouse events
|
||||
|
||||
void SymbolSymmetryEditor::onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {
|
||||
// Start drawing
|
||||
drawing = true;
|
||||
center = handle = pos;
|
||||
SetStatusText(_HELP_("drag to draw symmetry"));
|
||||
}
|
||||
|
||||
void SymbolSymmetryEditor::onLeftUp (const Vector2D& pos, wxMouseEvent& ev) {
|
||||
if (drawing && symmetry) {
|
||||
// Finalize the symmetry
|
||||
getSymbol()->actions.add(new AddSymbolPartAction(*getSymbol(), symmetry));
|
||||
// Select the part
|
||||
control.selectPart(symmetry);
|
||||
// No need to clean up, this editor gets destroyed
|
||||
//stopActions();
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolSymmetryEditor::onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
|
||||
// Resize the object
|
||||
if (drawing) {
|
||||
handle = to;
|
||||
makePart(center, handle, ev.ControlDown(), settings.symbol_grid_snap != ev.ShiftDown());
|
||||
control.Refresh(false);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Other events
|
||||
|
||||
void SymbolSymmetryEditor::onKeyChange(wxKeyEvent& ev) {
|
||||
if (drawing) {
|
||||
if (ev.GetKeyCode() == WXK_CONTROL || ev.GetKeyCode() == WXK_SHIFT) {
|
||||
// changed constrains
|
||||
makePart(center, handle, ev.ControlDown(), settings.symbol_grid_snap != ev.ShiftDown());
|
||||
control.Refresh(false);
|
||||
} else if (ev.GetKeyCode() == WXK_ESCAPE) {
|
||||
// cancel drawing
|
||||
stopActions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SymbolSymmetryEditor::isEditing() { return drawing; }
|
||||
|
||||
// ----------------------------------------------------------------------------- : Generating shapes
|
||||
|
||||
void SymbolSymmetryEditor::stopActions() {
|
||||
symmetry = SymbolSymmetryP();
|
||||
drawing = false;
|
||||
SetStatusText(_HELP_("draw symmetry"));
|
||||
control.Refresh(false);
|
||||
}
|
||||
|
||||
void SymbolSymmetryEditor::makePart(Vector2D a, Vector2D b, bool constrained, bool snap) {
|
||||
// snap
|
||||
if (snap) {
|
||||
a = snap_vector(a, settings.symbol_grid_size);
|
||||
b = snap_vector(b, settings.symbol_grid_size);
|
||||
}
|
||||
// constrain
|
||||
Vector2D dir = b - a;
|
||||
if (constrained) {
|
||||
double angle = atan2(dir.y, dir.x);
|
||||
// multiples of 2pi/24 i.e. 24 stops
|
||||
double mult = (2 * M_PI) / 24;
|
||||
angle = floor(angle / mult + 0.5) * mult;
|
||||
dir = Vector2D(cos(angle), sin(angle)) * dir.length();
|
||||
}
|
||||
// make part
|
||||
if (!symmetry) {
|
||||
symmetry = new_intrusive<SymbolSymmetry>();
|
||||
}
|
||||
symmetry->kind = mode == ID_SYMMETRY_ROTATION ? SYMMETRY_ROTATION : SYMMETRY_REFLECTION;
|
||||
symmetry->copies = copies->GetValue();
|
||||
symmetry->center = a;
|
||||
symmetry->handle = dir;
|
||||
symmetry->name = capitalize(mode == ID_SYMMETRY_ROTATION ? _TYPE_("rotation") : _TYPE_("reflection"))
|
||||
+ String::Format(_(" (%d)"), symmetry->copies);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2007 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
#ifndef HEADER_GUI_SYMBOL_SYMMETRY_EDITOR
|
||||
#define HEADER_GUI_SYMBOL_SYMMETRY_EDITOR
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
#include <gui/symbol/editor.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : SymbolSymmetryEditor
|
||||
|
||||
/// Editor for adding symmetries
|
||||
class SymbolSymmetryEditor : public SymbolEditorBase {
|
||||
public:
|
||||
SymbolSymmetryEditor(SymbolControl* control);
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
virtual void draw(DC& dc);
|
||||
|
||||
// --------------------------------------------------- : UI
|
||||
|
||||
virtual void initUI (wxToolBar* tb, wxMenuBar* mb);
|
||||
virtual void destroyUI(wxToolBar* tb, wxMenuBar* mb);
|
||||
virtual void onUpdateUI(wxUpdateUIEvent&);
|
||||
virtual void onCommand(int id);
|
||||
virtual int modeToolId();
|
||||
|
||||
// --------------------------------------------------- : Mouse events
|
||||
|
||||
virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev);
|
||||
virtual void onLeftUp (const Vector2D& pos, wxMouseEvent& ev);
|
||||
virtual void onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
|
||||
|
||||
// --------------------------------------------------- : Other events
|
||||
|
||||
virtual void onKeyChange(wxKeyEvent& ev);
|
||||
|
||||
virtual bool isEditing();
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
private:
|
||||
int mode;
|
||||
SymbolSymmetryP symmetry;
|
||||
bool drawing;
|
||||
Vector2D center, handle;
|
||||
// controls
|
||||
wxSpinCtrl* copies;
|
||||
|
||||
/// Cancel the drawing
|
||||
void stopActions();
|
||||
|
||||
/// Make the symmetry object
|
||||
void makePart(Vector2D a, Vector2D b, bool constrained, bool snap);
|
||||
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
@@ -565,6 +565,8 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
|
||||
} else if (minPrec <= PREC_SET && token==_(":=")) {
|
||||
// We made a mistake, the part before the := should be a variable name,
|
||||
// not an expression. Remove that instruction.
|
||||
// TODO: There is a bug here:
|
||||
// (if x then a else b) := c will use the 'b' as variable name
|
||||
Instruction instr = script.getInstructions().back();
|
||||
if (instr.instr != I_GET_VAR) {
|
||||
input.add_error(_("Can only assign to variables"));
|
||||
|
||||
Reference in New Issue
Block a user