mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 ()
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 ()
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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 ()
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user