initial checkin of C++ port (in progress)

git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@2 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
twanvl
2006-10-01 14:08:07 +00:00
parent f5c0071da6
commit ddfb1a5089
56 changed files with 8781 additions and 0 deletions
+284
View File
@@ -0,0 +1,284 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/symbol/basic_shape_editor.hpp>
#include <util/window_id.hpp>
#include <data/action/symbol.hpp>
#include <wx/spinctrl.h>
// ----------------------------------------------------------------------------- : SymbolBasicShapeEditor
SymbolBasicShapeEditor::SymbolBasicShapeEditor(SymbolControl* control)
: SymbolEditorBase(control)
, drawing(false)
, mode(ID_SHAPE_CIRCLE)
{
control->SetCursor(*wxCROSS_CURSOR);
}
// ----------------------------------------------------------------------------- : Drawing
void SymbolBasicShapeEditor::draw(DC& dc) {
// highlight the part we are drawing
if (drawing) {
control.highlightPart(dc, *shape, HIGHLIGHT_BORDER);
}
}
// ----------------------------------------------------------------------------- : UI
void SymbolBasicShapeEditor::initUI(wxToolBar* tb, wxMenuBar* mb) {
sides = new wxSpinCtrl( tb, ID_SIDES, _("3"), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 3, 50, 3);
sidesL = new wxStaticText(tb, ID_SIDES, _(" sides: "));
sides->SetSize(50, -1);
tb->AddSeparator();
tb->AddTool(ID_SHAPE_CIRCLE, _("Ellipse"), Bitmap(_("TOOL_CIRCLE")), wxNullBitmap, wxITEM_CHECK, _("Circle / Ellipse"), _("Draw circles and ellipses"));
tb->AddTool(ID_SHAPE_RECTANGLE, _("Rectangle"), Bitmap(_("TOOL_RECTANGLE")), wxNullBitmap, wxITEM_CHECK, _("Square / Rectangle"), _("Draw squares and rectangles"));
tb->AddTool(ID_SHAPE_POLYGON, _("Polygon"), Bitmap(_("TOOL_TRIANGLE")), wxNullBitmap, wxITEM_CHECK, _("Polygon"), _("Draw triangles, pentagons and other regular polygons"));
tb->AddTool(ID_SHAPE_STAR, _("Star"), Bitmap(_("TOOL_STAR")), wxNullBitmap, wxITEM_CHECK, _("Star"), _("Draw stars"));
tb->AddControl(sidesL);
tb->AddControl(sides);
tb->Realize();
control.SetCursor(*wxCROSS_CURSOR);
}
void SymbolBasicShapeEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
tb->DeleteTool(ID_SHAPE_CIRCLE);
tb->DeleteTool(ID_SHAPE_RECTANGLE);
tb->DeleteTool(ID_SHAPE_POLYGON);
tb->DeleteTool(ID_SHAPE_STAR);
tb->RemoveChild(sidesL);
tb->RemoveChild(sides);
// HACK: hardcoded size of rest of toolbar
tb->DeleteToolByPos(4); // delete separator
tb->DeleteToolByPos(4); // delete sidesL
tb->DeleteToolByPos(4); // delete sides
#if wxVERSION_NUMBER < 2600
delete sides;
delete sidesL;
#endif
}
void SymbolBasicShapeEditor::onUpdateUI(wxUpdateUIEvent& ev) {
if (ev.GetId() >= ID_SHAPE && ev.GetId() < ID_SHAPE_MAX) {
ev.Check(ev.GetId() == mode);
} else if (ev.GetId() == ID_SIDES) {
ev.Enable(mode == ID_SHAPE_POLYGON || mode == ID_SHAPE_STAR);
} else {
ev.Enable(false); // we don't know about this item
}
}
void SymbolBasicShapeEditor::onCommand(int id) {
if (id >= ID_SHAPE && id < ID_SHAPE_MAX) {
// change shape mode
mode = id;
}
}
int SymbolBasicShapeEditor::modeToolId() { return ID_MODE_SHAPES; }
// ----------------------------------------------------------------------------- : Mouse events
void SymbolBasicShapeEditor::onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {
// Start drawing
drawing = true;
start = end = pos;
SetStatusText(_("Drag to resize shape, Ctrl constrains shape, Shift centers shape"));
}
void SymbolBasicShapeEditor::onLeftUp (const Vector2D& pos, wxMouseEvent& ev) {
if (drawing && shape) {
// Finalize the shape
getSymbol()->actions.add(new AddSymbolPartAction(*getSymbol(), shape));
// Select the part
control.selectPart(shape);
// no need to clean up, this editor is replaced
// // Clean up
// stopActions()
}
}
void SymbolBasicShapeEditor::onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
// Resize the object
if (drawing) {
end = to;
makeShape(start, end, ev.ControlDown(), ev.ShiftDown());
control.Refresh(false);
}
}
// ----------------------------------------------------------------------------- : Other events
void SymbolBasicShapeEditor::onKeyChange(wxKeyEvent& ev) {
if (drawing) {
if (ev.GetKeyCode() == WXK_CONTROL || ev.GetKeyCode() == WXK_SHIFT) {
// changed constrains
makeShape(start, end, ev.ControlDown(), ev.ShiftDown());
control.Refresh(false);
} else if (ev.GetKeyCode() == WXK_ESCAPE) {
// cancel drawing
stopActions();
}
}
}
bool SymbolBasicShapeEditor::isEditing() { return drawing; }
// ----------------------------------------------------------------------------- : Generating shapes
void SymbolBasicShapeEditor::stopActions() {
shape = SymbolPartP();
drawing = false;
switch (mode) {
case ID_SHAPE_CIRCLE:
SetStatusText(_("Click and drag to draw a ellipse, hold Ctrl for a circle"));
break;
case ID_SHAPE_RECTANGLE:
SetStatusText(_("Click and drag to draw a rectangle, hold Ctrl for a square"));
break;
case ID_SHAPE_POLYGON:
SetStatusText(_("Click and drag to draw a polygon"));
break;
case ID_SHAPE_STAR:
SetStatusText(_("Click and drag to draw a star"));
break;
}
control.Refresh(false);
}
inline double sgn(double d) {
return d < 0 ? - 1 : 1;
}
void SymbolBasicShapeEditor::makeShape(const Vector2D& a, const Vector2D& b, bool constrained, bool centered) {
// constrain
Vector2D size = b - a;
if (constrained) {
if (abs(size.x) > abs(size.y)) {
size.y = sgn(size.y) * abs(size.x);
} else {
size.x = sgn(size.x) * abs(size.y);
}
}
// make shape
if (centered) {
makeCenteredShape(a, size, constrained);
} else {
makeCenteredShape(a + size / 2, size / 2, constrained);
}
}
// TODO : Move out of this class
void SymbolBasicShapeEditor::makeCenteredShape(const Vector2D& c, Vector2D r, bool constrained) {
shape = new_shared<SymbolPart>();
// What shape to make?
switch (mode) {
case ID_SHAPE_CIRCLE: {
// A circle / ellipse
if (constrained) {
shape->name = _("Circle");
} else {
shape->name = _("Ellipse");
}
// a circle has 4 control points, the first is: (x+r, y) db(0, kr) da(0, -kr)
// kr is a magic constant
const double kr = 0.5522847498f; // = 4/3 * (sqrt(2) - 1)
shape->points.push_back(new_shared7<ControlPoint>(c.x + r.x, c.y, 0, kr * r.y, 0, -kr * r.y, LOCK_SIZE));
shape->points.push_back(new_shared7<ControlPoint>(c.x, c.y - r.y, kr * r.x, 0, -kr * r.x, 0, LOCK_SIZE));
shape->points.push_back(new_shared7<ControlPoint>(c.x - r.x, c.y, 0, -kr * r.y, 0, kr * r.y, LOCK_SIZE));
shape->points.push_back(new_shared7<ControlPoint>(c.x, c.y + r.y, -kr * r.x, 0, kr * r.x, 0, LOCK_SIZE));
break;
} case ID_SHAPE_RECTANGLE: {
// A rectangle / square
if (constrained) {
shape->name = _("Square");
} else {
shape->name = _("Rectangle");
}
// a rectangle just has four corners
shape->points.push_back(new_shared2<ControlPoint>(c.x - r.x, c.y - r.y));
shape->points.push_back(new_shared2<ControlPoint>(c.x + r.x, c.y - r.y));
shape->points.push_back(new_shared2<ControlPoint>(c.x + r.x, c.y + r.y));
shape->points.push_back(new_shared2<ControlPoint>(c.x - r.x, c.y + r.y));
break;
} default: {
// A polygon or star
int n = sides->GetValue(); // number of sides
switch (n) {
case 3: shape->name = _("Triangle");
case 4: shape->name = _("Rhombus");
case 5: shape->name = _("Pentagon");
case 6: shape->name = _("Hexagon");
default: shape->name = _("Polygon");
}
// Example: n == 7
// a a..g = corners
// g b O = center
// f O c ra = radius, |Oa|
// e d
double alpha = 2 * M_PI / n; // internal angle /_aOb
// angle between point touching side and point on top
// floor((n+1)/4) == number of sides between these two points
// beta = /_aOc
double beta = alpha * ((n+1)/4);
// define:
// width = 2 = |fc|
// lb = |ac|
// gamma = (pi - beta) / 2
// equations:
// lb * sin(gamma) == 1 (right angled tri /_\ aXc where X is halfway fc)
// lb / sin(beta) == ra / sin(gamma) (law of sines in /_\ abc)
// solving leads to:
// sin(gamma) == cos(beta/2)
double lb = 1 / cos(beta/2);
double ra = lb / sin(beta) * cos(beta/2);
// now we know the center of the polygon:
double y = c.y + (ra - 1) * r.y;
if (mode == ID_SHAPE_POLYGON) {
// we can generate points
for(int i = 0 ; i < n ; ++i) {
double theta = alpha * i;
shape->points.push_back(new_shared2<ControlPoint>(
c.x + ra * r.x * sin(theta),
y - ra * r.y * cos(theta)
));
}
} else {
// a star is made using a smaller, inverted polygon at the inside
// points are interleaved
// rb = radius of smaller polygon
// lc = length of a side
double lc = ra * sin(alpha) / cos(alpha/2);
// ld = length of side skipping one corner
double delta = alpha * 2;
double ld = ra * sin(delta) / cos(delta/2);
// Using symmetry: /_\gab ~ /_\axb where x is intersection
// gives ratio lc/ld
// converting back to radius using ra/lb = cos(beta/2) / sin(beta)
// NOTE: This is only correct for n<=6, but gives acceptable results for higher n
double rb = (ld - 2 * lc * (lc/ld)) * ra / lb;
for(int i = 0 ; i < n ; ++i) {
double theta = alpha * i;
// from a
shape->points.push_back(new_shared2<ControlPoint>(
c.x + ra * r.x * sin(theta),
y - ra * r.y * cos(theta)
));
// from b
theta = alpha * (i + 0.5);
shape->points.push_back(new_shared2<ControlPoint>(
c.x + rb * r.x * sin(theta),
y - rb * r.y * cos(theta)
));
}
}
break;
}
}
}
+73
View File
@@ -0,0 +1,73 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_BASIC_SHAPE_EDITOR
#define HEADER_GUI_SYMBOL_BASIC_SHAPE_EDITOR
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <gui/symbol/editor.hpp>
class wxSpinCtrl;
// ----------------------------------------------------------------------------- : SymbolBasicShapeEditor
/// Editor for drawing basic shapes such as rectangles and polygons
class SymbolBasicShapeEditor : public SymbolEditorBase {
public:
SymbolBasicShapeEditor(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& e);
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;
SymbolPartP shape;
Vector2D start;
Vector2D end;
bool drawing;
// controls
wxSpinCtrl* sides;
wxStaticText* sidesL;
/// Cancel the drawing
void stopActions();
/// Make the shape
/** when centered: a = center, b-a = radius
* otherwise: a = top left, b = bottom right
*/
void makeShape(const Vector2D& a, const Vector2D& b, bool constrained, bool centered);
/// Make the shape, centered in c, with radius r
void makeCenteredShape(const Vector2D& c, Vector2D r, bool constrained);
};
// ----------------------------------------------------------------------------- : EOF
#endif
+236
View File
@@ -0,0 +1,236 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/symbol/control.hpp>
#include <gui/symbol/window.hpp>
#include <gui/symbol/editor.hpp>
#include <gui/symbol/select_editor.hpp>
#include <gui/symbol/point_editor.hpp>
#include <gui/symbol/basic_shape_editor.hpp>
#include <gui/util.hpp>
#include <data/action/symbol.hpp>
#include <util/window_id.hpp>
#include <wx/dcbuffer.h>
// ----------------------------------------------------------------------------- : SymbolControl
SymbolControl::SymbolControl(SymbolWindow* parent, int id, const SymbolP& symbol)
: wxControl(parent, id)
, SymbolViewer(symbol)
, parent(parent)
{
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
}
void SymbolControl::switchEditor(const SymbolEditorBaseP& e) {
if (editor) editor->destroyUI(parent->GetToolBar(), parent->GetMenuBar());
editor = e;
if (editor) editor->initUI (parent->GetToolBar(), parent->GetMenuBar());
Refresh(false);
}
void SymbolControl::onSymbolChange() {
selectedParts.clear();
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
Refresh(false);
}
void SymbolControl::onModeChange(wxCommandEvent& ev) {
switch (ev.GetId()) {
case ID_MODE_SELECT:
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
break;
case ID_MODE_ROTATE:
switchEditor(new_shared2<SymbolSelectEditor>(this, true));
break;
case ID_MODE_POINTS:
if (selectedParts.size() == 1) {
singleSelection = *selectedParts.begin();
switchEditor(new_shared2<SymbolPointEditor>(this, singleSelection));
}
break;
case ID_MODE_SHAPES:
if (!selectedParts.empty()) {
selectedParts.clear();
signalSelectionChange();
}
switchEditor(new_shared1<SymbolBasicShapeEditor>(this));
break;
}
}
void SymbolControl::onExtraTool(wxCommandEvent& ev) {
if (editor) editor->onCommand(ev.GetId());
}
void SymbolControl::onAction(const Action& action) {
TYPE_CASE_(action, SymbolPartAction) {
Refresh(false);
}
}
void SymbolControl::onUpdateSelection() {
switch(editor->modeToolId()) {
case ID_MODE_POINTS:
// can only select a single part!
if (selectedParts.size() > 1) {
SymbolPartP part = *selectedParts.begin();
selectedParts.clear();
selectedParts.insert(part);
signalSelectionChange();
} else if (selectedParts.empty()) {
selectedParts.insert(singleSelection);
signalSelectionChange();
}
if (singleSelection != *selectedParts.begin()) {
// begin editing another part
singleSelection = *selectedParts.begin();
editor = new_shared2<SymbolPointEditor>(this, singleSelection);
Refresh(false);
}
break;
case ID_MODE_SHAPES:
if (!selectedParts.empty()) {
// there can't be a selection
selectedParts.clear();
signalSelectionChange();
}
break;
default:
Refresh(false);
break;
}
}
void SymbolControl::selectPart(const SymbolPartP& part) {
selectedParts.clear();
selectedParts.insert(part);
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
signalSelectionChange();
}
void SymbolControl::activatePart(const SymbolPartP& part) {
selectedParts.clear();
selectedParts.insert(part);
switchEditor(new_shared2<SymbolPointEditor>(this, part));
}
void SymbolControl::signalSelectionChange() {
parent->onSelectFromControl();
}
bool SymbolControl::isEditing() {
return editor && editor->isEditing();
}
// ----------------------------------------------------------------------------- : Drawing
void SymbolControl::draw(DC& dc) {
// clear the background
clearDC(dc, Color(0, 128, 0));
// draw symbol iself
SymbolViewer::draw(dc);
// draw editing overlay
if (editor) {
editor->draw(dc);
}
}
void SymbolControl::onPaint(wxPaintEvent& e) {
wxBufferedPaintDC dc(this);
dc.BeginDrawing();
draw(dc);
dc.EndDrawing();
}
// ----------------------------------------------------------------------------- : Events
// Mouse events, convert position, forward event
void SymbolControl::onLeftDown(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onLeftDown(pos, ev);
lastPos = pos;
ev.Skip(); // for focus
}
void SymbolControl::onLeftUp(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onLeftUp(pos, ev);
lastPos = pos;
}
void SymbolControl::onLeftDClick(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onLeftDClick(pos, ev);
lastPos = pos;
}
void SymbolControl::onRightDown(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onRightDown(pos, ev);
lastPos = pos;
}
void SymbolControl::onMotion(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
// Dragging something?
if (ev.LeftIsDown()) {
if (editor) editor->onMouseDrag(lastPos, pos, ev);
} else {
if (editor) editor->onMouseMove(lastPos, pos, ev);
}
lastPos = pos;
}
// Key events, just forward
void SymbolControl::onKeyChange(wxKeyEvent& ev) {
if (editor) editor->onKeyChange(ev);
ev.Skip(); // so we get char events
}
void SymbolControl::onChar(wxKeyEvent& ev) {
if (editor) editor->onChar(ev);
else ev.Skip();
}
void SymbolControl::onSize(wxSizeEvent& ev) {
wxSize s = ev.GetSize();
rotation.setZoom(min(s.GetWidth(), s.GetHeight()));
Refresh(false);
}
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:
ev.Check(editor->modeToolId() == ev.GetId());
if (ev.GetId() == ID_MODE_POINTS) {
// can only edit points when a single part is selected <TODO?>
ev.Enable(selectedParts.size() == 1);
}
break;
case ID_MODE_PAINT:
ev.Enable(false); // TODO
break;
default:
if (ev.GetId() >= ID_CHILD_MIN && ev.GetId() < ID_CHILD_MAX) {
editor->onUpdateUI(ev); // foward to editor
}
}
}
// ----------------------------------------------------------------------------- : Event table
BEGIN_EVENT_TABLE(SymbolControl, wxControl)
EVT_PAINT (SymbolControl::onPaint)
EVT_SIZE (SymbolControl::onSize)
EVT_LEFT_UP (SymbolControl::onLeftUp)
EVT_LEFT_DOWN (SymbolControl::onLeftDown)
EVT_RIGHT_DOWN (SymbolControl::onRightDown)
EVT_LEFT_DCLICK (SymbolControl::onLeftDClick)
EVT_MOTION (SymbolControl::onMotion)
EVT_KEY_UP (SymbolControl::onKeyChange)
EVT_KEY_DOWN (SymbolControl::onKeyChange)
EVT_CHAR (SymbolControl::onChar)
END_EVENT_TABLE ()
+103
View File
@@ -0,0 +1,103 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_CONTROL
#define HEADER_GUI_SYMBOL_CONTROL
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/symbol.hpp>
#include <gui/symbol/viewer.hpp>
class SymbolWindow;
DECLARE_POINTER_TYPE(SymbolEditorBase);
// ----------------------------------------------------------------------------- : SymbolControl
/// Control for editing symbols
/** What kind of editing is done is determined by the contained SymbolEditorBase object
* That object handles all events and the drawing. This class is mostly just a proxy.
*/
class SymbolControl : public wxControl, public SymbolViewer {
public:
SymbolControl(SymbolWindow* parent, int id, const SymbolP& symbol);
virtual void onSymbolChange();
virtual void onAction(const Action&);
// Forward command to editor
void onExtraTool(wxCommandEvent& ev);
// Switch to some editing mode
void onModeChange(wxCommandEvent& ev);
/// Handle UpdateUIEvents propagated from the SymbolWindow
/** Handles events for editing mode related stuff
*/
void onUpdateUI(wxUpdateUIEvent& ev);
/// The selection has changed, tell the part list
void signalSelectionChange();
/// Activate a part, open it in the point editor
void activatePart(const SymbolPartP& part);
/// Select a specific part from the symbol
/// The editor is switched to the select editor
void selectPart(const SymbolPartP& part);
/// Update the selection
void onUpdateSelection();
/// Are we editing?
bool isEditing();
private:
/// Switch the a different editor object
void switchEditor(const SymbolEditorBaseP& e);
/// Draw the editor
void draw(DC& dc);
private:
DECLARE_EVENT_TABLE();
// --------------------------------------------------- : Data
public:
/// What parts are selected
set<SymbolPartP> selectedParts;
SymbolPartP singleSelection;
/// Parent window
SymbolWindow* parent;
private:
/// The current editor
SymbolEditorBaseP editor;
/// Last mouse position
Vector2D lastPos;
// --------------------------------------------------- : Events
void onLeftDown (wxMouseEvent& ev);
void onLeftUp (wxMouseEvent& ev);
void onLeftDClick(wxMouseEvent& ev);
void onRightDown (wxMouseEvent& ev);
void onMotion (wxMouseEvent& ev);
void onPaint (wxPaintEvent& e);
void onKeyChange(wxKeyEvent& ev);
void onChar (wxKeyEvent& ev);
void onSize (wxSizeEvent& ev);
};
// ----------------------------------------------------------------------------- : EOF
#endif
+16
View File
@@ -0,0 +1,16 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/symbol/editor.hpp>
#include <gui/symbol/window.hpp>
// ----------------------------------------------------------------------------- : SymbolEditorBase
void SymbolEditorBase::SetStatusText(const String& text) {
control.parent->SetStatusText(text);
}
+91
View File
@@ -0,0 +1,91 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_EDITOR
#define HEADER_GUI_SYMBOL_EDITOR
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/symbol.hpp>
#include <gui/symbol/control.hpp>
class SymbolControl;
// ----------------------------------------------------------------------------- : SymbolEditorBase
/// Base class for editors of symbols.
/** A symbol editor is like a FieldEditor, events are forwarded to it.
* Differrent SymbolEditors represent different tools.
* NOTE : Do not confuse with SymbolEditor (a FieldEditor)
*/
class SymbolEditorBase {
protected:
/// The control for which we are editing
SymbolControl& control;
inline SymbolP getSymbol() { return control.getSymbol(); }
void SetStatusText(const String& text);
public:
SymbolEditorBase(SymbolControl* control)
: control(*control)
{}
virtual ~SymbolEditorBase() {};
// --------------------------------------------------- : Drawing
/// Drawing for this control,
virtual void draw(DC& dc) = 0;
// --------------------------------------------------- : UI
/// Init extra toolbar items and menus needed for this panel
virtual void initUI(wxToolBar* tb, wxMenuBar* mb) {}
/// Destroy the extra items added by initUI
virtual void destroyUI(wxToolBar* tb, wxMenuBar* mb) {}
/// Update the UI by enabling/disabling items
virtual void onUpdateUI(wxUpdateUIEvent& ev) {}
/// Respond to one of the extra menu/tool items
virtual void onCommand(int id) {}
/// Tool id used in the symbol window
virtual int modeToolId() = 0;
// --------------------------------------------------- : Mouse events
/// The left mouse button has been pressed, at the given position (internal coordinates)
virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {}
/// The left mouse button has been released, at the given position (internal coordinates)
virtual void onLeftUp (const Vector2D& pos, wxMouseEvent& ev) {}
/// The left mouse button has been double clicked, at the given position (internal coordinates)
virtual void onLeftDClick (const Vector2D& pos, wxMouseEvent& ev) {}
/// The right mouse button has been pressed, at the given position (internal coordinates)
virtual void onRightDown (const Vector2D& pos, wxMouseEvent& ev) {}
/// The mouse is being moved, no mouse buttons are pressed
virtual void onMouseMove (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {}
/// The mouse is being moved while mouse buttons are being pressed
virtual void onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {}
// --------------------------------------------------- : Keyboard events
/// A key is pressed or released, should be used for modifier keys (Shift/Ctrl/Alt)
virtual void onKeyChange (wxKeyEvent& ev) {}
/// A key is pressed/clicked
virtual void onChar (wxKeyEvent& ev) {}
// --------------------------------------------------- : Other events
/// A context menu is requested
virtual void onContextMenu(wxContextMenuEvent& ev) {}
/// Is the user currently editing, i.e. dragging the mouse?
/** This disables undo/redo, so the current action is not
* undone while it is in progress.
*/
virtual bool isEditing() { return false; }
};
// ----------------------------------------------------------------------------- : EOF
#endif
+155
View File
@@ -0,0 +1,155 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/symbol/part_list.hpp>
#include <gui/util.hpp>
#include <data/action/symbol.hpp>
#include <wx/imaglist.h>
// ----------------------------------------------------------------------------- : Constructor
SymbolPartList::SymbolPartList(Window* parent, int id, SymbolP symbol)
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize,
wxLC_REPORT | wxLC_NO_HEADER | wxLC_VIRTUAL | wxLC_EDIT_LABELS)
, SymbolView(symbol)
{
// Create image list
wxImageList* images = new wxImageList(16,16);
// NOTE: this is based on the order of the SymbolPartCombine enum!
images->Add(loadResourceImage(_("COMBINE_OR")));
images->Add(loadResourceImage(_("COMBINE_SUB")));
images->Add(loadResourceImage(_("COMBINE_AND")));
images->Add(loadResourceImage(_("COMBINE_XOR")));
images->Add(loadResourceImage(_("COMBINE_OVER")));
images->Add(loadResourceImage(_("COMBINE_BORDER")));
AssignImageList(images, wxIMAGE_LIST_SMALL);
// create columns
InsertColumn(0, _("Name"));
update();
}
// ----------------------------------------------------------------------------- : View events
void SymbolPartList::onSymbolChange() {
update();
}
void SymbolPartList::onAction(const Action& action) {
TYPE_CASE(action, ReorderSymbolPartsAction) {
if (selected == (long) action.partId1) {
selectItem((long) action.partId2);
} else if (selected == (long) action.partId2) {
selectItem((long) action.partId1);
}
}
TYPE_CASE_(action, SymbolPartListAction) {
update();
}
}
// ----------------------------------------------------------------------------- : Other
String SymbolPartList::OnGetItemText(long item, long col) const {
assert(col == 0);
return getPart(item)->name;
}
int SymbolPartList::OnGetItemImage(long item) const {
return getPart(item)->combine;
}
SymbolPartP SymbolPartList::getPart(long item) const {
return symbol->parts.at(item);
}
void SymbolPartList::selectItem(long item) {
selected = (long)item;
long count = GetItemCount();
for (long i = 0 ; i < count ; ++i) {
SetItemState(i, i == selected ? wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED : 0,
wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
}
}
void SymbolPartList::getSelectedParts(set<SymbolPartP>& sel) {
sel.clear();
long count = GetItemCount();
for (long i = 0 ; i < count ; ++ i) {
bool selected = GetItemState(i, wxLIST_STATE_SELECTED);
if (selected) {
sel.insert(symbol->parts.at(i));
}
}
}
void SymbolPartList::selectParts(const set<SymbolPartP>& sel) {
long count = GetItemCount();
for (long i = 0 ; i < count ; ++ i) {
// is that part selected?
bool selected = sel.find(symbol->parts.at(i)) != sel.end();
SetItemState(i, selected ? wxLIST_STATE_SELECTED : 0,
wxLIST_STATE_SELECTED);
}
}
void SymbolPartList::update() {
if (symbol->parts.empty()) {
// deleting all items requires a full refresh on win32
SetItemCount(0);
Refresh(true);
} else {
SetItemCount((long) symbol->parts.size() );
Refresh(false);
}
}
// ----------------------------------------------------------------------------- : Event handling
void SymbolPartList::onSelect(wxListEvent& ev) {
selected = ev.GetIndex();
ev.Skip();
}
void SymbolPartList::onDeselect(wxListEvent& ev) {
selected = -1;
ev.Skip();
}
void SymbolPartList::onLabelEdit(wxListEvent& ev){
symbol->actions.add(
new SymbolPartNameAction(getPart(ev.GetIndex()), ev.GetLabel())
);
}
void SymbolPartList::onSize(wxSizeEvent& ev) {
wxSize s = GetClientSize();
SetColumnWidth(0, s.GetWidth() - 2);
}
void SymbolPartList::onDrag(wxMouseEvent& ev) {
if (!ev.Dragging() || selected == -1) return;
// reorder the list of parts
int flags;
long item = HitTest(ev.GetPosition(), flags);
if (flags & wxLIST_HITTEST_ONITEM) {
if (item > 0) EnsureVisible(item - 1);
if (item < GetItemCount() - 1) EnsureVisible(item + 1);
if (item != selected) {
// swap the two items
symbol->actions.add(new ReorderSymbolPartsAction(*symbol, item, selected));
selectItem(item); // deselect all other items, to prevent 'lassoing' them
}
}
}
// ----------------------------------------------------------------------------- : Event table
BEGIN_EVENT_TABLE(SymbolPartList, wxListCtrl)
EVT_LIST_ITEM_SELECTED (wxID_ANY, SymbolPartList::onSelect)
EVT_LIST_ITEM_DESELECTED (wxID_ANY, SymbolPartList::onDeselect)
EVT_LIST_END_LABEL_EDIT (wxID_ANY, SymbolPartList::onLabelEdit)
EVT_SIZE ( SymbolPartList::onSize)
EVT_MOTION ( SymbolPartList::onDrag)
END_EVENT_TABLE ()
+72
View File
@@ -0,0 +1,72 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_PART_LIST
#define HEADER_GUI_SYMBOL_PART_LIST
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/symbol.hpp>
#include <wx/listctrl.h>
// ----------------------------------------------------------------------------- : SymbolPartList
// A list view of parts of a symbol
class SymbolPartList : public wxListCtrl, public SymbolView {
public:
SymbolPartList(Window* parent, int id, SymbolP symbol = SymbolP());
/// Update the list
void update();
/// Is there a selection?
inline bool hasSelection() const { return selected != -1; }
/// Return the last part that was selected
/** @pre hasSelection()
*/
inline SymbolPartP getSelection() const { return getPart(selected); }
/// Get a set of selected parts
void getSelectedParts(set<SymbolPartP>& sel);
/// Select the specified parts, and nothing else
void selectParts(const set<SymbolPartP>& sel);
/// Another symbol is being viewed
void onSymbolChange();
/// Event handler for changes to the symbol
virtual void onAction(const Action& a);
protected:
/// Get the text of an item
virtual String OnGetItemText(long item, long col) const;
/// Get the icon of an item
virtual int OnGetItemImage(long item) const;
private:
/// The selected item, or -1 if there is no selection
long selected;
/// Get a part from the symbol
SymbolPartP getPart(long item) const;
/// Select an item, also in the list control
/// Deselects all other items
void selectItem(long item);
// --------------------------------------------------- : Event handling
DECLARE_EVENT_TABLE();
void onSelect (wxListEvent& ev);
void onDeselect (wxListEvent& ev);
void onLabelEdit(wxListEvent& ev);
void onSize (wxSizeEvent& ev);
void onDrag (wxMouseEvent& ev);
};
// ----------------------------------------------------------------------------- : EOF
#endif
+529
View File
@@ -0,0 +1,529 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/symbol/point_editor.hpp>
#include <gui/symbol/window.hpp>
#include <gfx/bezier.hpp>
#include <data/action/symbol_part.hpp>
#include <util/window_id.hpp>
#include <util/error.hpp>
// ----------------------------------------------------------------------------- : SymbolPointEditor
SymbolPointEditor::SymbolPointEditor(SymbolControl* control, const SymbolPartP& part)
: SymbolEditorBase(control)
, part(part)
, selection(SELECTED_NONE)
, hovering(SELECTED_NONE)
// Load gui stock
, pointSelect(_("CUR_POINT"), wxBITMAP_TYPE_CUR_RESOURCE)
, pointAdd (_("CUR_POINT_ADD"), wxBITMAP_TYPE_CUR_RESOURCE)
, pointCurve (_("CUR_POINT_CURVE"),wxBITMAP_TYPE_CUR_RESOURCE)
, pointMove (_("CUR_POINT_MOVE"), wxBITMAP_TYPE_CUR_RESOURCE)
{
resetActions();
// // fix pen joins
// penHandleHover.join = wxJOIN_MITER;
// penMainHover.join = wxJOIN_MITER;
}
// ----------------------------------------------------------------------------- : Drawing
void SymbolPointEditor::draw(DC& dc) {
// highlight the part
control.highlightPart(dc, *part, HIGHLIGHT_BORDER);
// handles etc.
if (hovering == SELECTED_LINE) {
drawHoveredLine(dc);
}
drawHandles(dc);
if (hovering == SELECTED_NEW_POINT) {
drawNewPoint(dc);
}
}
void SymbolPointEditor::drawHoveredLine(DC& dc) {
BezierCurve c(*hoverLine1, *hoverLine2);
wxPoint prevPoint = control.rotation.tr(hoverLine1->pos);
for(int i = 1 ; i <= 100 ; ++i) {
// Draw 100 segments of the curve
double t = double(i)/100.0f;
wxPoint curPoint = control.rotation.tr(c.pointAt(t));
double selectPercent = 1.0 - 1.2 * sqrt(fabs(hoverLineT-t)); // amount to color
if (selectPercent > 0) {
// gradient color
Color color(
col(300 - 300 * selectPercent),
col(300 * selectPercent),
col(0)
);
dc.SetPen(wxPen(color, 3));
dc.DrawLine(prevPoint, curPoint);
}
prevPoint = curPoint;
}
}
void SymbolPointEditor::drawHandles(DC& dc) {
dc.SetPen(Color(0,0,128));
dc.SetBrush(Color(128,128,255));
for (int i = 0 ; (size_t)i < part->points.size() ; ++i) {
// determine which handles to draw
bool selected = pointSelected(*part->getPoint(i));
bool selBefore = selected || pointSelected(*part->getPoint(i-1));
bool selAfter = selected || pointSelected(*part->getPoint(i+1));
// and draw them
drawControlPoint(dc, *part->getPoint(i), selBefore, selAfter);
}
}
void SymbolPointEditor::drawNewPoint(DC& dc) {
dc.SetPen(*wxGREEN_PEN);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
wxPoint p = control.rotation.tr(newPoint);
drawHandleBox(dc, p.x, p.y, true);
}
void SymbolPointEditor::drawControlPoint(DC& dc, const ControlPoint& p, bool drawHandleBefore, bool drawHandleAfter) {
// Position
wxPoint p0 = control.rotation.tr(p.pos);
// Sub handles
if (drawHandleBefore || drawHandleAfter) {
dc.SetBrush(*wxTRANSPARENT_BRUSH);
// Before handle
if (drawHandleBefore && p.segmentBefore == SEGMENT_CURVE) {
wxPoint p1 = control.rotation.tr(p.pos + p.deltaBefore);
dc.SetPen(handlePen(PEN_LINE, p.lock));
dc.DrawLine(p0.x, p0.y, p1.x, p1.y);
dc.SetPen(handlePen(handleHovered(p, HANDLE_BEFORE) ? PEN_HOVER : PEN_NORMAL, p.lock));
drawHandleCircle(dc, p1.x, p1.y);
}
// After handle
if (drawHandleAfter && p.segmentAfter == SEGMENT_CURVE) {
wxPoint p1 = control.rotation.tr(p.pos + p.deltaAfter);
dc.SetPen(handlePen(PEN_LINE, p.lock));
dc.DrawLine(p0.x, p0.y, p1.x, p1.y);
dc.SetPen(handlePen(handleHovered(p, HANDLE_AFTER) ? PEN_HOVER : PEN_NORMAL, p.lock));
drawHandleCircle(dc, p1.x, p1.y);
}
}
// Main handle
// last, so it draws over lines to handles
bool selected = pointSelected(p);
wxPen hp(*wxBLACK, pointHovered(p) ? 2 : 1);
hp.SetJoin(wxJOIN_MITER);
dc.SetPen(hp);
dc.SetBrush(selected ? *wxGREEN_BRUSH : *wxTRANSPARENT_BRUSH);
drawHandleBox(dc, p0.x, p0.y, selected);
}
void SymbolPointEditor::drawHandleBox(DC& dc, UInt px, UInt py, bool active) {
dc.DrawRectangle(px - 3, py - 3, 7, 7);
if (!active) {
dc.SetPen(*wxWHITE_PEN);
dc.DrawRectangle(px - 2, py - 2, 5, 5);
}
}
void SymbolPointEditor::drawHandleCircle(DC& dc, UInt px, UInt py) {
dc.DrawCircle(px, py, 4);
}
wxPen SymbolPointEditor::handlePen(WhichPen p, LockMode lock) {
Color col;
if (lock == LOCK_FREE) col = Color(100, 100, 255);
if (lock == LOCK_DIR) col = Color(153, 0, 204);
if (lock == LOCK_SIZE) col = Color(204, 50, 50);
switch(p) {
case PEN_NORMAL: return wxPen(col);
case PEN_HOVER: return wxPen(col, 2);
case PEN_LINE: return wxPen(col, 1, wxDOT);
default: throw InternalError(_("SymbolPointEditor::handlePen"));
}
}
// ----------------------------------------------------------------------------- : UI
void SymbolPointEditor::initUI(wxToolBar* tb, wxMenuBar* mb) {
// Initialize toolbar
tb->AddSeparator();
tb->AddTool(ID_SEGMENT_LINE, _("Line"), Bitmap(_("TOOL_LINE")), wxNullBitmap, wxITEM_CHECK, _("To straigt line"), _("Makes the selected line straight"));
tb->AddTool(ID_SEGMENT_CURVE, _("Curve"), Bitmap(_("TOOL_CURVE")), wxNullBitmap, wxITEM_CHECK, _("To curve"), _("Makes the selected line curved"));
tb->AddSeparator();
tb->AddTool(ID_LOCK_FREE, _("Free"), Bitmap(_("TOOL_LOCK_FREE")), wxNullBitmap, wxITEM_CHECK, _("Unlock node"), _("Allows the two control points on the node to be moved freely"));
tb->AddTool(ID_LOCK_DIR, _("Smooth"), Bitmap(_("TOOL_LOCK_DIR")), wxNullBitmap, wxITEM_CHECK, _("Make node smooth"), _("Makes the selected node smooth by placing the two control points opposite each other"));
tb->AddTool(ID_LOCK_SIZE, _("Symmetric"), Bitmap(_("TOOL_LOCK_SIZE")), wxNullBitmap, wxITEM_CHECK, _("Make node symmetric"), _("Makes the selected node symetric"));
tb->Realize();
// TODO : menu bar
//mb->Insert(2, curveMenu, _("&Curve"))
}
void SymbolPointEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
tb->DeleteTool(ID_SEGMENT_LINE);
tb->DeleteTool(ID_SEGMENT_CURVE);
tb->DeleteTool(ID_LOCK_FREE);
tb->DeleteTool(ID_LOCK_DIR);
tb->DeleteTool(ID_LOCK_SIZE);
// HACK: hardcoded size of rest of toolbar
tb->DeleteToolByPos(4); // delete separator
tb->DeleteToolByPos(4); // delete separator
// TODO : menu bar
//mb->Remove(2)
}
void SymbolPointEditor::onUpdateUI(wxUpdateUIEvent& ev) {
// enable
bool enabled = false, checked = false;
switch (ev.GetId()) {
case ID_SEGMENT_LINE: case ID_SEGMENT_CURVE:
enabled = selection == SELECTED_LINE;
break;
case ID_LOCK_FREE: case ID_LOCK_DIR: case ID_LOCK_SIZE:
enabled = selection == SELECTED_POINTS &&
selectedPoints.size() == 1 &&
(*selectedPoints.begin())->segmentBefore == SEGMENT_CURVE &&
(*selectedPoints.begin())->segmentAfter == SEGMENT_CURVE;
break;
default:
ev.Enable(false); // we don't know this item
return;
}
// check
if (enabled) {
switch (ev.GetId()) {
case ID_SEGMENT_LINE: case ID_SEGMENT_CURVE:
checked = selectedLine1->segmentAfter == ev.GetId() - ID_SEGMENT;
break;
case ID_LOCK_FREE: case ID_LOCK_DIR: case ID_LOCK_SIZE:
checked = (*selectedPoints.begin())->lock == ev.GetId() - ID_LOCK;
break;
}
}
ev.Enable(enabled);
ev.Check(checked);
}
void SymbolPointEditor::onCommand(int id) {
switch (id) {
case ID_SEGMENT_LINE: case ID_SEGMENT_CURVE:
onChangeSegment( static_cast<SegmentMode>(id - ID_SEGMENT) );
case ID_LOCK_FREE: case ID_LOCK_DIR: case ID_LOCK_SIZE:
onChangeLock( static_cast<LockMode>(id - ID_LOCK) );
}
}
int SymbolPointEditor::modeToolId() { return ID_MODE_POINTS; }
// ----------------------------------------------------------------------------- : Mouse events
void SymbolPointEditor::onLeftDown(const Vector2D& pos, wxMouseEvent& ev) {
SelectedHandle handle = findHandle(pos);
if (handle.handle) {
selectHandle(handle, ev);
} else if (hovering == SELECTED_LINE) {
selectLine(ev);
} else if (hovering == SELECTED_NEW_POINT) {
selectLine(ev);
} else {
selectNothing();
}
// update window
control.Refresh(false);
}
void SymbolPointEditor::onLeftUp(const Vector2D& pos, wxMouseEvent& ev) {
// Left up => finalize all actions, new events start new actions
resetActions();
}
void SymbolPointEditor::onLeftDClick(const Vector2D& pos, wxMouseEvent& ev) {
findHoveredItem(pos, false);
if (hovering == SELECTED_NEW_POINT) {
// Add point
ControlPointAddAction* act = new ControlPointAddAction(part, hoverLine1Idx, hoverLineT);
getSymbol()->actions.add(act);
// select the new point
selectPoint(act->getNewPoint(), false);
selection = SELECTED_POINTS;
} else if (hovering == SELECTED_HANDLE && hoverHandle.handle == HANDLE_MAIN) { //%%%%%%% ||/&&
// Delete point
selectedPoints.clear();
selectPoint(hoverHandle.point, false);
getSymbol()->actions.add(controlPointRemoveAction(part, selectedPoints));
selectedPoints.clear();
selection = SELECTED_NONE;
}
// refresh
findHoveredItem(pos, false);
control.Refresh(false);
}
void SymbolPointEditor::onMouseMove(const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
// Moving the mouse without dragging => select a point/handle
findHoveredItem(to, ev.AltDown());
control.Refresh(false);
}
void SymbolPointEditor::onMouseDrag(const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
Vector2D delta = to - from;
if (selection == SELECTED_LINE && ev.AltDown()) {
// Drag the curve
if (controlPointMoveAction) controlPointMoveAction = 0;
if (!curveDragAction) {
curveDragAction = new CurveDragAction(selectedLine1, selectedLine2);
getSymbol()->actions.add(curveDragAction);
}
curveDragAction->move(delta, selectedLineT);
control.Refresh(false);
} else if (selection == SELECTED_POINTS || selection == SELECTED_LINE) {
// Move all selected points
if (curveDragAction) curveDragAction = 0;
if (!controlPointMoveAction) {
// create action we can add this movement to
controlPointMoveAction = new ControlPointMoveAction(selectedPoints);
getSymbol()->actions.add(controlPointMoveAction);
}
controlPointMoveAction->constrain = ev.ControlDown(); // ctrl constrains
controlPointMoveAction->move(delta);
newPoint += delta;
control.Refresh(false);
} else if (selection == SELECTED_HANDLE) {
// Move the selected handle
if (!handleMoveAction) {
handleMoveAction = new HandleMoveAction(selectedHandle);
getSymbol()->actions.add(handleMoveAction);
}
handleMoveAction->constrain = ev.ControlDown(); // ctrl constrains
handleMoveAction->move(delta);
control.Refresh(false);
}
}
// ----------------------------------------------------------------------------- : Other events
void SymbolPointEditor::onKeyChange(wxKeyEvent& ev) {
if (ev.GetKeyCode() == WXK_ALT && (hovering == SELECTED_LINE || hovering == SELECTED_NEW_POINT)) {
if (ev.AltDown()) {
hovering = SELECTED_LINE;
control.SetCursor(pointCurve);
SetStatusText(_("Drag to move curve"));
} else {
hovering = SELECTED_NEW_POINT;
control.SetCursor(pointAdd);
SetStatusText(_("Alt + drag to move curve; double click to add control point on this line"));
}
control.Refresh(false);
} else if (ev.GetKeyCode() == WXK_CONTROL) {
// constrain changed
if (controlPointMoveAction) {
controlPointMoveAction->constrain = ev.ControlDown();
controlPointMoveAction->move(Vector2D()); //refresh action
control.Refresh(false);
} else if (handleMoveAction) {
handleMoveAction->constrain = ev.ControlDown();
handleMoveAction->move(Vector2D()); //refresh action
control.Refresh(false);
}
}
}
void SymbolPointEditor::onChar(wxKeyEvent& ev) {
if (ev.GetKeyCode() == WXK_DELETE) {
deleteSelection();
} else {
ev.Skip();
}
}
bool SymbolPointEditor::isEditing() {
return handleMoveAction || controlPointMoveAction || curveDragAction;
}
// ----------------------------------------------------------------------------- : Selection
void SymbolPointEditor::selectNothing() {
selection = SELECTED_NONE;
selectedPoints.clear();
}
void SymbolPointEditor::selectPoint(const ControlPointP& point, bool toggle) {
set<ControlPointP>::iterator inSet = selectedPoints.find(point);
if (toggle) {
if (inSet == selectedPoints.end()) {
selectedPoints.insert(point);
} else {
selectedPoints.erase(inSet);
}
} else {
if (inSet == selectedPoints.end()) {
selectedPoints.clear();
selectedPoints.insert(point);
}
}
}
void SymbolPointEditor::selectHandle(const SelectedHandle& h, const wxMouseEvent& keystate) {
if (h.handle == HANDLE_MAIN) {
selection = SELECTED_POINTS;
selectPoint(h.point, keystate.ShiftDown());
} else {
selection = SELECTED_HANDLE;
selectedHandle = h;
}
}
void SymbolPointEditor::selectLine(const wxMouseEvent& keystate) {
selection = SELECTED_LINE;
selectedLine1 = hoverLine1;
selectedLine2 = hoverLine2;
selectedLineT = hoverLineT;
if (!keystate.ShiftDown()) selectedPoints.clear();
selectPoint(selectedLine1, true);
selectPoint(selectedLine2, true);
}
bool SymbolPointEditor::pointSelected(const ControlPointP& pnt) {
return selectedPoints.find(pnt) != selectedPoints.end();
}
bool SymbolPointEditor::pointSelected(const ControlPoint& pnt) {
FOR_EACH(s, selectedPoints) {
if (s.get() == &pnt) return true;
}
return false;
}
bool SymbolPointEditor::pointHovered(const ControlPointP& pnt) {
return handleHovered(pnt, HANDLE_MAIN);
}
bool SymbolPointEditor::pointHovered(const ControlPoint& pnt) {
return handleHovered(pnt, HANDLE_MAIN);
}
bool SymbolPointEditor::handleHovered(const ControlPointP& pnt, WhichHandle wh) {
return hovering == SELECTED_HANDLE && hoverHandle.point == pnt && hoverHandle.handle == wh;
}
bool SymbolPointEditor::handleHovered(const ControlPoint& pnt, WhichHandle wh) {
return hovering == SELECTED_HANDLE && hoverHandle.point && hoverHandle.point.get() == &pnt && hoverHandle.handle == wh;
}
// ----------------------------------------------------------------------------- : Actions
void SymbolPointEditor::resetActions() {
handleMoveAction = nullptr;
controlPointMoveAction = nullptr;
curveDragAction = nullptr;
}
void SymbolPointEditor::deleteSelection() {
if (!selectedPoints.empty()) {
getSymbol()->actions.add(controlPointRemoveAction(part, selectedPoints));
selectedPoints.clear();
resetActions();
control.Refresh(false);
}
}
void SymbolPointEditor::onChangeSegment(SegmentMode mode) {
assert(selectedLine1);
assert(selectedLine2);
if (selectedLine1->segmentAfter == mode) return;
getSymbol()->actions.add(new SegmentModeAction(selectedLine1, selectedLine2, mode));
control.Refresh(false);
}
void SymbolPointEditor::onChangeLock(LockMode mode) {
getSymbol()->actions.add(new LockModeAction(*selectedPoints.begin(), mode));
control.Refresh(false);
}
// ----------------------------------------------------------------------------- : Finding items
void SymbolPointEditor::findHoveredItem(const Vector2D& pos, bool altDown) {
// is there a point currently under the cursor?
hoverHandle = findHandle(pos);
// change cursor and statusbar if point is under it
if (hoverHandle.handle) {
hovering = SELECTED_HANDLE;
control.SetCursor(pointMove);
SetStatusText(_("Click and drag to move control point"));
} else {
// Not on a point or handle, maybe the cursor is on a curve
if (checkPosOnCurve(pos)) {
if (altDown) {
hovering = SELECTED_LINE;
control.SetCursor(pointCurve);
SetStatusText(_("Drag to move curve"));
} else {
hovering = SELECTED_NEW_POINT;
control.SetCursor(pointAdd);
SetStatusText(_("Alt + drag to move curve; double click to add control point on this line"));
}
} else {
hovering = SELECTED_NONE;
control.SetCursor(*wxSTANDARD_CURSOR);
SetStatusText(_(""));
}
}
}
bool SymbolPointEditor::checkPosOnCurve(const Vector2D& pos) {
double range = control.rotation.trInvS(3); // less then 3 pixels away is still a hit
size_t size = part->points.size();
for(int i = 0 ; (size_t)i < size ; ++i) {
// Curve between these lines
hoverLine1 = part->getPoint(i);
hoverLine2 = part->getPoint(i + 1);
if (posOnSegment(pos, range, *hoverLine1, *hoverLine2, newPoint, hoverLineT)) {
// mouse is on this line
hoverLine1Idx = i;
return true;
}
}
return false;
}
SelectedHandle SymbolPointEditor::findHandle(const Vector2D& pos) {
double range = control.rotation.trInvS(3); // less then 3 pixels away is still a hit
// Is there a main handle there?
FOR_EACH(p, part->points) {
if (inRange(p->pos, pos, range)) {
// point is at pos
return SelectedHandle(p, HANDLE_MAIN);
}
}
// Is there a sub handle there?
// only check visible handles
for (int i = 0 ; (size_t)i < part->points.size() ; ++i) {
ControlPointP p = part->getPoint(i);
bool sel = pointSelected(p);
bool before = sel || pointSelected(part->getPoint(i-1)); // are the handles visible?
bool after = sel || pointSelected(part->getPoint(i+1));
if (before && p->segmentBefore == SEGMENT_CURVE) {
if (inRange(p->pos + p->deltaBefore, pos, range)) {
return SelectedHandle(p, HANDLE_BEFORE);
}
}
if (after && p->segmentAfter == SEGMENT_CURVE) {
if (inRange(p->pos + p->deltaAfter, pos, range)) {
return SelectedHandle(p, HANDLE_AFTER);
}
}
}
// Nothing found
return HANDLE_NONE;
}
bool SymbolPointEditor::inRange(const Vector2D& a, const Vector2D& b, double range) {
return abs(a.x - b.x) <= range &&
abs(a.y - b.y) <= range;
}
+169
View File
@@ -0,0 +1,169 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_POINT_EDITOR
#define HEADER_GUI_SYMBOL_POINT_EDITOR
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <gui/symbol/editor.hpp>
class HandleMoveAction;
class ControlPointMoveAction;
class CurveDragAction;
// ----------------------------------------------------------------------------- : SymbolPointEditor
// Symbol editor for editing control points and handles
class SymbolPointEditor : public SymbolEditorBase {
public:
SymbolPointEditor(SymbolControl* control, const SymbolPartP& part);
// --------------------------------------------------- : Drawing
virtual void draw(DC& dc);
private:
/// Draws a gradient on the selected line to indicate curve dragging
void drawHoveredLine(DC& dc);
/// Draw all handles belonging to selected points
void drawHandles(DC& dc);
/// Draws the point to be inserted
void drawNewPoint(DC& dc);
/// Draw a single control point
void drawControlPoint(DC& dc, const ControlPoint& p, bool drawHandleBefore, bool drawHandleAfter);
/// Draws a handle as a box
void drawHandleBox(DC& dc, UInt px, UInt py, bool active);
/// Draws a handle as a circle
void drawHandleCircle(DC& dc, UInt px, UInt py);
enum WhichPen {
PEN_NORMAL, //^ Pen for normal handles
PEN_HOVER, //^ Pen for hovered handles
PEN_LINE, //^ Pen for the line to handles
PEN_MAIN, //^ Pen for the main handle
PEN_NEW_POINT //^ Pen for the new point
};
/// Retrieve a pen for the drawing of parts of handles
wxPen handlePen(WhichPen p, LockMode lock);
/// Retrieve a pen for the drawing of other things
wxPen otherPen(WhichPen p);
public:
// --------------------------------------------------- : UI
virtual void initUI(wxToolBar* tb, wxMenuBar* mb);
virtual void destroyUI(wxToolBar* tb, wxMenuBar* mb);
virtual void onUpdateUI(wxUpdateUIEvent& e);
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 onLeftDClick(const Vector2D& pos, wxMouseEvent& ev);
virtual void onMouseMove(const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
virtual void onMouseDrag(const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
// --------------------------------------------------- : Other events
virtual void onKeyChange(wxKeyEvent& ev);
virtual void onChar(wxKeyEvent& ev);
virtual bool isEditing();
private:
// --------------------------------------------------- : Data
// The symbol part we are editing
SymbolPartP part;
// Actions in progress
// All are owned by the action stack, or they are 0
HandleMoveAction* handleMoveAction;
ControlPointMoveAction* controlPointMoveAction;
CurveDragAction* curveDragAction;
// Selection
enum Selection {
SELECTED_NONE, //^ no selection
SELECTED_POINTS, //^ some points are selected
SELECTED_HANDLE, //^ a handle is selected
SELECTED_LINE, //^ a line is selected
SELECTED_NEW_POINT //^ a new point on a line (used for hovering)
};
Selection selection;
// points
set<ControlPointP> selectedPoints;
// handle
SelectedHandle selectedHandle;
// line
ControlPointP selectedLine1, selectedLine2; // selected the line between these points
double selectedLineT; // time on the line of the selection
// Mouse feedback
Selection hovering;
// handle
SelectedHandle hoverHandle; // the handle currently under the cursor
// new point
Vector2D newPoint;
// line
ControlPointP hoverLine1, hoverLine2; // hovering on the line between these points
double hoverLineT;
int hoverLine1Idx; // index of hoverLine1 in the list of points
// Gui stock
wxBitmap background;
wxCursor pointSelect, pointAdd, pointCurve, pointMove;
// --------------------------------------------------- : Selection
/// Clears the selection
void selectNothing();
/// Select a point, if toggle then toggles the selection of the point
void selectPoint(const ControlPointP& point, bool toggle);
void selectHandle(const SelectedHandle& h, const wxMouseEvent& keystate);
void selectLine(const wxMouseEvent& keystate);
/// Is a point selected?
bool pointSelected(const ControlPointP& pnt);
bool pointSelected(const ControlPoint& pnt);
/// Is the mouse pointer above a point?
bool pointHovered(const ControlPointP& pnt);
bool pointHovered(const ControlPoint& pnt);
/// Is the mouse pointer above a handle of a point?
bool handleHovered(const ControlPointP& pnt, WhichHandle wh);
bool handleHovered(const ControlPoint& pnt, WhichHandle wh);
// --------------------------------------------------- : Actions
// Finalize actions; new events start new actions
void resetActions();
void deleteSelection();
void onChangeSegment(SegmentMode mode);
void onChangeLock (LockMode mode);
// --------------------------------------------------- : Finding items
/// Finds the item that is currently being hovered, stores the results in hover*
void findHoveredItem(const Vector2D& pos, bool altDown);
/// Is the specified position on a curve?
/// If so, sets hoverLine*, and set hovering=hoveringLine
bool checkPosOnCurve(const Vector2D& pos);
/// Finds a handle at or near pos
SelectedHandle findHandle(const Vector2D& pos);
/// Is the manhatan distance between two points <= range?
bool inRange(const Vector2D& a, const Vector2D& b, double range);
};
// ----------------------------------------------------------------------------- : EOF
#endif
+424
View File
@@ -0,0 +1,424 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/symbol/select_editor.hpp>
#include <gui/symbol/window.hpp>
#include <gui/util.hpp>
#include <util/window_id.hpp>
#include <data/action/symbol.hpp>
#include <gfx/gfx.hpp>
// ----------------------------------------------------------------------------- : SymbolSelectEditor
SymbolSelectEditor::SymbolSelectEditor(SymbolControl* control, bool rotate)
: SymbolEditorBase(control)
, rotate(rotate)
, cursorRotate(_("CUR_ROTATE"), wxBITMAP_TYPE_CUR_RESOURCE)
, cursorShearX(_("CUR_SHEAR_X"), wxBITMAP_TYPE_CUR_RESOURCE)
, cursorShearY(_("CUR_SHEAR_Y"), wxBITMAP_TYPE_CUR_RESOURCE)
{
// Load resource images
Image rot = loadResourceImage(_("HANDLE_ROTATE"));
handleRotateTL = wxBitmap(rot);
handleRotateTR = wxBitmap(rotateImageBy(rot,90));
handleRotateBR = wxBitmap(rotateImageBy(rot,180));
handleRotateBL = wxBitmap(rotateImageBy(rot,270));
Image shear = loadResourceImage(_("HANDLE_SHEAR_X"));
handleShearX = wxBitmap(shear);
handleShearY = wxBitmap(rotateImageBy(shear,90));
handleCenter = wxBitmap(loadResourceImage(_("HANDLE_CENTER")));
// Make sure all parts have updated bounds
FOR_EACH(p, getSymbol()->parts) {
p->calculateBounds();
}
resetActions();
}
// ----------------------------------------------------------------------------- : Drawing
void SymbolSelectEditor::draw(DC& dc) {
// highlight selected parts
FOR_EACH(p, control.selectedParts) {
control.highlightPart(dc, *p, HIGHLIGHT_INTERIOR);
}
// highlight the part under the cursor
if (highlightPart) {
control.highlightPart(dc, *highlightPart, HIGHLIGHT_BORDER);
}
// draw handles
drawHandles(dc);
}
void SymbolSelectEditor::drawHandles(DC& dc) {
if (control.selectedParts.empty()) return;
if (rotateAction) return; // not when rotating
updateBoundingBox();
// Draw handles on all sides
for (int dx = -1 ; dx <= 1 ; ++dx) {
for (int dy = -1 ; dy <= 1 ; ++dy) {
if (dx != 0 || dy != 0) {
// no handle in the center
drawHandle(dc, dx, dy);
}
}
}
// Draw rotation center?
if (rotate) {
drawRotationCenter(dc, center);
}
}
void SymbolSelectEditor::drawHandle(DC& dc, int dx, int dy) {
wxPoint p = control.rotation.tr(handlePos(dx, dy));
p.x += 4 * dx;
p.y += 4 * dy;
if (rotate) {
// rotate or shear handle
if (dx == 0) dc.DrawBitmap(handleShearX, p.x - 10, p.y - 3 - (dy < 0 ? 1 : 0));
if (dy == 0) dc.DrawBitmap(handleShearY, p.x - 3 - (dx < 0 ? 1 : 0), p.y - 10);
else {
// rotate
if (dx == -1 && dy == -1) dc.DrawBitmap(handleRotateTL, p.x - 5, p.y - 5);
if (dx == -1 && dy == 1) dc.DrawBitmap(handleRotateTR, p.x - 5, p.y - 11);
if (dx == 1 && dy == -1) dc.DrawBitmap(handleRotateBL, p.x - 11, p.y - 5);
if (dx == 1 && dy == 1) dc.DrawBitmap(handleRotateBR, p.x - 11, p.y - 11);
}
} else {
// resize handle
dc.SetBrush(*wxBLUE_BRUSH);
dc.SetPen( *wxWHITE_PEN);
dc.DrawRectangle(p.x - 3, p.y - 3, 6, 6);
}
}
void SymbolSelectEditor::drawRotationCenter(DC& dc, const Vector2D& pos) {
wxPoint p = control.rotation.tr(pos);
dc.DrawBitmap(handleCenter, p.x - 9, p.y - 9);
}
// ----------------------------------------------------------------------------- : UI
void SymbolSelectEditor::initUI(wxToolBar* tb, wxMenuBar* mb) {
tb->AddSeparator();
tb->AddTool(ID_PART_MERGE, _("Merge"), loadResourceImage(_("COMBINE_OR")), wxNullBitmap, wxITEM_CHECK, _("Merge with shapes below"), _("Merges this shape with those below it"));
tb->AddTool(ID_PART_SUBTRACT, _("Subtract"), loadResourceImage(_("COMBINE_SUB_DARK")), wxNullBitmap, wxITEM_CHECK, _("Subtract from shapes below"), _("Subtracts this shape from shapes below it, leaves only the area in that shape that is not in this shape"));
tb->AddTool(ID_PART_INTERSECTION, _("Intersect"), loadResourceImage(_("COMBINE_AND_DARK")), wxNullBitmap, wxITEM_CHECK, _("Intersect with shapes below"), _("Intersects this shape with shapes below it, leaves only the area in both shapes"));
// note: difference doesn't work (yet)
tb->AddTool(ID_PART_OVERLAP, _("Overlap"), loadResourceImage(_("COMBINE_OVER")), wxNullBitmap, wxITEM_CHECK, _("Place above other shapes"), _("Place this shape, and its border above shapes below it"));
tb->AddTool(ID_PART_BORDER, _("Border"), loadResourceImage(_("COMBINE_BORDER")), wxNullBitmap, wxITEM_CHECK, _("Draw as a border"), _("Draws this shape as a border"));
tb->Realize();
}
void SymbolSelectEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
tb->DeleteTool(ID_PART_MERGE);
tb->DeleteTool(ID_PART_SUBTRACT);
tb->DeleteTool(ID_PART_INTERSECTION);
tb->DeleteTool(ID_PART_OVERLAP);
tb->DeleteTool(ID_PART_BORDER);
// HACK: hardcoded size of rest of toolbar
tb->DeleteToolByPos(4); // delete separator
}
void SymbolSelectEditor::onUpdateUI(wxUpdateUIEvent& ev) {
if (ev.GetId() >= ID_PART && ev.GetId() < ID_PART_MAX) {
if (control.selectedParts.empty()) {
ev.Check(false);
ev.Enable(false);
} else {
ev.Enable(true);
bool check = true;
FOR_EACH(p, control.selectedParts) {
if (p->combine != ev.GetId() - ID_PART) {
check = false;
break;
}
}
ev.Check(check);
}
} else if (ev.GetId() == ID_EDIT_DUPLICATE) {
ev.Enable(!control.selectedParts.empty());
} else {
ev.Enable(false); // we don't know about this item
}
}
void SymbolSelectEditor::onCommand(int id) {
if (id >= ID_PART && id < ID_PART_MAX) {
// change combine mode
getSymbol()->actions.add(new CombiningModeAction(
control.selectedParts,
static_cast<SymbolPartCombine>(id - ID_PART)
));
control.Refresh(false);
} else if (id == ID_EDIT_DUPLICATE && !isEditing()) {
// duplicate selection, not when dragging
DuplicateSymbolPartsAction* action = new DuplicateSymbolPartsAction(
*getSymbol(), control.selectedParts
);
getSymbol()->actions.add(action);
control.Refresh(false);
}
}
int SymbolSelectEditor::modeToolId() {
return rotate ? ID_MODE_ROTATE : ID_MODE_SELECT;
}
// ----------------------------------------------------------------------------- : Mouse Events
void SymbolSelectEditor::onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {
}
void SymbolSelectEditor::onLeftUp (const Vector2D& pos, wxMouseEvent& ev) {
if (isEditing()) {
// stop editing
resetActions();
} else {
// mouse not moved, change selection
// Are we on a handle?
int dx, dy;
if (onAnyHandle(pos, &dx, &dy)) return; // don't change the selection
// Select the part under the cursor
SymbolPartP part = findPart(pos);
if (part) {
if (ev.ShiftDown()) {
// toggle selection
set<SymbolPartP>::iterator it = control.selectedParts.find(part);
if (it != control.selectedParts.end()) {
control.selectedParts.erase(it);
} else {
control.selectedParts.insert(part);
}
} else {
if (control.selectedParts.find(part) != control.selectedParts.end()) {
// already selected, don't change selection
// instead switch between rotate and resize mode
rotate = !rotate;
} else {
// select the part under the cursor
control.selectedParts.clear();
control.selectedParts.insert(part);
}
}
} else if (!ev.ShiftDown()) {
// select nothing
control.selectedParts.clear();
}
// selection has changed
updateBoundingBox();
control.signalSelectionChange();
}
control.Refresh(false);
}
void SymbolSelectEditor::onLeftDClick(const Vector2D& pos, wxMouseEvent& ev) {
// start editing the points of the clicked part
highlightPart = findPart(pos);
if (highlightPart) {
control.activatePart(highlightPart);
}
}
void SymbolSelectEditor::onMouseMove (const Vector2D& from, const Vector2D& to, wxMouseEvent& e) {
// can we highlight a part?
highlightPart = findPart(to);
// are we on a handle?
int dx, dy;
if (!control.selectedParts.empty() && onAnyHandle(to, &dx, &dy)) {
// we are on a handle, don't highlight
highlightPart = SymbolPartP();
if (rotate) {
// shear or rotating?
if (dx == 0 || dy == 0) {
SetStatusText(String(_("Drag to shear selected shape")) + (control.selectedParts.size() > 1 ? _("s") : _("")));
control.SetCursor(dx == 0 ? cursorShearX : cursorShearY);
} else {
SetStatusText(String(_("Drag to rotate selected shape")) + (control.selectedParts.size() > 1 ? _("s") : _("")) + _(", Ctrl constrains angle to multiples of 15 degrees"));
control.SetCursor(cursorRotate);
}
} else {
SetStatusText(String(_("Drag to resize selected shape")) + (control.selectedParts.size() > 1 ? _("s") : _("")) + _(", Ctrl constrains size"));
// what cursor to use?
if (dx == dy) control.SetCursor(wxCURSOR_SIZENWSE);
else if (dx == -dy) control.SetCursor(wxCURSOR_SIZENESW);
else if (dx == 0) control.SetCursor(wxCURSOR_SIZENS);
else if (dy == 0) control.SetCursor(wxCURSOR_SIZEWE);
}
} else {
if (highlightPart) {
SetStatusText(_("Click to select shape, drag to move shape, double click to edit shape"));
} else {
SetStatusText(_(""));
}
control.SetCursor(*wxSTANDARD_CURSOR);
}
control.Refresh(false);
}
void SymbolSelectEditor::onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
if (control.selectedParts.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
if (onAnyHandle(from, &scaleX, &scaleY)) {
if (rotate) {
if (scaleX == 0 || scaleY == 0) {
// shear, center/fixed point on the opposite side
shearAction = new SymbolPartShearAction(control.selectedParts, handlePos(-scaleX, -scaleY));
getSymbol()->actions.add(shearAction);
} else {
// rotate
rotateAction = new SymbolPartRotateAction(control.selectedParts, center);
getSymbol()->actions.add(rotateAction);
startAngle = angleTo(to);
}
} else {
// we are on a handle; start scaling
scaleAction = new SymbolPartScaleAction(control.selectedParts, scaleX, scaleY);
getSymbol()->actions.add(scaleAction);
}
} else {
// move
moveAction = new SymbolPartMoveAction(control.selectedParts);
getSymbol()->actions.add(moveAction);
}
}
// now update the action
if (moveAction) {
// move the selected parts
moveAction->constrain = ev.ControlDown();
moveAction->move(to - from);
} else if (scaleAction) {
// scale the selected parts
Vector2D delta = to-from;
Vector2D dMin, dMax;
if (scaleX == -1) dMin.x = delta.x;
if (scaleX == 1) dMax.x = delta.x;
if (scaleY == -1) dMin.y = delta.y;
if (scaleY == 1) dMax.y = delta.y;
scaleAction->constrain = ev.ControlDown();
scaleAction->move(dMin, dMax);
} else if (rotateAction) {
// rotate the selected parts
double angle = angleTo(to);
rotateAction->constrain = ev.ControlDown();
rotateAction->rotateTo(startAngle - angle);
} else if (shearAction) {
// shear the selected parts
Vector2D delta = to-from;
delta = delta.mul(Vector2D(scaleY, scaleX));
delta = delta.div(maxV - minV);
shearAction->constrain = ev.ControlDown();
shearAction->move(delta);
}
control.Refresh(false);
}
// ----------------------------------------------------------------------------- : Key Events
void SymbolSelectEditor::onKeyChange (wxKeyEvent& ev) {
if (ev.GetKeyCode() == WXK_CONTROL) {
// changed constrains
if (moveAction) {
moveAction->constrain = ev.ControlDown();
moveAction->move(Vector2D()); // apply constrains
control.Refresh(false);
} else if (scaleAction) {
// only allow constrained scaling in diagonal direction
scaleAction->constrain = ev.ControlDown();
scaleAction->update(); // apply constrains
control.Refresh(false);
} else if (rotateAction) {
rotateAction->constrain = ev.ControlDown();
rotateAction->rotateBy(0); // apply constrains
control.Refresh(false);
}
}
}
void SymbolSelectEditor::onChar(wxKeyEvent& ev) {
if (ev.GetKeyCode() == WXK_DELETE) {
// delete selected parts
getSymbol()->actions.add(new RemoveSymbolPartsAction(*getSymbol(), control.selectedParts));
control.selectedParts.clear();
resetActions();
control.Refresh(false);
} else {
ev.Skip();
}
}
bool SymbolSelectEditor::isEditing() {
return moveAction || scaleAction || rotateAction || shearAction;
}
// ----------------------------------------------------------------------------- : Other
Vector2D SymbolSelectEditor::handlePos(int dx, int dy) {
return Vector2D(
0.5 * (maxV.x + minV.x + dx * (maxV.x - minV.x)),
0.5 * (maxV.y + minV.y + dy * (maxV.y - minV.y))
);
}
bool SymbolSelectEditor::onHandle(const Vector2D& mpos, int dx, int dy) {
wxPoint p = control.rotation.tr(handlePos(dx, dy));
wxPoint mp = control.rotation.tr(mpos);
p.x = p.x + 4 * dx;
p.y = p.y + 4 * dy;
return mp.x >= p.x - 4 && mp.x < p.x + 4 &&
mp.y >= p.y - 4 && mp.y < p.y + 4;
}
bool SymbolSelectEditor::onAnyHandle(const Vector2D& mpos, int* dxOut, int* dyOut) {
for (int dx = -1 ; dx < 1 ; ++dx) {
for (int dy = -1 ; dy < 1 ; ++dy) {
if ((dx != 0 || dy != 0) && onHandle(mpos, dx, dy)) { // (0,0) == center, not a handle
*dxOut = dx;
*dyOut = dy;
return true;
}
}
}
return false;
}
double SymbolSelectEditor::angleTo(const Vector2D& pos) {
return atan2(center.x - pos.x, center.y - pos.y);
}
SymbolPartP SymbolSelectEditor::findPart(const Vector2D& pos) {
FOR_EACH(p, getSymbol()->parts) {
if (pointInPart(pos, *p)) return p;
}
return SymbolPartP();
}
void SymbolSelectEditor::updateBoundingBox() {
// Find min and max coordinates
minV = Vector2D::infinity();
maxV = -Vector2D::infinity();
FOR_EACH(p, control.selectedParts) {
minV = piecewise_min(minV, p->minPos);
maxV = piecewise_max(maxV, p->maxPos);
}
// Find rotation center
center = Vector2D(0,0);
FOR_EACH(p, control.selectedParts) {
Vector2D size = p->maxPos - p->minPos;
size = size.mul(p->rotationCenter);
center += p->minPos + size;
}
center /= control.selectedParts.size();
}
void SymbolSelectEditor::resetActions() {
moveAction = nullptr;
scaleAction = nullptr;
rotateAction = nullptr;
shearAction = nullptr;
}
+117
View File
@@ -0,0 +1,117 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_SELECT_EDITOR
#define HEADER_GUI_SYMBOL_SELECT_EDITOR
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <gui/symbol/editor.hpp>
DECLARE_POINTER_TYPE(SymbolPartMoveAction);
DECLARE_POINTER_TYPE(SymbolPartScaleAction);
DECLARE_POINTER_TYPE(SymbolPartRotateAction);
DECLARE_POINTER_TYPE(SymbolPartShearAction);
// ----------------------------------------------------------------------------- : SymbolSelectEditor
/// Editor that allows the user to select symbol parts
class SymbolSelectEditor : public SymbolEditorBase {
public:
SymbolSelectEditor(SymbolControl* control, bool rotate);
// --------------------------------------------------- : Drawing
virtual void draw(DC& dc);
private:
/// Draw handles on all sides
void drawHandles(DC& dc);
/// Draw a handle, dx and dy indicate the side, can be {-1,0,1}
void drawHandle(DC& dc, int dx, int dy);
/// Draw the rotation center
void drawRotationCenter(DC& dc, const Vector2D& pos);
public:
// --------------------------------------------------- : UI
virtual void initUI (wxToolBar* tb, wxMenuBar* mb);
virtual void destroyUI(wxToolBar* tb, wxMenuBar* mb);
virtual void onUpdateUI(wxUpdateUIEvent& e);
virtual void onCommand(int id);
virtual int modeToolId();
// --------------------------------------------------- : Mouse events
virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev);
virtual void onLeftDClick (const Vector2D& pos, wxMouseEvent& ev);
virtual void onLeftUp (const Vector2D& pos, wxMouseEvent& ev);
virtual void onMouseMove (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
virtual void onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
// --------------------------------------------------- : Other events
virtual void onKeyChange (wxKeyEvent& ev);
virtual void onChar (wxKeyEvent& ev);
virtual bool isEditing();
private:
// The part under the mouse cursor
SymbolPartP highlightPart;
// Actions
// All are either owned by the symbol's action stack or equal 0
SymbolPartMoveAction* moveAction;
SymbolPartScaleAction* scaleAction;
SymbolPartRotateAction* rotateAction;
SymbolPartShearAction* shearAction;
// Bounding box of selection
Vector2D minV, maxV;
// Where is the rotation center?
Vector2D center;
// At what angle is the handle we started draging for rotation
double startAngle;
// what side are we dragging/rotating on?
int scaleX, scaleY;
// Do we want to rotate?
bool rotate;
// Graphics assets
wxCursor cursorRotate;
wxCursor cursorShearX;
wxCursor cursorShearY;
Bitmap handleRotateTL, handleRotateTR, handleRotateBL, handleRotateBR;
Bitmap handleShearX, handleShearY;
Bitmap handleCenter;
/// Is the mouse on a scale/rotate handle?
bool onHandle(const Vector2D& mpos, int dx, int dy);
/// Is the mouse on any handle?
/** Returns the handle coordinates [-1..1] in d?Out
*/
bool onAnyHandle(const Vector2D& mpos, int* dxOut, int* dyOut);
/// Angle between center and pos
double angleTo(const Vector2D& pos);
/// 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 selectedParts
/// Updates center to be the rotation center of the parts
void updateBoundingBox();
/// Reset all the actions to 0
void resetActions();
};
// ----------------------------------------------------------------------------- : EOF
#endif
+220
View File
@@ -0,0 +1,220 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/symbol/viewer.hpp>
// ----------------------------------------------------------------------------- : Constructor
SymbolViewer::SymbolViewer(const SymbolP& symbol, double borderRadius)
: borderRadius(borderRadius)
, SymbolView(symbol)
, rotation(0, RealRect(0,0,500,500))
{}
// ----------------------------------------------------------------------------- : Drawing
typedef shared_ptr<wxMemoryDC> MemoryDCP;
// Return a temporary DC with the same size as the parameter
MemoryDCP getTempDC(DC& dc) {
wxSize s = dc.GetSize();
Bitmap buffer(s.GetWidth(), s.GetHeight(), 1);
MemoryDCP newDC(new wxMemoryDC);
newDC->SelectObject(buffer);
// On windows 9x it seems that bitmaps are not black by default
#if !BITMAPS_DEFAULT_BLACK
newDC->SetPen(*wxTRANSPARENT_PEN);
newDC->SetBrush(*wxBLACK_BRUSH);
newDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
#endif
return newDC;
}
// Combine the temporary DCs used in the drawing with the main dc
void combineBuffers(DC& dc, DC* borders, DC* interior) {
wxSize s = dc.GetSize();
if (borders) dc.Blit(0, 0, s.GetWidth(), s.GetHeight(), borders, 0, 0, wxOR);
if (interior) dc.Blit(0, 0, s.GetWidth(), s.GetHeight(), interior, 0, 0, wxAND_INVERT);
}
void SymbolViewer::draw(DC& dc) {
bool paintedSomething = false;
bool buffersFilled = false;
// Temporary dcs
MemoryDCP borderDC;
MemoryDCP interiorDC;
// Check if we can paint directly to the dc
// This will fail if there are parts with combine == intersection
FOR_EACH(p, symbol->parts) {
if (p->combine == PART_INTERSECTION) {
paintedSomething = true;
break;
}
}
// Draw all parts, in reverse order (bottom to top)
FOR_EACH_REVERSE(p, symbol->parts) {
const SymbolPart& part = *p;
if (part.combine == PART_OVERLAP && buffersFilled) {
// We will be overlapping some previous parts, write them to the screen
combineBuffers(dc, borderDC.get(), interiorDC.get());
// Clear the buffers
buffersFilled = false;
paintedSomething = true;
wxSize s = dc.GetSize();
if (borderDC) {
borderDC->SetBrush(*wxBLACK_BRUSH);
borderDC->SetPen( *wxTRANSPARENT_PEN);
borderDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
}
interiorDC->SetBrush(*wxBLACK_BRUSH);
interiorDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
}
if (!paintedSomething) {
// No need to buffer
if (!interiorDC) interiorDC = getTempDC(dc);
combineSymbolPart(part, dc, *interiorDC, true, false);
buffersFilled = true;
} else {
if (!borderDC) borderDC = getTempDC(dc);
if (!interiorDC) interiorDC = getTempDC(dc);
// Draw this part to the buffer
combineSymbolPart(part, *borderDC, *interiorDC, false, false);
buffersFilled = true;
}
}
// Output the final parts from the buffer
if (buffersFilled) {
combineBuffers(dc, borderDC.get(), interiorDC.get());
}
}
void SymbolViewer::highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style) {
// create point list
vector<wxPoint> points;
size_t size = part.points.size();
for(size_t i = 0 ; i < size ; ++i) {
segmentSubdivide(*part.getPoint((int)i), *part.getPoint((int)i+1), rotation, points);
}
// draw
if (style == HIGHLIGHT_BORDER) {
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.SetPen (wxPen(Color(255,0,0), 2));
dc.DrawPolygon((int)points.size(), &points[0]);
} else {
dc.SetLogicalFunction(wxOR);
dc.SetBrush(Color(0,0,64));
dc.SetPen (*wxTRANSPARENT_PEN);
dc.DrawPolygon((int)points.size(), &points[0]);
if (part.combine == PART_SUBTRACT || part.combine == PART_BORDER) {
dc.SetLogicalFunction(wxAND);
dc.SetBrush(Color(192,192,255));
dc.DrawPolygon((int)points.size(), &points[0]);
}
dc.SetLogicalFunction(wxCOPY);
}
}
void SymbolViewer::combineSymbolPart(const SymbolPart& part, DC& border, DC& interior, bool directB, bool directI) {
// what color should the interior be?
// use black when drawing to the screen
int interiorCol = directI ? 0 : 255;
// how to draw depends on combining mode
switch(part.combine) {
case PART_OVERLAP:
case PART_MERGE: {
drawSymbolPart(part, &border, &interior, 255, interiorCol, directB);
break;
} case PART_SUBTRACT: {
border.SetLogicalFunction(wxAND);
drawSymbolPart(part, &border, &interior, 0, interiorCol ^ 255, directB);
border.SetLogicalFunction(wxCOPY);
break;
} case PART_INTERSECTION: {
MemoryDCP keepBorder = getTempDC(border);
MemoryDCP keepInterior = getTempDC(interior);
drawSymbolPart(part, keepBorder.get(), keepInterior.get(), 255, 255, false);
// combine the temporary dcs with the result using the AND operator
wxSize s = border.GetSize();
border .Blit(0, 0, s.GetWidth(), s.GetHeight(), &*keepBorder, 0, 0, wxAND);
interior.Blit(0, 0, s.GetWidth(), s.GetHeight(), &*keepInterior, 0, 0, wxAND);
break;
} case PART_DIFFERENCE: {
// TODO
break;
} case PART_BORDER: {
// draw border as interior
drawSymbolPart(part, nullptr, &border, 0, 255, false);
break;
}
}
}
void SymbolViewer::drawSymbolPart(const SymbolPart& part, DC* border, DC* interior, int borderCol, int interiorCol, bool directB) {
// create point list
vector<wxPoint> points;
size_t size = part.points.size();
for(size_t i = 0 ; i < size ; ++i) {
segmentSubdivide(*part.getPoint((int)i), *part.getPoint((int)i+1), rotation, points);
}
// draw border
if (border) {
if (directB) {
// white/green
border->SetBrush(Color(borderCol, min(255,borderCol + 128), borderCol));
} else {
// white/black
border->SetBrush(Color(borderCol, borderCol, borderCol));
}
border->SetPen(wxPen(*wxWHITE, rotation.trS(borderRadius)));
border->DrawPolygon((int)points.size(), &points[0]);
}
// draw interior
if (interior) {
interior->SetBrush(Color(interiorCol,interiorCol,interiorCol));
interior->SetPen(*wxTRANSPARENT_PEN);
interior->DrawPolygon((int)points.size(), &points[0]);
}
}
/*
void SymbolViewer::calcBezierPoint(const ControlPointP& p0, const ControlPointP& p1, Point*& p_out, UInt count) {
BezierCurve c(*p0, *p1);
// add start point
*p_out = toDisplay(*p0);
++p_out;
// recursively calculate rest of curve
calcBezierOpt(c, *p0, *p1, 0.0f, 1.0f, p_out, count-1);
}
void SymbolViewer::calcBezierOpt(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, Point*& p_out, mutable UInt count) {
if (count <= 0) return;
double midtime = (t0+t1) * 0.5f;
Vector2D midpoint = c.pointAt(midtime);
Vector2D d0 = p0 - midpoint;
Vector2D d1 = midpoint - p1;
// Determine treshold for subdivision, greater angle -> subdivide
// greater size -> subdivide
double treshold = fabs( atan2(d0.x,d0.y) - atan2(d1.x,d1.y)) * (p0-p1).lengthSqr();
bool subdivide = treshold >= .0001;
// subdivide left
calcBezierOpt(c, p0, midpoint, t0, midtime, p_out, count/2);
// add midpoint
if (subdivide) {
*p_out = toDisplay(midpoint);
++p_out;
count -= 1;
}
// subdivide right
calcBezierOpt(c, midpoint, p1, midtime, t1, p_out, count/2);
}
*/
+74
View File
@@ -0,0 +1,74 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_VIEWER
#define HEADER_GUI_SYMBOL_VIEWER
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/rotation.hpp>
#include <data/symbol.hpp>
#include <gfx/bezier.hpp>
// ----------------------------------------------------------------------------- : Symbol Viewer
enum HighlightStyle {
HIGHLIGHT_BORDER,
HIGHLIGHT_INTERIOR
};
/// Class that knows how to draw a symbol
class SymbolViewer : public SymbolView {
public:
// --------------------------------------------------- : Data
SymbolViewer(const SymbolP& symbol, double borderRadius = 0.05);
// drawing
double borderRadius;
// --------------------------------------------------- : Point translation
Rotation rotation; //^ Object that handles rotation, scaling and translation
// --------------------------------------------------- : Drawing
public:
/// Draw the symbol to a dc
void draw(DC& dc);
void highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style);
private:
/// Combines a symbol part with what is currently drawn, the border and interior are drawn separatly
/** directB/directI are true if the border/interior is the screen dc, false if it
* is a temporary 1 bit one
*/
void combineSymbolPart(const SymbolPart& part, DC& border, DC& interior, bool directB, bool directI);
/// Draw a symbol part, draws the border and the interior to separate DCs
/** The DCs may be null. directB should be true when drawing the border directly to the screen.
* The **Col parameters give the color to use for the (interior of) the border and the interior
* default should be white (255) border and black (0) interior.
*/
void drawSymbolPart(const SymbolPart& part, DC* border, DC* interior, int borderCol, int interiorCol, bool directB);
/*
// ------------------- Bezier curve calculation
// Calculate the points on a bezier curve between p0 and p1
// Stores the Points in p_out, at most count points are stored
// after this call p_out points to just beyond the last point
void calcBezierPoint(const ControlPointP& p0, const ControlPointP& p1, wxPoint*& p_out, UInt count);
// Subdivide a bezier curve by adding at most count points
// p0 = c(t0), p1 = c(p1)
// subdivides linearly between t0 and t1, and only when necessary
// adds points to p_out and increments the pointer when a point is added
void calcBezierOpt(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, wxPoint*& p_out, UInt count);
*/};
// ----------------------------------------------------------------------------- : EOF
#endif
+283
View File
@@ -0,0 +1,283 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/symbol/window.hpp>
#include <gui/symbol/control.hpp>
#include <gui/symbol/part_list.hpp>
#include <gui/icon_menu.hpp>
#include <util/window_id.hpp>
#include <util/io/reader.hpp>
#include <wx/filename.h>
#include <wx/wfstream.h>
// ----------------------------------------------------------------------------- : Window ids
enum SymIDs
{ idFileNew = wxID_NEW
, idFileOpen = wxID_OPEN
, idFileSave = wxID_SAVE
, idFileSaveAs = wxID_SAVEAS
, idFileStore = 0
, idFileExit = wxID_EXIT
, idExtraTools = 1000
, idExtraToolsMax = idExtraTools + 500
, idEditUndo = wxID_UNDO
, idEditRedo = wxID_REDO
, idEditDuplicate = 1100 // idExtraTools + 100
, idModeSelect = idFileStore + 1
, idModeRotate
, idModePoints
, idModeShapes
, idModePaint
, idModeMax
, idPartList
, idControl
};
// ------------------------------------------------------------------------------------------------ : Default symbol
// A default symbol part, a square, moved by d
SymbolPartP defaultSymbolPart(double d) {
SymbolPartP part = new_shared<SymbolPart>();
part->points.push_back(new_shared2<ControlPoint>(d + .2, d + .2));
part->points.push_back(new_shared2<ControlPoint>(d + .2, d + .8));
part->points.push_back(new_shared2<ControlPoint>(d + .8, d + .8));
part->points.push_back(new_shared2<ControlPoint>(d + .8, d + .2));
part->name = _("Square");
return part;
}
// A default symbol, a square
SymbolP defaultSymbol() {
SymbolP symbol = new_shared<Symbol>();
symbol->parts.push_back(defaultSymbolPart(0));
return symbol;
}
// ----------------------------------------------------------------------------- : Constructor
SymbolWindow::SymbolWindow(Window* parent) {
init(parent, defaultSymbol());
}
SymbolWindow::SymbolWindow(Window* parent, String filename) {
// TODO
init(parent, defaultSymbol());
}
void SymbolWindow::init(Window* parent, SymbolP symbol) {
Create(parent, wxID_ANY, _("Symbol Editor"), wxDefaultPosition, wxSize(600,600), wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
inSelectionEvent = false;
// Menu bar
wxMenuBar* menuBar = new wxMenuBar();
IconMenu* menuFile = new IconMenu();
menuFile->Append(ID_FILE_NEW, _("TOOL_NEW"), _("&New...\tCtrl+N"), _("Create a new symbol"));
menuFile->Append(ID_FILE_OPEN, _("TOOL_OPEN"), _("&Open...\tCtrl+O"), _("Open a symbol"));
menuFile->Append(ID_FILE_SAVE, _("TOOL_SAVE"), _("&Save\tCtrl+S"), _("Save the symbol"));
menuFile->Append(ID_FILE_SAVE_AS, _("Save &As...\tF12"), _("Save the symbol under a diferent filename"));
menuFile->AppendSeparator();
menuFile->Append(ID_FILE_STORE, _("TOOL_APPLY"), _("S&tore\tCtrl+Enter"), _("Stores the symbol in the set"));
menuFile->AppendSeparator();
menuFile->Append(ID_FILE_EXIT, _("&Close\tAlt+F4"), _("Closes the symbol editor"));
menuBar->Append(menuFile, _("&File"));
IconMenu* menuEdit = new IconMenu();
menuEdit->Append(ID_EDIT_UNDO, _("TOOL_UNDO"), _("&Undo\tCtrl+Z"), _("Undoes the last action"));
menuEdit->Append(ID_EDIT_REDO, _("TOOL_REDO"), _("&Redo\tF4"), _(""));
menuEdit->AppendSeparator();
menuEdit->Append(ID_EDIT_DUPLICATE, _("TOOL_DUPLICATE"), _("&Duplicate\tCtrl+D"),_("Duplicates the selected shapes"));
menuBar->Append(menuEdit, _("&Edit"));
IconMenu* menuTool = new IconMenu();
menuTool->Append(ID_MODE_SELECT, _("TOOL_MODE_SELECT"), _("&Select\tF5"), _("Select and move shapes"), wxITEM_CHECK);
menuTool->Append(ID_MODE_ROTATE, _("TOOL_MODE_ROTATE"), _("&Rotate\tF6"), _("Rotate and shear shapes"), wxITEM_CHECK);
menuTool->Append(ID_MODE_POINTS, _("TOOL_MODE_CURVE"), _("&Points\tF7"), _("Edit control points for a shape in the symbol"), wxITEM_CHECK);
menuTool->Append(ID_MODE_SHAPES, _("TOOL_CIRCLE"), _("&Basic Shapes\tF8"), _("Draw basic shapes, such as rectangles and circles"), wxITEM_CHECK);
menuTool->Append(ID_MODE_PAINT, _("TOOL_MODE_PAINT"), _("P&aint\tF9"), _("Paint on the shape using a paintbrush"), wxITEM_CHECK);
menuBar->Append(menuTool, _("&Tool"));
SetMenuBar(menuBar);
// Statusbar
CreateStatusBar();
SetStatusText(_(""));
// Toolbar
wxToolBar* tb = CreateToolBar(wxTB_FLAT | wxNO_BORDER | wxTB_HORIZONTAL | wxTB_TEXT);
tb->AddTool(ID_FILE_STORE, _("Store"), Bitmap(_("TOOL_APPLY")), wxNullBitmap, wxITEM_NORMAL, _("Store symbol in set"), _("Stores the symbol in the set"));
tb->AddSeparator();
tb->AddTool(ID_EDIT_UNDO, _("Undo"), Bitmap(_("TOOL_UNDO")), wxNullBitmap, wxITEM_NORMAL, _("Undo"), _("Undoes the last action"));
tb->AddTool(ID_EDIT_REDO, _("Redo"), Bitmap(_("TOOL_REDO")), wxNullBitmap, wxITEM_NORMAL, _("Redo"), _("Redoes the last action undone"));
tb->Realize();
// 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);
em->AddTool(ID_MODE_SELECT,_("Select"), Bitmap(_("TOOL_MODE_SELECT")), wxNullBitmap, wxITEM_CHECK, _("Select (F5)"), _("Select and move parts of the symbol"));
em->AddTool(ID_MODE_ROTATE,_("Rotate"), Bitmap(_("TOOL_MODE_ROTATE")), wxNullBitmap, wxITEM_CHECK, _("Rotate (F6)"), _("Rotate and shear parts of the symbol"));
em->AddSeparator();
em->AddTool(ID_MODE_POINTS,_("Points"), Bitmap(_("TOOL_MODE_CURVE")), wxNullBitmap, wxITEM_CHECK, _("Points (F7)"), _("Edit control points for a shape in the symbol"));
em->AddSeparator();
em->AddTool(ID_MODE_SHAPES,_("Basic Shapes"), Bitmap(_("TOOL_CIRCLE")), wxNullBitmap, wxITEM_CHECK, _("Basic Shapes (F8)"), _("Draw basic shapes, such as rectangles and circles"));
em->AddSeparator();
em->AddTool(ID_MODE_PAINT, _("Paint"), Bitmap(_("TOOL_MODE_PAINT")), wxNullBitmap, wxITEM_CHECK, _("Paint on shape (F9)"), _("Paint on the shape using a paintbrush"));
em->AddSeparator();
em->Realize();
// Controls
control = new SymbolControl (this, ID_CONTROL, symbol);
parts = new SymbolPartList(this, ID_PART_LIST, symbol);
// Lay out
wxSizer* es = new wxBoxSizer(wxHORIZONTAL);
es->Add(em, 0, wxEXPAND | wxTOP | wxBOTTOM | wxALIGN_CENTER, 1);
emp->SetSizer(es);
wxSizer* s = new wxBoxSizer(wxHORIZONTAL);
wxSizer* v = new wxBoxSizer(wxVERTICAL);
v->Add(emp, 0, wxEXPAND);
v->Add(parts, 1, wxEXPAND);
s->Add(v, 0, wxEXPAND);
s->Add(control, 1, wxEXPAND);
SetSizer(s);
}
// ----------------------------------------------------------------------------- : Event handling
void SymbolWindow::onFileNew(wxCommandEvent& ev) {
SymbolP symbol = defaultSymbol();
parts->setSymbol(symbol);
control->setSymbol(symbol);
}
void SymbolWindow::onFileOpen(wxCommandEvent& ev) {
String name = wxFileSelector(_("Open symbol"),_(""),_(""),_(""),_("Symbol files|*.mse-symbol;*.bmp|MSE2 symbol files (*.mse-symbol)|*.mse-symbol|MSE1 symbol files (*.bmp)|*.bmp"),wxOPEN|wxFILE_MUST_EXIST, this);
if (!name.empty()) {
wxFileName n(name);
String ext = n.GetExt();
SymbolP symbol;
if (ext.Lower() == _("bmp")) {
//% symbol = importSymbol(wxImage(name));
} else {
Reader reader(new_shared1<wxFileInputStream>(name), name);
reader.handle(symbol);
}
// show...
parts->setSymbol(symbol);
control->setSymbol(symbol);
}
}
void SymbolWindow::onFileSave(wxCommandEvent& ev) {
}
void SymbolWindow::onFileSaveAs(wxCommandEvent& ev) {
}
void SymbolWindow::onFileStore(wxCommandEvent& ev) {
}
void SymbolWindow::onFileExit(wxCommandEvent& ev) {
Close();
}
void SymbolWindow::onEditUndo(wxCommandEvent& ev) {
if (!control->isEditing()) {
control->getSymbol()->actions.undo();
control->Refresh(false);
}
}
void SymbolWindow::onEditRedo(wxCommandEvent& ev) {
if (!control->isEditing()) {
control->getSymbol()->actions.redo();
control->Refresh(false);
}
}
void SymbolWindow::onModeChange(wxCommandEvent& ev) {
control->onModeChange(ev);
}
void SymbolWindow::onExtraTool(wxCommandEvent& ev) {
control->onExtraTool(ev);
}
void SymbolWindow::onUpdateUI(wxUpdateUIEvent& ev) {
switch(ev.GetId()) {
// file menu
case idFileStore: {
// ev.Enable(value);
break;
// undo/redo
} case idEditUndo: {
ev.Enable(control->getSymbol()->actions.canUndo());
String label = control->getSymbol()->actions.undoName();
ev.SetText(label + _("\tCtrl+Z"));
GetToolBar()->SetToolShortHelp(ID_EDIT_UNDO, label);
break;
} case idEditRedo: {
ev.Enable(control->getSymbol()->actions.canRedo());
String label = control->getSymbol()->actions.redoName();
ev.SetText(label + _("\tF4"));
GetToolBar()->SetToolShortHelp(ID_EDIT_REDO, label);
break;
} default: {
// items created by the editor control
control->onUpdateUI(ev);
break;
}
}
}
void SymbolWindow::onSelectFromList(wxListEvent& ev) {
if (inSelectionEvent) return ;
inSelectionEvent = true;
parts->getSelectedParts(control->selectedParts);
control->onUpdateSelection();
inSelectionEvent = false;
}
void SymbolWindow::onActivateFromList(wxListEvent& ev) {
control->activatePart(control->getSymbol()->parts.at(ev.GetIndex()));
}
void SymbolWindow::onSelectFromControl() {
if (inSelectionEvent) return ;
inSelectionEvent = true;
parts->selectParts(control->selectedParts);
inSelectionEvent = false;
}
// ----------------------------------------------------------------------------- : Event table
BEGIN_EVENT_TABLE(SymbolWindow, wxFrame)
EVT_MENU (ID_FILE_NEW, SymbolWindow::onFileNew)
EVT_MENU (ID_FILE_OPEN, SymbolWindow::onFileOpen)
EVT_MENU (ID_FILE_SAVE, SymbolWindow::onFileSave)
EVT_MENU (ID_FILE_SAVE_AS, SymbolWindow::onFileSaveAs)
EVT_MENU (ID_FILE_STORE, SymbolWindow::onFileStore)
EVT_MENU (ID_FILE_EXIT, SymbolWindow::onFileExit)
EVT_MENU (ID_EDIT_UNDO, SymbolWindow::onEditUndo)
EVT_MENU (ID_EDIT_REDO, SymbolWindow::onEditRedo)
EVT_TOOL_RANGE (ID_MODE_MIN, ID_MODE_MAX, SymbolWindow::onModeChange)
EVT_TOOL_RANGE (ID_CHILD_MIN, ID_CHILD_MAX, SymbolWindow::onExtraTool)
EVT_UPDATE_UI (wxID_ANY, SymbolWindow::onUpdateUI)
EVT_LIST_ITEM_SELECTED (ID_PART_LIST, SymbolWindow::onSelectFromList)
EVT_LIST_ITEM_DESELECTED (ID_PART_LIST, SymbolWindow::onSelectFromList)
EVT_LIST_ITEM_ACTIVATED (ID_PART_LIST, SymbolWindow::onActivateFromList)
END_EVENT_TABLE ()
+75
View File
@@ -0,0 +1,75 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_WINDOW
#define HEADER_GUI_SYMBOL_WINDOW
// ----------------------------------------------------------------------------- : Includes
#include "../../util/prec.hpp"
#include <data/symbol.hpp>
#include <wx/listctrl.h>
//#include "control.hpp"
class SymbolControl;
class SymbolPartList;
// ----------------------------------------------------------------------------- : SymbolWindow
/// The window for editing symbols
class SymbolWindow : public Frame {
public:
/// Construct a SymbolWindow
SymbolWindow(Window* parent);
/// Construct a SymbolWindow showing a symbol from a file
SymbolWindow(Window* parent, String filename);
// /// Construct a SymbolWindow showing a symbol from a set
// SymbolWindow(Window* parent);
private:
// --------------------------------------------------- : Children
/// Actual initialisation
void init(Window* parent, SymbolP symbol);
SymbolControl* control; //^ The control for editing/displaying the symbol
SymbolPartList* parts; //^ A list of parts in the symbol
// when editing a symbol field
// SymbolValueP value
// SetP set
// --------------------------------------------------- : Event handling
DECLARE_EVENT_TABLE();
void onFileNew (wxCommandEvent&);
void onFileOpen (wxCommandEvent&);
void onFileSave (wxCommandEvent&);
void onFileSaveAs(wxCommandEvent&);
void onFileStore (wxCommandEvent&);
void onFileExit (wxCommandEvent&);
void onEditUndo (wxCommandEvent&);
void onEditRedo (wxCommandEvent&);
void onModeChange(wxCommandEvent&);
void onExtraTool (wxCommandEvent&);
void onUpdateUI(wxUpdateUIEvent& e);
/// Changing selected parts in the list
void onSelectFromList(wxListEvent& ev);
/// Activating a part: open the point editor
void onActivateFromList(wxListEvent& ev);
bool inSelectionEvent; //^ Prevent recursion in onSelect...
public:
void onSelectFromControl();
};
// ----------------------------------------------------------------------------- : EOF
#endif
+43
View File
@@ -0,0 +1,43 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/util.hpp>
#include <util/error.hpp>
#include <wx/mstream.h>
// ----------------------------------------------------------------------------- : DC related
/// Fill a DC with a single color
void clearDC(DC& dc, const wxBrush& brush) {
wxSize size = dc.GetSize();
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(brush);
dc.DrawRectangle(0, 0, size.GetWidth(), size.GetHeight());
}
// ----------------------------------------------------------------------------- : Image related
Image loadResourceImage(String name) {
#ifdef __WXMSW__
// Load resource
// based on wxLoadUserResource
// The image can be in an IMAGE resource, in any file format
HRSRC hResource = ::FindResource(wxGetInstance(), name, _("IMAGE"));
if ( hResource == 0 ) throw InternalError(_("Resource not found: ") + name);
HGLOBAL hData = ::LoadResource(wxGetInstance(), hResource);
if ( hData == 0 ) throw InternalError(_("Resource not an image: ") + name);
char* data = (char *)::LockResource(hData);
if ( !data ) throw InternalError(_("Resource cannot be locked: ") + name);
int len = ::SizeofResource(wxGetInstance(), hResource);
wxMemoryInputStream stream(data, len);
return wxImage(stream);
#endif
}
+29
View File
@@ -0,0 +1,29 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_UTIL
#define HEADER_GUI_UTIL
/** @file gui/util.hpp
* Utility functions for use in the gui. Most are related to drawing.
*/
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
// ----------------------------------------------------------------------------- : DC related
/// Fill a DC with a single color
void clearDC(DC& dc, const wxBrush& brush = *wxBLACK_BRUSH);
// ----------------------------------------------------------------------------- : Resource related
/// Load an image from a resource
Image loadResourceImage(String name);
// ----------------------------------------------------------------------------- : EOF
#endif