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,350 @@
|
|||||||
|
All or most of the source files in this distribution refer to this
|
||||||
|
file for copyright and warranty information. This file should be
|
||||||
|
included whenever those files are redistributed.
|
||||||
|
|
||||||
|
This software is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License, version 2, as
|
||||||
|
published by the Free Software Foundation. That license is reproduced
|
||||||
|
below.
|
||||||
|
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Library General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Library General
|
||||||
|
Public License instead of this License.
|
||||||
@@ -0,0 +1,368 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 <data/action/symbol.hpp>
|
||||||
|
#include <data/action/symbol_part.hpp>
|
||||||
|
|
||||||
|
typedef pair<SymbolPartP,SymbolPartCombine> pair_part_combine_t;
|
||||||
|
typedef pair<SymbolPartP,size_t > pair_part_size_t;
|
||||||
|
DECLARE_TYPEOF_COLLECTION(pair_part_combine_t);
|
||||||
|
DECLARE_TYPEOF_COLLECTION(pair_part_size_t);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Moving symbol parts
|
||||||
|
|
||||||
|
SymbolPartMoveAction::SymbolPartMoveAction(const set<SymbolPartP>& parts)
|
||||||
|
: parts(parts)
|
||||||
|
, constrain(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String SymbolPartMoveAction::getName(bool toUndo) const {
|
||||||
|
return parts.size() == 1 ? _("Move shape") : _("Move shapes");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartMoveAction::perform(bool toUndo) {
|
||||||
|
// move the points back
|
||||||
|
FOR_EACH(p, parts) {
|
||||||
|
p->minPos -= moved;
|
||||||
|
p->maxPos -= moved;
|
||||||
|
FOR_EACH(pnt, p->points) {
|
||||||
|
pnt->pos -= moved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
moved = -moved;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartMoveAction::move(const Vector2D& deltaDelta) {
|
||||||
|
delta += deltaDelta;
|
||||||
|
// Move each point by deltaDelta, possibly constrained
|
||||||
|
Vector2D d = constrainVector(delta, constrain);
|
||||||
|
Vector2D dd = d - moved;
|
||||||
|
FOR_EACH(p, parts) {
|
||||||
|
p->minPos += dd;
|
||||||
|
p->maxPos += dd;
|
||||||
|
FOR_EACH(pnt, p->points) {
|
||||||
|
pnt->pos += dd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
moved = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Rotating symbol parts
|
||||||
|
|
||||||
|
SymbolPartMatrixAction::SymbolPartMatrixAction(const set<SymbolPartP>& parts, const Vector2D& center)
|
||||||
|
: parts(parts)
|
||||||
|
, center(center)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void SymbolPartMatrixAction::transform(const Vector2D& mx, const Vector2D& my) {
|
||||||
|
// Transform each point
|
||||||
|
FOR_EACH(p, parts) {
|
||||||
|
FOR_EACH(pnt, p->points) {
|
||||||
|
pnt->pos = (pnt->pos - center).mul(mx,my) + center;
|
||||||
|
pnt->deltaBefore = pnt->deltaBefore.mul(mx,my);
|
||||||
|
pnt->deltaAfter = pnt->deltaAfter .mul(mx,my);
|
||||||
|
}
|
||||||
|
// bounds change after transforming
|
||||||
|
p->calculateBounds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SymbolPartRotateAction::SymbolPartRotateAction(const set<SymbolPartP>& parts, const Vector2D& center)
|
||||||
|
: SymbolPartMatrixAction(parts, center)
|
||||||
|
, constrain(false)
|
||||||
|
, angle(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String SymbolPartRotateAction::getName(bool toUndo) const {
|
||||||
|
return parts.size() == 1 ? _("Rotate shape") : _("Rotate shapes");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartRotateAction::perform(bool toUndo) {
|
||||||
|
// move the points back
|
||||||
|
rotateBy(-angle);
|
||||||
|
angle = -angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartRotateAction::rotateTo(double newAngle) {
|
||||||
|
double oldAngle = angle;
|
||||||
|
angle = newAngle;
|
||||||
|
// constrain?
|
||||||
|
if (constrain) {
|
||||||
|
// multiples of 2pi/24 i.e. 24 stops
|
||||||
|
double mult = (2 * M_PI) / 24;
|
||||||
|
angle = floor(angle / mult + 0.5) * mult;
|
||||||
|
}
|
||||||
|
if (oldAngle != angle) rotateBy(angle - oldAngle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartRotateAction::rotateBy(double deltaAngle) {
|
||||||
|
// Rotation 'matrix'
|
||||||
|
transform(
|
||||||
|
Vector2D(cos(deltaAngle), -sin(deltaAngle)),
|
||||||
|
Vector2D(sin(deltaAngle), cos(deltaAngle))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Shearing symbol parts
|
||||||
|
|
||||||
|
SymbolPartShearAction::SymbolPartShearAction(const set<SymbolPartP>& parts, const Vector2D& center)
|
||||||
|
: SymbolPartMatrixAction(parts, center)
|
||||||
|
, constrain(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String SymbolPartShearAction::getName(bool toUndo) const {
|
||||||
|
return parts.size() == 1 ? _("Shear shape") : _("Shear shapes");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartShearAction::perform(bool toUndo) {
|
||||||
|
// move the points back
|
||||||
|
// the vector shear = (x,y) is used as:
|
||||||
|
// <1 x>
|
||||||
|
// <y 1>
|
||||||
|
// inverse is:
|
||||||
|
// <1 -x> /
|
||||||
|
// <-y 1> / (1 - xy)
|
||||||
|
// we have: xy = 0 => (1 - xy) = 1
|
||||||
|
shearBy(-shear);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartShearAction::move(const Vector2D& deltaShear) {
|
||||||
|
shear += deltaShear;
|
||||||
|
shearBy(deltaShear);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartShearAction::shearBy(const Vector2D& shear) {
|
||||||
|
// Shear 'matrix'
|
||||||
|
transform(
|
||||||
|
Vector2D(1, shear.x),
|
||||||
|
Vector2D(shear.y, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Scaling symbol parts
|
||||||
|
|
||||||
|
|
||||||
|
SymbolPartScaleAction::SymbolPartScaleAction(const set<SymbolPartP>& parts, int scaleX, int scaleY)
|
||||||
|
: parts(parts)
|
||||||
|
, constrain(false)
|
||||||
|
, scaleX(scaleX), scaleY(scaleY)
|
||||||
|
{
|
||||||
|
// Find min and max coordinates
|
||||||
|
oldMin = Vector2D::infinity();
|
||||||
|
Vector2D oldMax = -Vector2D::infinity();
|
||||||
|
FOR_EACH(p, parts) {
|
||||||
|
oldMin = piecewise_min(oldMin, p->minPos);
|
||||||
|
oldMax = piecewise_max(oldMax, p->maxPos);
|
||||||
|
}
|
||||||
|
// new == old
|
||||||
|
newMin = newRealMin = oldMin;
|
||||||
|
newSize = newRealSize = oldSize = oldMax - oldMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
String SymbolPartScaleAction::getName(bool toUndo) const {
|
||||||
|
return parts.size() == 1 ? _("Scale shape") : _("Scale shapes");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartScaleAction::perform(bool toUndo) {
|
||||||
|
swap(oldMin, newMin);
|
||||||
|
swap(oldSize, newSize);
|
||||||
|
transformAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartScaleAction::move(const Vector2D& deltaMin, const Vector2D& deltaMax) {
|
||||||
|
newRealMin += deltaMin;
|
||||||
|
newRealSize += deltaMax - deltaMin;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartScaleAction::update() {
|
||||||
|
// Move each point so the range <oldMin...oldMax> maps to <newMin...newMax>
|
||||||
|
// we have already moved to the current <newMin...newMax>
|
||||||
|
Vector2D tmpMin = oldMin, tmpSize = oldSize; // the size before any scaling
|
||||||
|
oldMin = newMin; oldSize = newSize; // the size before this move
|
||||||
|
// the size after the move
|
||||||
|
newMin = newRealMin; newSize = newRealSize;
|
||||||
|
if (constrain && scaleX != 0 && scaleY != 0) {
|
||||||
|
Vector2D scale = newSize.div(tmpSize);
|
||||||
|
scale = constrainVector(scale, true, true);
|
||||||
|
newSize = tmpSize.mul(scale);
|
||||||
|
newMin += (newRealSize - newSize).mul(Vector2D(scaleX == -1 ? 1 : 0, scaleY == -1 ? 1 : 0));
|
||||||
|
}
|
||||||
|
// now move all points
|
||||||
|
transformAll();
|
||||||
|
// restore oldMin/Size
|
||||||
|
oldMin = tmpMin; oldSize = tmpSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartScaleAction::transformAll() {
|
||||||
|
Vector2D scale = newSize.div(oldSize);
|
||||||
|
FOR_EACH(p, parts) {
|
||||||
|
p->minPos = transform(p->minPos);
|
||||||
|
p->maxPos = transform(p->maxPos);
|
||||||
|
// make sure that max >= min
|
||||||
|
if (p->minPos.x > p->maxPos.x) swap(p->minPos.x, p->maxPos.x);
|
||||||
|
if (p->minPos.y > p->maxPos.y) swap(p->minPos.y, p->maxPos.y);
|
||||||
|
// scale all points
|
||||||
|
FOR_EACH(pnt, p->points) {
|
||||||
|
pnt->pos = transform(pnt->pos);
|
||||||
|
// also scale handles
|
||||||
|
pnt->deltaBefore = pnt->deltaBefore.mul(scale);
|
||||||
|
pnt->deltaAfter = pnt->deltaAfter .mul(scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Change combine mode
|
||||||
|
|
||||||
|
CombiningModeAction::CombiningModeAction(const set<SymbolPartP>& parts, SymbolPartCombine mode) {
|
||||||
|
FOR_EACH(p, parts) {
|
||||||
|
this->parts.push_back(make_pair(p,mode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String CombiningModeAction::getName(bool toUndo) const {
|
||||||
|
return _("Change combine mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CombiningModeAction::perform(bool toUndo) {
|
||||||
|
FOR_EACH(pm, parts) {
|
||||||
|
swap(pm.first->combine, pm.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Change name
|
||||||
|
|
||||||
|
SymbolPartNameAction::SymbolPartNameAction(const SymbolPartP& part, const String& name)
|
||||||
|
: part(part), partName(name)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String SymbolPartNameAction::getName(bool toUndo) const {
|
||||||
|
return _("Change shape name");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPartNameAction::perform(bool toUndo) {
|
||||||
|
swap(part->name, partName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Add symbol part
|
||||||
|
|
||||||
|
AddSymbolPartAction::AddSymbolPartAction(Symbol& symbol, const SymbolPartP& part)
|
||||||
|
: symbol(symbol), part(part)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String AddSymbolPartAction::getName(bool toUndo) const {
|
||||||
|
return _("Add ") + part->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddSymbolPartAction::perform(bool toUndo) {
|
||||||
|
if (toUndo) {
|
||||||
|
assert(!symbol.parts.empty());
|
||||||
|
symbol.parts.erase (symbol.parts.begin());
|
||||||
|
} else {
|
||||||
|
symbol.parts.insert(symbol.parts.begin(), part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Remove symbol part
|
||||||
|
|
||||||
|
RemoveSymbolPartsAction::RemoveSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts)
|
||||||
|
: symbol(symbol)
|
||||||
|
{
|
||||||
|
size_t index = 0;
|
||||||
|
FOR_EACH(p, symbol.parts) {
|
||||||
|
if (parts.find(p) != parts.end()) {
|
||||||
|
removals.push_back(make_pair(p, index)); // remove this part
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String RemoveSymbolPartsAction::getName(bool toUndo) const {
|
||||||
|
return removals.size() == 1 ? _("Remove shape") : _("Remove shapes");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveSymbolPartsAction::perform(bool toUndo) {
|
||||||
|
if (toUndo) {
|
||||||
|
// reinsert the parts
|
||||||
|
// ascending order, this is the reverse of removal
|
||||||
|
FOR_EACH(r, removals) {
|
||||||
|
assert(r.second <= symbol.parts.size());
|
||||||
|
symbol.parts.insert(symbol.parts.begin() + r.second, r.first);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// remove the parts
|
||||||
|
// descending order, because earlier removals shift the rest of the vector
|
||||||
|
FOR_EACH_REVERSE(r, removals) {
|
||||||
|
assert(r.second < symbol.parts.size());
|
||||||
|
symbol.parts.erase(symbol.parts.begin() + r.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Duplicate symbol parts
|
||||||
|
|
||||||
|
DuplicateSymbolPartsAction::DuplicateSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts)
|
||||||
|
: symbol(symbol)
|
||||||
|
{
|
||||||
|
UInt index = 0;
|
||||||
|
FOR_EACH(p, symbol.parts) {
|
||||||
|
index += 1;
|
||||||
|
if (parts.find(p) != parts.end()) {
|
||||||
|
// duplicate this part
|
||||||
|
duplications.push_back(make_pair(p->clone(), index));
|
||||||
|
index += 1; // the clone also takes up space on the vector
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String DuplicateSymbolPartsAction::getName(bool toUndo) const {
|
||||||
|
return duplications.size() == 1 ? _("Duplicate shape") : _("Duplicate shapes");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DuplicateSymbolPartsAction::perform(bool toUndo) {
|
||||||
|
if (toUndo) {
|
||||||
|
// remove the clones
|
||||||
|
// walk in reverse order, otherwise we will shift the vector
|
||||||
|
FOR_EACH_REVERSE(d, duplications) {
|
||||||
|
assert(d.second < symbol.parts.size());
|
||||||
|
symbol.parts.erase(symbol.parts.begin() + d.second);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// insert the clones
|
||||||
|
FOR_EACH(d, duplications) {
|
||||||
|
assert(d.second <= symbol.parts.size());
|
||||||
|
symbol.parts.insert(symbol.parts.begin() + d.second, d.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DuplicateSymbolPartsAction::getParts(set<SymbolPartP>& parts) {
|
||||||
|
parts.clear();
|
||||||
|
FOR_EACH(d, duplications) {
|
||||||
|
parts.insert(d.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Reorder symbol parts
|
||||||
|
|
||||||
|
ReorderSymbolPartsAction::ReorderSymbolPartsAction(Symbol& symbol, size_t partId1, size_t partId2)
|
||||||
|
: symbol(symbol), partId1(partId1), partId2(partId2)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String ReorderSymbolPartsAction::getName(bool toUndo) const {
|
||||||
|
return _("Reorder");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReorderSymbolPartsAction::perform(bool toUndo) {
|
||||||
|
assert(partId1 < symbol.parts.size());
|
||||||
|
assert(partId2 < symbol.parts.size());
|
||||||
|
swap(symbol.parts[partId1], symbol.parts[partId2]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_DATA_ACTION_SYMBOL
|
||||||
|
#define HEADER_DATA_ACTION_SYMBOL
|
||||||
|
|
||||||
|
/** @file data/action/symbol.hpp
|
||||||
|
*
|
||||||
|
* Actions operating on Symbols or whole SymbolParts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/prec.hpp>
|
||||||
|
#include <util/action_stack.hpp>
|
||||||
|
#include <data/symbol.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Transform symbol part
|
||||||
|
|
||||||
|
/// Anything that changes a part
|
||||||
|
class SymbolPartAction : public Action {};
|
||||||
|
|
||||||
|
/// Anything that changes a part as displayed in the part list
|
||||||
|
class SymbolPartListAction : public SymbolPartAction {};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Moving symbol parts
|
||||||
|
|
||||||
|
/// Move some symbol parts
|
||||||
|
class SymbolPartMoveAction : public SymbolPartAction {
|
||||||
|
public:
|
||||||
|
SymbolPartMoveAction(const set<SymbolPartP>& parts);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
/// Update this action to move some more
|
||||||
|
void move(const Vector2D& delta);
|
||||||
|
|
||||||
|
private:
|
||||||
|
set<SymbolPartP> parts; //^ Parts to move
|
||||||
|
Vector2D delta; //^ How much to move
|
||||||
|
Vector2D moved; //^ How much has been moved
|
||||||
|
public:
|
||||||
|
bool constrain; //^ Constrain movement?
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Rotating symbol parts
|
||||||
|
|
||||||
|
/// Transforming symbol parts using a matrix
|
||||||
|
class SymbolPartMatrixAction : public SymbolPartAction {
|
||||||
|
public:
|
||||||
|
SymbolPartMatrixAction(const set<SymbolPartP>& parts, const Vector2D& center);
|
||||||
|
|
||||||
|
/// Update this action to move some more
|
||||||
|
void move(const Vector2D& delta);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Perform the transformation using the given matrix
|
||||||
|
void transform(const Vector2D& mx, const Vector2D& my);
|
||||||
|
|
||||||
|
set<SymbolPartP> parts; //^ Parts to transform
|
||||||
|
Vector2D center; //^ Center to transform around
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Rotate some symbol parts
|
||||||
|
class SymbolPartRotateAction : public SymbolPartMatrixAction {
|
||||||
|
public:
|
||||||
|
SymbolPartRotateAction(const set<SymbolPartP>& parts, const Vector2D& center);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
/// Update this action to rotate to a different angle
|
||||||
|
void rotateTo(double newAngle);
|
||||||
|
|
||||||
|
/// Update this action to rotate by a deltaAngle
|
||||||
|
void rotateBy(double deltaAngle);
|
||||||
|
|
||||||
|
private:
|
||||||
|
double angle; //^ How much to rotate?
|
||||||
|
public:
|
||||||
|
bool constrain; //^ Constrain movement?
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Shearing symbol parts
|
||||||
|
|
||||||
|
/// Shear some symbol parts
|
||||||
|
class SymbolPartShearAction : public SymbolPartMatrixAction {
|
||||||
|
public:
|
||||||
|
SymbolPartShearAction(const set<SymbolPartP>& parts, const Vector2D& center);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
/// Change shear by a given amount
|
||||||
|
void move(const Vector2D& deltaShear);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector2D shear; //^ Shearing, shear.x == 0 || shear.y == 0
|
||||||
|
void shearBy(const Vector2D& shear);
|
||||||
|
public:
|
||||||
|
bool constrain; //^ Constrain movement?
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Scaling symbol parts
|
||||||
|
|
||||||
|
/// Scale some symbol parts
|
||||||
|
class SymbolPartScaleAction : public SymbolPartAction {
|
||||||
|
public:
|
||||||
|
SymbolPartScaleAction(const set<SymbolPartP>& parts, int scaleX, int scaleY);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
/// Change min and max coordinates
|
||||||
|
void move(const Vector2D& deltaMin, const Vector2D& deltaMax);
|
||||||
|
/// Update the action's effect
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
set<SymbolPartP> parts; //^ Parts to scale
|
||||||
|
Vector2D oldMin, oldSize; //^ the original pos/size
|
||||||
|
Vector2D newRealMin, newRealSize; //^ the target pos/sizevoid shearBy(const Vector2D& shear)
|
||||||
|
Vector2D newMin, newSize; //^ the target pos/size after applying constrains
|
||||||
|
int scaleX, scaleY; //^ to what corner are we attached?
|
||||||
|
/// Transform everything in the parts
|
||||||
|
void transformAll();
|
||||||
|
/// Transform a single vector
|
||||||
|
inline Vector2D transform(const Vector2D& v) {
|
||||||
|
return (v - oldMin).div(oldSize).mul(newSize) + newMin;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
bool constrain; //^ Constrain movement?
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Change combine mode
|
||||||
|
|
||||||
|
/// Change the name of a symbol part
|
||||||
|
class CombiningModeAction : public SymbolPartListAction {
|
||||||
|
public:
|
||||||
|
CombiningModeAction(const set<SymbolPartP>& parts, SymbolPartCombine mode);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
vector<pair<SymbolPartP,SymbolPartCombine> > parts; //^ Affected parts with new combining modes
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Change name
|
||||||
|
|
||||||
|
/// Change the name of a symbol part
|
||||||
|
class SymbolPartNameAction : public SymbolPartListAction {
|
||||||
|
public:
|
||||||
|
SymbolPartNameAction(const SymbolPartP& part, const String& name);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SymbolPartP part; //^ Affected part
|
||||||
|
String partName; //^ New name
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Add symbol part
|
||||||
|
|
||||||
|
/// Adding a part to a symbol, added at the front of the list
|
||||||
|
/// front = drawn on top
|
||||||
|
class AddSymbolPartAction : public SymbolPartListAction {
|
||||||
|
public:
|
||||||
|
AddSymbolPartAction(Symbol& symbol, const SymbolPartP& part);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Symbol& symbol; //^ Symbol to add the part to
|
||||||
|
SymbolPartP part; //^ Part to add
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Remove symbol part
|
||||||
|
|
||||||
|
/// Removing parts from a symbol
|
||||||
|
class RemoveSymbolPartsAction : public SymbolPartListAction {
|
||||||
|
public:
|
||||||
|
RemoveSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Symbol& symbol;
|
||||||
|
/// Removed parts and their positions, sorted by ascending pos
|
||||||
|
vector<pair<SymbolPartP, size_t> > removals;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Duplicate symbol parts
|
||||||
|
|
||||||
|
/// Duplicating parts in a symbol
|
||||||
|
class DuplicateSymbolPartsAction : public SymbolPartListAction {
|
||||||
|
public:
|
||||||
|
DuplicateSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
/// Fill a set with all the new parts
|
||||||
|
void getParts(set<SymbolPartP>& parts);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Symbol& symbol;
|
||||||
|
/// Duplicates of parts and their positions, sorted by ascending pos
|
||||||
|
vector<pair<SymbolPartP, size_t> > duplications;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Reorder symbol parts
|
||||||
|
|
||||||
|
/// Change the position of a part in a symbol.
|
||||||
|
/// This is done by swapping two parts.
|
||||||
|
class ReorderSymbolPartsAction : public SymbolPartListAction {
|
||||||
|
public:
|
||||||
|
ReorderSymbolPartsAction(Symbol& symbol, size_t partId1, size_t partId2);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Symbol& symbol; //^ Symbol to swap the parts in
|
||||||
|
public:
|
||||||
|
size_t partId1, partId2; //^ Indeces of parts to swap
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,379 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 <data/action/symbol_part.hpp>
|
||||||
|
#include <gfx/bezier.hpp>
|
||||||
|
DECLARE_TYPEOF_COLLECTION(Vector2D);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Utility
|
||||||
|
|
||||||
|
inline double sgn(double v) { return v > 0 ? 1 : -1; }
|
||||||
|
|
||||||
|
Vector2D constrainVector(const Vector2D& v, bool constrain, bool onlyDiagonal) {
|
||||||
|
if (!constrain) return v;
|
||||||
|
double ax = abs(v.x), ay = abs(v.y);
|
||||||
|
if (ax * 2 < ay && !onlyDiagonal) {
|
||||||
|
return Vector2D(0, v.y); // vertical
|
||||||
|
} else if(ay * 2 < ax && !onlyDiagonal) {
|
||||||
|
return Vector2D(v.x, 0); // horizontal
|
||||||
|
} else {
|
||||||
|
return Vector2D( // diagonal
|
||||||
|
sgn(v.x) * (ax + ay) / 2,
|
||||||
|
sgn(v.y) * (ax + ay) / 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Move control point
|
||||||
|
|
||||||
|
ControlPointMoveAction::ControlPointMoveAction(const set<ControlPointP>& points)
|
||||||
|
: points(points)
|
||||||
|
, constrain(false)
|
||||||
|
{
|
||||||
|
oldValues.reserve(points.size());
|
||||||
|
FOR_EACH(p, points) {
|
||||||
|
oldValues.push_back(p->pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String ControlPointMoveAction::getName(bool toUndo) const {
|
||||||
|
return points.size() == 1 ? _("Move point") : _("Move points");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlPointMoveAction::perform(bool toUndo) {
|
||||||
|
/*
|
||||||
|
set<ControlPointP>::const_iterator it = points.begin();
|
||||||
|
vector<Vector2D> ::iterator it2 = oldValues.begin();
|
||||||
|
for( ; it != points.end() && it2 != oldValues.end() ; ++it, ++it2) {
|
||||||
|
swap<Vector2D>((*it)->pos, *it2);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
FOR_EACH_2(p,points, op,oldValues) {
|
||||||
|
swap(p->pos, op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlPointMoveAction::move (const Vector2D& deltaDelta) {
|
||||||
|
delta += deltaDelta;
|
||||||
|
// Move each point by delta, possibly constrained
|
||||||
|
Vector2D d = constrainVector(delta, constrain);
|
||||||
|
set<ControlPointP>::const_iterator it = points.begin();
|
||||||
|
vector<Vector2D> ::iterator it2 = oldValues.begin();
|
||||||
|
for( ; it != points.end() && it2 != oldValues.end() ; ++it, ++it2) {
|
||||||
|
(*it)->pos = (*it2) + d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Move handle
|
||||||
|
|
||||||
|
HandleMoveAction::HandleMoveAction(const SelectedHandle& handle)
|
||||||
|
: handle(handle)
|
||||||
|
, constrain(false)
|
||||||
|
, oldHandle(handle.getHandle())
|
||||||
|
, oldOther (handle.getOther())
|
||||||
|
{}
|
||||||
|
|
||||||
|
String HandleMoveAction::getName(bool toUndo) const {
|
||||||
|
return _("Move handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleMoveAction::perform(bool toUndo) {
|
||||||
|
swap(oldHandle, handle.getHandle());
|
||||||
|
swap(oldOther, handle.getOther());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleMoveAction::move(const Vector2D& deltaDelta) {
|
||||||
|
delta += deltaDelta;
|
||||||
|
handle.getHandle() = constrainVector(oldHandle + delta, constrain);
|
||||||
|
handle.getOther() = oldOther;
|
||||||
|
handle.onUpdateHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Segment mode
|
||||||
|
|
||||||
|
ControlPointUpdate::ControlPointUpdate(const ControlPointP& pnt)
|
||||||
|
: other(*pnt)
|
||||||
|
, point(pnt)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void ControlPointUpdate::perform() {
|
||||||
|
swap(other, *point);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SegmentModeAction::SegmentModeAction(const ControlPointP& p1, const ControlPointP& p2, SegmentMode mode)
|
||||||
|
: point1(p1), point2(p2)
|
||||||
|
{
|
||||||
|
if (p1->segmentAfter == mode) return;
|
||||||
|
point1.other.segmentAfter = point2.other.segmentBefore = mode;
|
||||||
|
if (mode == SEGMENT_LINE) {
|
||||||
|
point1.other.deltaAfter = Vector2D(0,0);
|
||||||
|
point2.other.deltaBefore = Vector2D(0,0);
|
||||||
|
point1.other.lock = LOCK_FREE;
|
||||||
|
point2.other.lock = LOCK_FREE;
|
||||||
|
} else if (mode == SEGMENT_CURVE) {
|
||||||
|
point1.other.deltaAfter = (p2->pos - p1->pos) / 3.0f;
|
||||||
|
point2.other.deltaBefore = (p1->pos - p2->pos) / 3.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String SegmentModeAction::getName(bool toUndo) const {
|
||||||
|
SegmentMode mode = toUndo ? point1.point->segmentAfter : point1.other.segmentAfter;
|
||||||
|
if (mode == SEGMENT_LINE) return _("Convert to line");
|
||||||
|
else return _("Convert to curve");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SegmentModeAction::perform(bool toUndo) {
|
||||||
|
point1.perform();
|
||||||
|
point2.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Locking mode
|
||||||
|
|
||||||
|
LockModeAction::LockModeAction(const ControlPointP& p, LockMode lock)
|
||||||
|
: point(p)
|
||||||
|
{
|
||||||
|
point.other.lock = lock;
|
||||||
|
point.other.onUpdateLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
String LockModeAction::getName(bool toUndo) const {
|
||||||
|
return _("Lock point");
|
||||||
|
}
|
||||||
|
|
||||||
|
void LockModeAction::perform(bool toUndo) {
|
||||||
|
point.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Move curve
|
||||||
|
|
||||||
|
CurveDragAction::CurveDragAction(const ControlPointP& point1, const ControlPointP& point2)
|
||||||
|
: SegmentModeAction(point1, point2, SEGMENT_CURVE)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String CurveDragAction::getName(bool toUndo) const {
|
||||||
|
return _("Move curve");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveDragAction::perform(bool toUndo) {
|
||||||
|
SegmentModeAction::perform(toUndo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveDragAction::move(const Vector2D& delta, double t) {
|
||||||
|
// Logic:
|
||||||
|
// Assuming old point is p, new point is p'
|
||||||
|
// Point on old bezier curve is:
|
||||||
|
// p = a t^3 + 3b (1-t) t^2 + 3c (1-t)^2 t + d (1-t)^2
|
||||||
|
// Point on new bezier curve is:
|
||||||
|
// p_(' = a t^3 + 3b') (1-t) t^2 + 3c' (1-t)^2 t + d (1-t)^2
|
||||||
|
// We now want to change control points b and c, the closer we are to b (t close to 0)
|
||||||
|
// the more effect we have on b, so we substitute:
|
||||||
|
// b' = b + x t
|
||||||
|
// c' = c + x (1-t)
|
||||||
|
// Solving for x we get:
|
||||||
|
// x = (p'-p) / ( t (1-t) ( t^2 + (1-t)^2) )
|
||||||
|
// Naming:
|
||||||
|
// delta = p' - p
|
||||||
|
// pointDelta = x * t * (1-t)
|
||||||
|
Vector2D pointDelta = delta / (3 * (t * t + (1-t) * (1-t)));
|
||||||
|
point1.point->deltaAfter += pointDelta / t;
|
||||||
|
point2.point->deltaBefore += pointDelta / (1-t);
|
||||||
|
point1.point->onUpdateHandle(HANDLE_AFTER);
|
||||||
|
point2.point->onUpdateHandle(HANDLE_BEFORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Add control point
|
||||||
|
|
||||||
|
ControlPointAddAction::ControlPointAddAction(const SymbolPartP& part, UInt insertAfter, double t)
|
||||||
|
: point1(part->getPoint(insertAfter))
|
||||||
|
, point2(part->getPoint(insertAfter + 1))
|
||||||
|
, part(part)
|
||||||
|
, insertAfter(insertAfter)
|
||||||
|
, newPoint(new ControlPoint())
|
||||||
|
{
|
||||||
|
// calculate new point
|
||||||
|
if (point1.other.segmentAfter == SEGMENT_CURVE) {
|
||||||
|
// calculate new handles using de Casteljau's subdivision algorithm
|
||||||
|
deCasteljau(point1.other, point2.other, t, *newPoint);
|
||||||
|
// unlock if needed
|
||||||
|
if (point1.other.lock == LOCK_SIZE) point1.other.lock = LOCK_DIR;
|
||||||
|
if (point2.other.lock == LOCK_SIZE) point2.other.lock = LOCK_DIR;
|
||||||
|
newPoint->lock = LOCK_DIR;
|
||||||
|
newPoint->segmentBefore = SEGMENT_CURVE;
|
||||||
|
newPoint->segmentAfter = SEGMENT_CURVE;
|
||||||
|
} else {
|
||||||
|
newPoint->pos = point1.other.pos * (1 - t) + point2.other.pos * t;
|
||||||
|
newPoint->lock = LOCK_FREE;
|
||||||
|
newPoint->segmentBefore = SEGMENT_LINE;
|
||||||
|
newPoint->segmentAfter = SEGMENT_LINE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String ControlPointAddAction::getName(bool toUndo) const {
|
||||||
|
return _("Add control point");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlPointAddAction::perform(bool toUndo) {
|
||||||
|
if (toUndo) { // remove the point
|
||||||
|
part->points.erase( part->points.begin() + insertAfter + 1);
|
||||||
|
} else {
|
||||||
|
part->points.insert(part->points.begin() + insertAfter + 1, newPoint);
|
||||||
|
}
|
||||||
|
// update points before/after
|
||||||
|
point1.perform();
|
||||||
|
point2.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Remove control point
|
||||||
|
|
||||||
|
/// Sqaure root that caries the sign over the root
|
||||||
|
/// or formally: ssqrt(x) = Re<sqrt(x)> - Im<sqrt(x)> = x / sqrt(|x|)
|
||||||
|
double ssqrt(double x) {
|
||||||
|
if (x > 0) return sqrt(x);
|
||||||
|
else return -sqrt(-x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a single control point
|
||||||
|
class SinglePointRemoveAction : public Action {
|
||||||
|
public:
|
||||||
|
SinglePointRemoveAction(const SymbolPartP& part, UInt position);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const { return _("Delete point"); }
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SymbolPartP part;
|
||||||
|
UInt position;
|
||||||
|
ControlPointP point; //^ Removed point
|
||||||
|
ControlPointUpdate point1, point2; //^ Points before/after
|
||||||
|
};
|
||||||
|
|
||||||
|
SinglePointRemoveAction::SinglePointRemoveAction(const SymbolPartP& part, UInt position)
|
||||||
|
: part(part)
|
||||||
|
, position(position)
|
||||||
|
, point (part->getPoint(position - 1))
|
||||||
|
, point1(part->getPoint(position - 1))
|
||||||
|
, point2(part->getPoint(position + 1))
|
||||||
|
{
|
||||||
|
if (point1.other.segmentAfter == SEGMENT_CURVE || point2.other.segmentBefore == SEGMENT_CURVE) {
|
||||||
|
// try to preserve curve
|
||||||
|
Vector2D before = point->deltaBefore;
|
||||||
|
Vector2D after = point->deltaAfter;
|
||||||
|
|
||||||
|
// convert both segments to curves first
|
||||||
|
if (point1.other.segmentAfter != SEGMENT_CURVE) {
|
||||||
|
point1.other.deltaAfter = -
|
||||||
|
before = (point1.other.pos - point->pos) / 3.0;
|
||||||
|
point1.other.segmentAfter = SEGMENT_CURVE;
|
||||||
|
}
|
||||||
|
if (point2.other.segmentBefore != SEGMENT_CURVE) {
|
||||||
|
point2.other.deltaBefore = -
|
||||||
|
after = (point2.other.pos - point->pos) / 3.0;
|
||||||
|
point2.other.segmentBefore = SEGMENT_CURVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The inverse of adding a point, reconstruct the original handles
|
||||||
|
// before being subdivided using de Casteljau's algorithm
|
||||||
|
// length of handles
|
||||||
|
double bl = before.length() + 0.00000001; // prevent division by 0
|
||||||
|
double al = after .length() + 0.00000001;
|
||||||
|
double totl = bl + al;
|
||||||
|
// set new handle sizes
|
||||||
|
point1.other.deltaAfter *= totl / bl;
|
||||||
|
point2.other.deltaBefore *= totl / al;
|
||||||
|
|
||||||
|
// Also take in acount cases where the point does not correspond to a freshly added point.
|
||||||
|
// distance from the point to the curve as it would be in the above case can be used,
|
||||||
|
// in the case of a point just added this distance = 0
|
||||||
|
BezierCurve c(point1.other, point2.other);
|
||||||
|
double t = bl / totl;
|
||||||
|
Vector2D p = c.pointAt(t);
|
||||||
|
Vector2D distP = point->pos - p;
|
||||||
|
// adjust handle sizes
|
||||||
|
point1.other.deltaAfter *= ssqrt(distP.dot(point1.other.deltaAfter) /point1.other.deltaAfter.lengthSqr()) + 1;
|
||||||
|
point2.other.deltaBefore *= ssqrt(distP.dot(point2.other.deltaBefore)/point2.other.deltaBefore.lengthSqr()) + 1;
|
||||||
|
|
||||||
|
// unlock if needed
|
||||||
|
if (point1.other.lock == LOCK_SIZE) point1.other.lock = LOCK_DIR;
|
||||||
|
if (point2.other.lock == LOCK_SIZE) point2.other.lock = LOCK_DIR;
|
||||||
|
} else {
|
||||||
|
// just lines, keep it that way
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinglePointRemoveAction::perform(bool toUndo) {
|
||||||
|
if (toUndo) {
|
||||||
|
// reinsert the point
|
||||||
|
part->points.insert(part->points.begin() + position, point);
|
||||||
|
} else {
|
||||||
|
// remove the point
|
||||||
|
part->points.erase( part->points.begin() + position);
|
||||||
|
}
|
||||||
|
// update points around removed point
|
||||||
|
point1.perform();
|
||||||
|
point2.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
DECLARE_POINTER_TYPE(SinglePointRemoveAction);
|
||||||
|
DECLARE_TYPEOF_COLLECTION(SinglePointRemoveActionP);
|
||||||
|
|
||||||
|
|
||||||
|
/// Remove a set of points from a symbol part
|
||||||
|
/// Internally represented as a list of Single Point Remove Actions
|
||||||
|
/// Not all points mat be removed, at least two points must remain
|
||||||
|
class ControlPointRemoveAction : public Action {
|
||||||
|
public:
|
||||||
|
ControlPointRemoveAction(const SymbolPartP& part, const set<ControlPointP>& toDelete);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
vector<SinglePointRemoveActionP> removals;
|
||||||
|
};
|
||||||
|
|
||||||
|
ControlPointRemoveAction::ControlPointRemoveAction(const SymbolPartP& part, const set<ControlPointP>& toDelete) {
|
||||||
|
int index = 0;
|
||||||
|
// find points to remove, in reverse order
|
||||||
|
FOR_EACH(point, part->points) {
|
||||||
|
if (toDelete.find(point) != toDelete.end()) {
|
||||||
|
// remove this point
|
||||||
|
removals.push_back(new_shared2<SinglePointRemoveAction>(part, index));
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String ControlPointRemoveAction::getName(bool toUndo) const {
|
||||||
|
return removals.size() == 1 ? _("Delete point") : _("Delete points");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlPointRemoveAction::perform(bool toUndo) {
|
||||||
|
if (toUndo) {
|
||||||
|
FOR_EACH(r, removals) r->perform(toUndo);
|
||||||
|
} else {
|
||||||
|
// in reverse order, because positions of later points will
|
||||||
|
// change after removal of earlier points.
|
||||||
|
FOR_EACH_REVERSE(r, removals) r->perform(toUndo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Action* controlPointRemoveAction(const SymbolPartP& part, const set<ControlPointP>& toDelete) {
|
||||||
|
if (part->points.size() - toDelete.size() < 2) {
|
||||||
|
// TODO : remove part?
|
||||||
|
//new_shared<ControlPointRemoveAllAction>(part);
|
||||||
|
return 0; // no action
|
||||||
|
} else {
|
||||||
|
return new ControlPointRemoveAction(part, toDelete);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_DATA_ACTION_SYMBOL_PART
|
||||||
|
#define HEADER_DATA_ACTION_SYMBOL_PART
|
||||||
|
|
||||||
|
/** @file data/action/symbol_part.hpp
|
||||||
|
*
|
||||||
|
* Actions operating on the insides of SymbolParts (ControlPoints and the like).
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/prec.hpp>
|
||||||
|
#include <util/action_stack.hpp>
|
||||||
|
#include <data/symbol.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Utility
|
||||||
|
|
||||||
|
/// Constrain a vector to be horizontal, vertical or diagonal
|
||||||
|
/// If constraint==false does nothing
|
||||||
|
Vector2D constrainVector(const Vector2D& v, bool constrain = true, bool onlyDiagonal = false);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Move control point
|
||||||
|
|
||||||
|
/// Moving a control point in a symbol
|
||||||
|
class ControlPointMoveAction : public Action {
|
||||||
|
public:
|
||||||
|
ControlPointMoveAction(const set<ControlPointP>& points);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
/// Update this action to move some more
|
||||||
|
void move(const Vector2D& delta);
|
||||||
|
|
||||||
|
private:
|
||||||
|
set<ControlPointP> points; //^ Points to move
|
||||||
|
vector<Vector2D> oldValues; //^ Their old positions
|
||||||
|
Vector2D delta; //^ Amount we moved
|
||||||
|
public:
|
||||||
|
bool constrain; //^ Constrain movement?
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Move handle
|
||||||
|
|
||||||
|
/// Moving a handle(before/after) of a control point in a symbol
|
||||||
|
class HandleMoveAction : public Action {
|
||||||
|
public:
|
||||||
|
HandleMoveAction(const SelectedHandle& handle);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
/// Update this action to move some more
|
||||||
|
void move(const Vector2D& delta);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SelectedHandle handle; //^ The handle to move
|
||||||
|
Vector2D oldHandle; //^ Old value of this handle
|
||||||
|
Vector2D oldOther; //^ Old value of other handle, needed for contraints
|
||||||
|
Vector2D delta; //^ Amount we moved
|
||||||
|
public:
|
||||||
|
bool constrain; //^ Constrain movement?
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Segment mode
|
||||||
|
|
||||||
|
/// Utility class to update a control point
|
||||||
|
class ControlPointUpdate {
|
||||||
|
public:
|
||||||
|
ControlPointUpdate(const ControlPointP& pnt);
|
||||||
|
|
||||||
|
/// Perform or undo an update on this control point
|
||||||
|
void perform();
|
||||||
|
|
||||||
|
/// Other value that is swapped with the current one.
|
||||||
|
/// Should be changed to make perform have an effect
|
||||||
|
ControlPoint other;
|
||||||
|
/// The point that is to be changed, should not be updated before perform()
|
||||||
|
ControlPointP point;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Changing a line to a curve and vice versa
|
||||||
|
class SegmentModeAction : public Action {
|
||||||
|
public:
|
||||||
|
SegmentModeAction(const ControlPointP& p1, const ControlPointP& p2, SegmentMode mode);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ControlPointUpdate point1, point2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Locking mode
|
||||||
|
|
||||||
|
/// Locking a control point
|
||||||
|
class LockModeAction : public Action {
|
||||||
|
public:
|
||||||
|
LockModeAction(const ControlPointP& p, LockMode mode);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ControlPointUpdate point; //^ The affected point
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Move curve
|
||||||
|
|
||||||
|
/// Dragging a curve; also coverts lines to curves
|
||||||
|
/** Inherits from SegmentModeAction because it also has that effect
|
||||||
|
*/
|
||||||
|
class CurveDragAction : public SegmentModeAction {
|
||||||
|
public:
|
||||||
|
CurveDragAction(const ControlPointP& point1, const ControlPointP& point2);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
// Move the curve by this much, it is grabbed at time t
|
||||||
|
void move(const Vector2D& delta, double t);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Add control point
|
||||||
|
|
||||||
|
/// Insert a new point in a symbol part
|
||||||
|
class ControlPointAddAction : public Action {
|
||||||
|
public:
|
||||||
|
/// Insert a new point in part, after position insertAfter_, at the time t on the segment
|
||||||
|
ControlPointAddAction(const SymbolPartP& part, UInt insertAfter, double t);
|
||||||
|
|
||||||
|
virtual String getName(bool toUndo) const;
|
||||||
|
virtual void perform(bool toUndo);
|
||||||
|
|
||||||
|
inline ControlPointP getNewPoint() const { return newPoint; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SymbolPartP part; //^ SymbolPart we are in
|
||||||
|
ControlPointP newPoint; //^ The point to insert
|
||||||
|
UInt insertAfter; //^ Insert after index .. in the array
|
||||||
|
ControlPointUpdate point1, point2; //^ Update the points around the new point
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Remove control point
|
||||||
|
|
||||||
|
/// Action that removes any number of points from a symbol part
|
||||||
|
/// TODO: If less then 3 points are left removes the entire part!
|
||||||
|
Action* controlPointRemoveAction(const SymbolPartP& part, const set<ControlPointP>& toDelete);
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 <data/symbol.hpp>
|
||||||
|
#include <gfx/bezier.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : ControlPoint
|
||||||
|
|
||||||
|
IMPLEMENT_REFLECTION_ENUM(LockMode) {
|
||||||
|
VALUE_N("free", LOCK_FREE);
|
||||||
|
VALUE_N("direction", LOCK_DIR);
|
||||||
|
VALUE_N("size", LOCK_SIZE);
|
||||||
|
}
|
||||||
|
IMPLEMENT_REFLECTION_ENUM(SegmentMode) {
|
||||||
|
VALUE_N("line", SEGMENT_LINE);
|
||||||
|
VALUE_N("curve", SEGMENT_CURVE);
|
||||||
|
}
|
||||||
|
IMPLEMENT_REFLECTION(ControlPoint) {
|
||||||
|
REFLECT_N("position", pos);
|
||||||
|
REFLECT_N("lock", lock);
|
||||||
|
REFLECT_N("line after", segmentAfter);
|
||||||
|
if (tag.reading() || segmentBefore == SEGMENT_CURVE) {
|
||||||
|
REFLECT_N("handle before", deltaBefore);
|
||||||
|
}
|
||||||
|
if (tag.reading() || segmentAfter == SEGMENT_CURVE) {
|
||||||
|
REFLECT_N("handle after", deltaAfter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlPoint::ControlPoint()
|
||||||
|
: segmentBefore(SEGMENT_LINE), segmentAfter(SEGMENT_LINE)
|
||||||
|
, lock(LOCK_FREE)
|
||||||
|
{}
|
||||||
|
ControlPoint::ControlPoint(double x, double y)
|
||||||
|
: segmentBefore(SEGMENT_LINE), segmentAfter(SEGMENT_LINE)
|
||||||
|
, lock(LOCK_FREE)
|
||||||
|
, pos(x,y)
|
||||||
|
{}
|
||||||
|
ControlPoint::ControlPoint(double x, double y, double xb, double yb, double xa, double ya, LockMode lock)
|
||||||
|
: segmentBefore(SEGMENT_CURVE), segmentAfter(SEGMENT_CURVE)
|
||||||
|
, lock(lock)
|
||||||
|
, pos(x,y)
|
||||||
|
, deltaBefore(xb,yb)
|
||||||
|
, deltaAfter(xa,ya)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void ControlPoint::onUpdateHandle(WhichHandle wh) {
|
||||||
|
// One handle has changed, update only the other one
|
||||||
|
if (lock == LOCK_DIR) {
|
||||||
|
getOther(wh) = -getHandle(wh) * getOther(wh).length() / getHandle(wh).length();
|
||||||
|
} else if (lock == LOCK_SIZE) {
|
||||||
|
getOther(wh) = -getHandle(wh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ControlPoint::onUpdateLock() {
|
||||||
|
// The lock has changed, avarage the handle values
|
||||||
|
if (lock == LOCK_DIR) {
|
||||||
|
// deltaBefore = x * deltaAfter
|
||||||
|
Vector2D dir = (deltaBefore - deltaAfter).normalized();
|
||||||
|
deltaBefore = dir * deltaBefore.length();
|
||||||
|
deltaAfter = dir * -deltaAfter.length();
|
||||||
|
} else if (lock == LOCK_SIZE) {
|
||||||
|
// deltaBefore = -deltaAfter
|
||||||
|
deltaBefore = (deltaBefore - deltaAfter) * 0.5;
|
||||||
|
deltaAfter = -deltaBefore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2D& ControlPoint::getHandle(WhichHandle wh) {
|
||||||
|
if (wh == HANDLE_BEFORE) {
|
||||||
|
return deltaBefore;
|
||||||
|
} else {
|
||||||
|
assert(wh == HANDLE_AFTER);
|
||||||
|
return deltaAfter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Vector2D& ControlPoint::getOther(WhichHandle wh) {
|
||||||
|
if (wh == HANDLE_BEFORE) {
|
||||||
|
return deltaAfter;
|
||||||
|
} else {
|
||||||
|
assert(wh == HANDLE_AFTER);
|
||||||
|
return deltaBefore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : SymbolPart
|
||||||
|
|
||||||
|
IMPLEMENT_REFLECTION_ENUM(SymbolPartCombine) {
|
||||||
|
VALUE_N("overlap", PART_OVERLAP);
|
||||||
|
VALUE_N("merge", PART_MERGE);
|
||||||
|
VALUE_N("subtract", PART_SUBTRACT);
|
||||||
|
VALUE_N("intersection", PART_INTERSECTION);
|
||||||
|
VALUE_N("difference", PART_DIFFERENCE);
|
||||||
|
VALUE_N("border", PART_BORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_REFLECTION(SymbolPart) {
|
||||||
|
REFLECT(name);
|
||||||
|
REFLECT(combine);
|
||||||
|
REFLECT_N("point", points);
|
||||||
|
// Fixes after reading
|
||||||
|
if (tag.reading()) {
|
||||||
|
// enforce constraints
|
||||||
|
enforceConstraints();
|
||||||
|
calculateBounds();
|
||||||
|
if (maxPos.x > 100 && maxPos.y > 100) {
|
||||||
|
// this is a <= 0.1.2 symbol, points range [0...500] instead of [0...1]
|
||||||
|
// adjust it
|
||||||
|
FOR_EACH(p, points) {
|
||||||
|
p->pos /= 500.0;
|
||||||
|
p->deltaBefore /= 500.0;
|
||||||
|
p->deltaAfter /= 500.0;
|
||||||
|
}
|
||||||
|
if (name.empty()) name = _("Shape");
|
||||||
|
calculateBounds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolPart::SymbolPart()
|
||||||
|
: combine(PART_OVERLAP), rotationCenter(.5, .5)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SymbolPartP SymbolPart::clone() const {
|
||||||
|
SymbolPartP part = new_shared1<SymbolPart>(*this);
|
||||||
|
// also clone the control points
|
||||||
|
FOR_EACH(p, part->points) {
|
||||||
|
p = new_shared1<ControlPoint>(*p);
|
||||||
|
}
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPart::enforceConstraints() {
|
||||||
|
for (int i = 0 ; i < (int)points.size() ; ++i) {
|
||||||
|
ControlPointP p1 = getPoint(i);
|
||||||
|
ControlPointP p2 = getPoint(i + 1);
|
||||||
|
p2->segmentBefore = p1->segmentAfter;
|
||||||
|
p1->onUpdateLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolPart::calculateBounds() {
|
||||||
|
minPos = Vector2D::infinity();
|
||||||
|
maxPos = -Vector2D::infinity();
|
||||||
|
for (int i = 0 ; i < (int)points.size() ; ++i) {
|
||||||
|
segmentBounds(*getPoint(i), *getPoint(i + 1), minPos, maxPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Symbol
|
||||||
|
|
||||||
|
IMPLEMENT_REFLECTION(Symbol) {
|
||||||
|
//%% version?
|
||||||
|
REFLECT_N("part", parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : SymbolView
|
||||||
|
|
||||||
|
SymbolView::SymbolView() {}
|
||||||
|
|
||||||
|
SymbolView::SymbolView(SymbolP symbol)
|
||||||
|
: symbol(symbol)
|
||||||
|
{
|
||||||
|
if (symbol) symbol->actions.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolView::~SymbolView() {
|
||||||
|
if (symbol) symbol->actions.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolView::setSymbol(SymbolP newSymbol) {
|
||||||
|
// no longer listening to old symbol
|
||||||
|
if (symbol) symbol->actions.removeListener(this);
|
||||||
|
symbol = newSymbol;
|
||||||
|
// start listening to new symbol
|
||||||
|
if (symbol) symbol->actions.addListener(this);
|
||||||
|
onSymbolChange();
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_DATA_SYMBOL
|
||||||
|
#define HEADER_DATA_SYMBOL
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/prec.hpp>
|
||||||
|
#include <util/reflect.hpp>
|
||||||
|
#include <util/action_stack.hpp>
|
||||||
|
#include <util/vector2d.hpp>
|
||||||
|
|
||||||
|
DECLARE_POINTER_TYPE(ControlPoint);
|
||||||
|
DECLARE_POINTER_TYPE(SymbolPart);
|
||||||
|
DECLARE_POINTER_TYPE(Symbol);
|
||||||
|
DECLARE_TYPEOF_COLLECTION(ControlPointP);
|
||||||
|
DECLARE_TYPEOF_COLLECTION(SymbolPartP);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : ControlPoint
|
||||||
|
|
||||||
|
/// Mode of locking for control points in a bezier curve
|
||||||
|
/** Specificly: the relation between deltaBefore and deltaAfter
|
||||||
|
*/
|
||||||
|
enum LockMode
|
||||||
|
{ LOCK_FREE //^ no locking
|
||||||
|
, LOCK_DIR //^ deltaBefore = x * deltaAfter
|
||||||
|
, LOCK_SIZE //^ deltaBefore = -deltaAfter
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Is the segment between two ControlPoints a line or a curve?
|
||||||
|
enum SegmentMode
|
||||||
|
{ SEGMENT_LINE
|
||||||
|
, SEGMENT_CURVE
|
||||||
|
};
|
||||||
|
|
||||||
|
/// To refer to a specific handle of a control point
|
||||||
|
enum WhichHandle
|
||||||
|
{ HANDLE_NONE = 0 //^ point is not selected
|
||||||
|
, HANDLE_MAIN
|
||||||
|
, HANDLE_BEFORE
|
||||||
|
, HANDLE_AFTER
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A control point (corner) of a SymbolPart (polygon/bezier-gon)
|
||||||
|
class ControlPoint {
|
||||||
|
public:
|
||||||
|
Vector2D pos; //^ position of the control point itself
|
||||||
|
Vector2D deltaBefore; //^ delta to bezier control point, for curve before point
|
||||||
|
Vector2D deltaAfter; //^ delta to bezier control point, for curve after point
|
||||||
|
SegmentMode segmentBefore, segmentAfter;
|
||||||
|
LockMode lock;
|
||||||
|
|
||||||
|
/// Default constructor
|
||||||
|
ControlPoint();
|
||||||
|
/// Constructor for straight lines, takes only the position
|
||||||
|
ControlPoint(double x, double y);
|
||||||
|
/// Constructor for curves lines, takes postions, deltaBefore, deltaAfter and lock mode
|
||||||
|
ControlPoint(double x, double y, double xb, double yb, double xa, double ya, LockMode lock = LOCK_FREE);
|
||||||
|
|
||||||
|
/// Must be called after deltaBefore/deltaAfter has changed, enforces lock constraints
|
||||||
|
void onUpdateHandle(WhichHandle wh);
|
||||||
|
/// Must be called after lock has changed, enforces lock constraints
|
||||||
|
void onUpdateLock();
|
||||||
|
|
||||||
|
/// Get a handle of this control point
|
||||||
|
Vector2D& getHandle(WhichHandle wh);
|
||||||
|
/// Get a handle of this control point that is oposite wh
|
||||||
|
Vector2D& getOther(WhichHandle wh);
|
||||||
|
|
||||||
|
DECLARE_REFLECTION();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Selected handles
|
||||||
|
|
||||||
|
/// A specific handle of a ControlPoint
|
||||||
|
class SelectedHandle {
|
||||||
|
public:
|
||||||
|
ControlPointP point; //^ the selected point
|
||||||
|
WhichHandle handle; //^ the selected handle of the point
|
||||||
|
|
||||||
|
// SelectedHandle
|
||||||
|
SelectedHandle() : handle(HANDLE_NONE) {}
|
||||||
|
SelectedHandle(const WhichHandle& handle) : handle(handle) { assert (handle == HANDLE_NONE); }
|
||||||
|
SelectedHandle(const ControlPointP& point, const WhichHandle& handle) : point(point), handle(handle) {}
|
||||||
|
|
||||||
|
inline Vector2D& getHandle() const { return point->getHandle(handle); }
|
||||||
|
inline Vector2D& getOther() const { return point->getOther (handle); }
|
||||||
|
inline void onUpdateHandle() const { return point->onUpdateHandle(handle); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
bool operator == (const ControlPointP pnt) const;
|
||||||
|
|
||||||
|
bool SelectedHandle::operator == (const ControlPointP pnt) const { return point == pnt; }
|
||||||
|
bool operator == (const WhichHandle& wh) const;
|
||||||
|
bool SelectedHandle::operator == (const WhichHandle& wh) const { return handle == wh; }
|
||||||
|
bool operator ! () const;
|
||||||
|
bool SelectedHandle::operator ! () const { return handle == handleNone; }
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : SymbolPart
|
||||||
|
|
||||||
|
/// How are symbol parts combined with parts below it?
|
||||||
|
enum SymbolPartCombine
|
||||||
|
{ PART_MERGE
|
||||||
|
, PART_SUBTRACT
|
||||||
|
, PART_INTERSECTION
|
||||||
|
, PART_DIFFERENCE
|
||||||
|
, PART_OVERLAP
|
||||||
|
, PART_BORDER
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A single part (polygon/bezier-gon) in a Symbol
|
||||||
|
class SymbolPart {
|
||||||
|
public:
|
||||||
|
/// The points of this polygon
|
||||||
|
vector<ControlPointP> points;
|
||||||
|
/// Name/label for this part
|
||||||
|
String name;
|
||||||
|
/// How is this part combined with parts below it?
|
||||||
|
SymbolPartCombine combine;
|
||||||
|
// Center of rotation, relative to the part, when the part is scaled to [0..1]
|
||||||
|
Vector2D rotationCenter;
|
||||||
|
/// Position and size of the part
|
||||||
|
/// this is the smallest axis aligned bounding box that fits around the part
|
||||||
|
Vector2D minPos, maxPos;
|
||||||
|
|
||||||
|
SymbolPart();
|
||||||
|
|
||||||
|
/// Create a clone of this symbol part
|
||||||
|
SymbolPartP clone() const;
|
||||||
|
|
||||||
|
/// Get a control point, wraps around
|
||||||
|
inline ControlPointP getPoint(int id) const {
|
||||||
|
return points[id >= 0 ? id % points.size() : id + points.size()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enforce lock constraints
|
||||||
|
void enforceConstraints();
|
||||||
|
|
||||||
|
/// Calculate the position and size of the part
|
||||||
|
void calculateBounds();
|
||||||
|
|
||||||
|
DECLARE_REFLECTION();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Symbol
|
||||||
|
|
||||||
|
/// An editable symbol, consists of any number of SymbolParts
|
||||||
|
class Symbol {
|
||||||
|
public:
|
||||||
|
/// The parts of this symbol
|
||||||
|
vector<SymbolPartP> parts;
|
||||||
|
/// Actions performed on this symbol and the parts in it
|
||||||
|
ActionStack actions;
|
||||||
|
|
||||||
|
DECLARE_REFLECTION();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : SymbolView
|
||||||
|
|
||||||
|
/// A 'view' of a symbol, is notified when the symbol is updated
|
||||||
|
class SymbolView : public ActionListener {
|
||||||
|
public:
|
||||||
|
SymbolView();
|
||||||
|
SymbolView(SymbolP symbol);
|
||||||
|
~SymbolView();
|
||||||
|
|
||||||
|
/// Get the symbol that is currently being viewed
|
||||||
|
inline SymbolP getSymbol() { return symbol; }
|
||||||
|
/// Change the symbol that is being viewed
|
||||||
|
void setSymbol(SymbolP symbol);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// The symbol that is currently being viewed, should not be modified directly!
|
||||||
|
SymbolP symbol;
|
||||||
|
|
||||||
|
/// Called when the associated symbol is changed, but not when it is initially set!
|
||||||
|
virtual void onSymbolChange() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 <gfx/bezier.hpp>
|
||||||
|
#include <gfx/polynomial.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Evaluation
|
||||||
|
|
||||||
|
BezierCurve::BezierCurve(const Vector2D& p0, const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) {
|
||||||
|
// calculate coefficients
|
||||||
|
c = (p1 - p0) * 3.0;
|
||||||
|
b = (p2 - p1) * 3.0 - c;
|
||||||
|
a = (p3 - p0) - c - b;
|
||||||
|
d = p0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BezierCurve::BezierCurve(const ControlPoint& p0, const ControlPoint& p3) {
|
||||||
|
// calculate coefficients
|
||||||
|
c = p0.deltaAfter * 3.0;
|
||||||
|
b = (p3.pos + p3.deltaBefore - p0.pos - p0.deltaAfter) * 3.0 - c;
|
||||||
|
a = (p3.pos - p0.pos) - c - b;
|
||||||
|
d = p0.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void deCasteljau(Vector2D a1, Vector2D a2, Vector2D a3, Vector2D a4,
|
||||||
|
Vector2D& b2, Vector2D& b3, Vector2D& b4c1,
|
||||||
|
Vector2D& c2, Vector2D& c3, double t)
|
||||||
|
{
|
||||||
|
b2 = a1 + (a2 - a1) * t;
|
||||||
|
Vector2D mid23 = a2 + (a3 - a2) * t;
|
||||||
|
c3 = a3 + (a4 - a3) * t;
|
||||||
|
b3 = b2 + (mid23 - b2) * t;
|
||||||
|
c2 = mid23 + (c3 - mid23) * t;
|
||||||
|
b4c1 = b3 + (c2 - b3) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deCasteljau(ControlPoint& a, ControlPoint& b, double t, ControlPoint& mid) {
|
||||||
|
deCasteljau(a.pos, a.deltaAfter, b.deltaBefore, b.pos, t, mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deCasteljau(const Vector2D& a1, Vector2D& a21, Vector2D& a34, const Vector2D& a4, double t, ControlPoint& out) {
|
||||||
|
Vector2D half21 = a21 * t;
|
||||||
|
Vector2D half34 = a34 * (1-t);
|
||||||
|
Vector2D mid23 = (a1 + a21) * (1-t) + (a34 + a4) * t;
|
||||||
|
Vector2D mid23h21 = (a1 + half21) * (1-t) + mid23 * t;
|
||||||
|
Vector2D mid23h34 = (a4 + half34) * t + mid23 * (1-t);
|
||||||
|
out.pos = mid23h21 * (1-t) + mid23h34 * t;
|
||||||
|
out.deltaBefore = mid23h21 - out.pos;
|
||||||
|
out.deltaAfter = mid23h34 - out.pos;
|
||||||
|
a21 = half21;
|
||||||
|
a34 = half34;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Drawing
|
||||||
|
|
||||||
|
void curveSubdivide(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, const Rotation& rot, vector<wxPoint>& out, UInt level) {
|
||||||
|
if (level <= 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
|
||||||
|
curveSubdivide(c, p0, midpoint, t0, midtime, rot, out, level - 1);
|
||||||
|
// add midpoint
|
||||||
|
if (subdivide) {
|
||||||
|
out.push_back(rot.tr(midpoint));
|
||||||
|
}
|
||||||
|
// subdivide right
|
||||||
|
curveSubdivide(c, midpoint, p1, midtime, t1, rot, out, level - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void segmentSubdivide(const ControlPoint& p0, const ControlPoint& p1, const Rotation& rot, vector<wxPoint>& out) {
|
||||||
|
assert(p0.segmentAfter == p1.segmentBefore);
|
||||||
|
// always the start
|
||||||
|
out.push_back(rot.tr(p0.pos));
|
||||||
|
if (p0.segmentAfter == SEGMENT_CURVE) {
|
||||||
|
// need more points?
|
||||||
|
BezierCurve curve(p0,p1);
|
||||||
|
curveSubdivide(curve, p0.pos, p1.pos, 0, 1, rot, out, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Bounds
|
||||||
|
|
||||||
|
void segmentBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max) {
|
||||||
|
assert(p1.segmentAfter == p2.segmentBefore);
|
||||||
|
if (p1.segmentAfter == SEGMENT_LINE) {
|
||||||
|
lineBounds (p1.pos, p2.pos, min, max);
|
||||||
|
} else {
|
||||||
|
bezierBounds(p1, p2, min, max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bezierBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max) {
|
||||||
|
assert(p1.segmentAfter == SEGMENT_CURVE);
|
||||||
|
// First of all, the corners should be in the bounding box
|
||||||
|
pointBounds(p1.pos, min, max);
|
||||||
|
pointBounds(p2.pos, min, max);
|
||||||
|
// Solve the derivative of the bezier curve to find its extremes
|
||||||
|
// It's only a quadtratic equation :)
|
||||||
|
BezierCurve curve(p1,p2);
|
||||||
|
double roots[4];
|
||||||
|
UInt count;
|
||||||
|
count = solveQuadratic(3*curve.a.x, 2*curve.b.x, curve.c.x, roots);
|
||||||
|
count += solveQuadratic(3*curve.a.y, 2*curve.b.y, curve.c.y, roots + count);
|
||||||
|
// now check them for min/max
|
||||||
|
for (UInt i = 0 ; i < count ; ++i) {
|
||||||
|
double t = roots[i];
|
||||||
|
if (t >=0 && t <= 1) {
|
||||||
|
pointBounds(curve.pointAt(t), min, max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lineBounds(const Vector2D& p1, const Vector2D& p2, Vector2D& min, Vector2D& max) {
|
||||||
|
pointBounds(p1, min, max);
|
||||||
|
pointBounds(p2, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pointBounds(const Vector2D& p, Vector2D& min, Vector2D& max) {
|
||||||
|
min = piecewise_min(min, p);
|
||||||
|
max = piecewise_max(max, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is a point inside the bounds <min...max>?
|
||||||
|
bool pointInBounds(const Vector2D& p, const Vector2D& min, const Vector2D& max) {
|
||||||
|
return p.x >= min.x && p.y >= min.y &&
|
||||||
|
p.x <= max.x && p.y <= max.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Point tests
|
||||||
|
|
||||||
|
// As a point inside a symbol part?
|
||||||
|
bool pointInPart(const Vector2D& pos, const SymbolPart& part) {
|
||||||
|
// Step 1. compare bounding box of the part
|
||||||
|
if (!pointInBounds(pos, part.minPos, part.maxPos)) return false;
|
||||||
|
|
||||||
|
// Step 2. trace ray outward, count intersections
|
||||||
|
int count = 0;
|
||||||
|
size_t size = part.points.size();
|
||||||
|
for(size_t i = 0 ; i < size ; ++i) {
|
||||||
|
ControlPointP p1 = part.getPoint((int) i);
|
||||||
|
ControlPointP p2 = part.getPoint((int) i + 1);
|
||||||
|
if (p1->segmentAfter == SEGMENT_LINE) {
|
||||||
|
count += intersectLineRay (p1->pos, p2->pos, pos);
|
||||||
|
} else {
|
||||||
|
count += intersectBezierRay(*p1, *p2, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count & 1; // odd number of intersections
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Finding points
|
||||||
|
|
||||||
|
bool posOnSegment(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut) {
|
||||||
|
if (p1.segmentAfter == SEGMENT_CURVE) {
|
||||||
|
return posOnBezier(pos, range, p1, p2, pOut, tOut);
|
||||||
|
} else {
|
||||||
|
return posOnLine (pos, range, p1.pos, p2.pos, pOut, tOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool posOnBezier(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut) {
|
||||||
|
assert(p1.segmentAfter == SEGMENT_CURVE);
|
||||||
|
// Find intersections with the horizontal and vertical lines through p0
|
||||||
|
// theoretically we would need to check in all directions, but this covers enough
|
||||||
|
BezierCurve curve(p1, p2);
|
||||||
|
double roots[6];
|
||||||
|
UInt count;
|
||||||
|
count = solveCubic(curve.a.y, curve.b.y, curve.c.y, curve.d.y - pos.y, roots);
|
||||||
|
count += solveCubic(curve.a.x, curve.b.x, curve.c.x, curve.d.x - pos.x, roots + count); // append intersections
|
||||||
|
// take the best intersection point
|
||||||
|
double bestDistSqr = std::numeric_limits<double>::max(); //infinity
|
||||||
|
for(UInt i = 0 ; i < count ; ++i) {
|
||||||
|
double t = roots[i];
|
||||||
|
if (t >= 0 && t < 1) {
|
||||||
|
Vector2D pnt = curve.pointAt(t);
|
||||||
|
double distSqr = (pnt - pos).lengthSqr();
|
||||||
|
if (distSqr < bestDistSqr) {
|
||||||
|
bestDistSqr = distSqr;
|
||||||
|
pOut = pnt;
|
||||||
|
tOut = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestDistSqr <= range * range;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool posOnLine(const Vector2D& pos, double range, const Vector2D& p1, const Vector2D& p2, Vector2D& pOut, double& t) {
|
||||||
|
Vector2D p21 = p2 - p1;
|
||||||
|
double p21len = p21.lengthSqr();
|
||||||
|
if (p21len < 0.00001) return false; // line is too short
|
||||||
|
t = p21.dot(pos - p1) / p21len; // 'time' on line p1->p2
|
||||||
|
if (t < 0 || t > 1) return false; // outside segment
|
||||||
|
pOut = p1 + p21 * t; // point on line
|
||||||
|
Vector2D dist = pOut - pos; // distance to line
|
||||||
|
return dist.lengthSqr() <= range * range; // in range?
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Intersection
|
||||||
|
|
||||||
|
UInt intersectBezierRay(const ControlPoint& p1, const ControlPoint& p2, const Vector2D& pos) {
|
||||||
|
// Looking only at the y coordinate
|
||||||
|
// we can use the cubic formula to find roots, points where the horizontal line
|
||||||
|
// through pos intersects the (extended) curve
|
||||||
|
BezierCurve curve(p1,p2);
|
||||||
|
double roots[3];
|
||||||
|
UInt count = solveCubic(curve.a.y, curve.b.y, curve.c.y, curve.d.y - pos.y, roots);
|
||||||
|
// now check if the solutions are left of pos.x
|
||||||
|
UInt solsInRange = 0;
|
||||||
|
for(UInt i = 0 ; i < count ; ++i) {
|
||||||
|
double t = roots[i];
|
||||||
|
if (t >= 0 && t < 1 && curve.pointAt(t).x < pos.x) {
|
||||||
|
solsInRange += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return solsInRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool intersectLineRay(const Vector2D& p1, const Vector2D& p2, const Vector2D& pos) {
|
||||||
|
// Vector2D intersection = p1 + t * (p2 - p1)
|
||||||
|
// intersection.y == pos.y
|
||||||
|
// == p1.y + t * (p2.y - p1.y)
|
||||||
|
// => t == (pos.y - p1.y) / (p2.y - p1.y)
|
||||||
|
// intersection.x == p1.x + t * (p2.x - p1.x)
|
||||||
|
// == p1.x + (pos.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y)
|
||||||
|
double dy = p2.y - p1.y;
|
||||||
|
if (fabs(dy) < 0.0000000001) {
|
||||||
|
// horizontal line
|
||||||
|
return (p1.x > pos.x || p2.x > pos.x) && // starts to the left of pos
|
||||||
|
fabs(p1.y - pos.y) < 0.0000000001; // same y as pos
|
||||||
|
} else {
|
||||||
|
double dx = p2.x - p1.x;
|
||||||
|
double t = (pos.y - p1.y) / dy;
|
||||||
|
if (t < 0.0 || t >= 1.0) return false;
|
||||||
|
double intersectX = p1.x + t * dx;
|
||||||
|
return intersectX <= pos.x; // intersection is left of pos
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_GFX_BEZIER
|
||||||
|
#define HEADER_GFX_BEZIER
|
||||||
|
|
||||||
|
/** @file gfx/bezier.hpp
|
||||||
|
*
|
||||||
|
* Bezier curve and line related functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/prec.hpp>
|
||||||
|
#include <util/rotation.hpp>
|
||||||
|
#include <data/symbol.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Evaluation
|
||||||
|
|
||||||
|
/// A bezier curve for evaluation
|
||||||
|
class BezierCurve {
|
||||||
|
public:
|
||||||
|
/// coefficients of the equation (x,y) = at^3 + bt^2 + ct + d
|
||||||
|
Vector2D a, b, c, d;
|
||||||
|
|
||||||
|
/// Construct a bezier curve evaluator given the 4 handles
|
||||||
|
BezierCurve(const Vector2D& p0, const Vector2D& p1, const Vector2D& p2, const Vector2D& p3);
|
||||||
|
|
||||||
|
/// Construct a bezier curve evaluator given two ControlPoints at the ends
|
||||||
|
BezierCurve(const ControlPoint& p0, const ControlPoint& p3);
|
||||||
|
|
||||||
|
/// Return the point on this curve at time t in [0...1)
|
||||||
|
inline Vector2D pointAt(double t) const {
|
||||||
|
return d + (c + (b + a * t) * t) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the tangent on this curve at time t in [0...1)
|
||||||
|
inline Vector2D tangentAt(double t) const {
|
||||||
|
return c + ((b * 2) + (a * 3) * t) * t;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Subdivide a curve from a to b, store the result in a control point
|
||||||
|
/** Also modifies the handles of the points to accomodate the inserted point
|
||||||
|
* Direct version, using input curve a1,a2,a3,a4 and output curves a1,b2,b3,b4 and c1,c2,c3,a4
|
||||||
|
*/
|
||||||
|
void deCasteljau(Vector2D a1, Vector2D a2, Vector2D a3, Vector2D a4,
|
||||||
|
Vector2D& b2, Vector2D& b3, Vector2D& b4c1,
|
||||||
|
Vector2D& c2, Vector2D& c3, double t);
|
||||||
|
|
||||||
|
/// Subdivide a curve from a to b at time t
|
||||||
|
/** Stores the point at time t in mid, updates the handles of a and b!
|
||||||
|
*/
|
||||||
|
void deCasteljau(ControlPoint& a, ControlPoint& b, double t, ControlPoint& mid);
|
||||||
|
|
||||||
|
/// Subdivide a curve from a to b, store the result in a control point
|
||||||
|
/** Also modifies the handles of the points to accomodate the inserted point!
|
||||||
|
*/
|
||||||
|
void deCasteljau(const Vector2D& a1, Vector2D& a21, Vector2D& a34, const Vector2D& a4, double t, ControlPoint& out);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Drawing
|
||||||
|
|
||||||
|
/// Devide a segment into a number of straight lines for display purposes
|
||||||
|
/** Adds the resulting corner points of those lines to out, the last point is not added.
|
||||||
|
* All points are converted to display coordinates using rot.tr
|
||||||
|
*/
|
||||||
|
void segmentSubdivide(const ControlPoint& p0, const ControlPoint& p1, const Rotation& rot, vector<wxPoint>& out);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Bounds
|
||||||
|
|
||||||
|
/// Find a bounding box that fits a segment (either a line or a bezier curve) between p1 and p2.
|
||||||
|
/** stores the results in min and max.
|
||||||
|
* min is only changed if the minimum is smaller then the current value in min,
|
||||||
|
* max only if the maximum is larger.
|
||||||
|
*/
|
||||||
|
void segmentBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max);
|
||||||
|
|
||||||
|
/// Find a bounding box that fits a curve between p1 and p2, stores the results in min and max.
|
||||||
|
/** min is only changed if the minimum is smaller then the current value in min,
|
||||||
|
* max only if the maximum is larger
|
||||||
|
*/
|
||||||
|
void bezierBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max);
|
||||||
|
|
||||||
|
/// Find a bounding box that fits around p1 and p2, stores the result in min and max
|
||||||
|
/** min is only changed if the minimum is smaller then the current value in min,
|
||||||
|
* max only if the maximum is larger
|
||||||
|
*/
|
||||||
|
void lineBounds(const Vector2D& p1, const Vector2D& p2, Vector2D& min, Vector2D& max);
|
||||||
|
|
||||||
|
/// Find a bounding 'box' that fits around a single point
|
||||||
|
/** min is only changed if the minimum is smaller then the current value in min,
|
||||||
|
* max only if the maximum is larger
|
||||||
|
*/
|
||||||
|
void pointBounds(const Vector2D& p, Vector2D& min, Vector2D& max);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Point tests
|
||||||
|
|
||||||
|
/// Is a point inside the given symbol part?
|
||||||
|
bool pointInPart(const Vector2D& p, const SymbolPart& part);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Finding points
|
||||||
|
|
||||||
|
/// Finds the position of p0 on the line between p1 and p2, returns true if the point is on (or near) the line
|
||||||
|
/// the line between p1 and p2 can also be a bezier curve
|
||||||
|
/** Returns the time on the segment in tOut, and the point on the segment in pOut
|
||||||
|
*/
|
||||||
|
bool posOnSegment(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut);
|
||||||
|
|
||||||
|
/// Finds the position of p0 on the line between p1 and p2, returns true if the point is on (or near)
|
||||||
|
/// the bezier curve between p1 and p2
|
||||||
|
bool posOnBezier (const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut);
|
||||||
|
|
||||||
|
/// Finds the position of p0 on the line p1-p2, returns true if the point is withing range of the line
|
||||||
|
/// if that is the case then (x,y) = p1 + (p2-p1) * out
|
||||||
|
bool posOnLine (const Vector2D& pos, double range, const Vector2D& p1, const Vector2D& p2, Vector2D& pOut, double& tOut);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Intersection
|
||||||
|
|
||||||
|
/// Counts the number of intersections between the ray/halfline from (-inf, pos.y) to pos
|
||||||
|
/// and the bezier curve between p1 and p2.
|
||||||
|
UInt intersectBezierRay(const ControlPoint& p1, const ControlPoint& p2, const Vector2D& pos);
|
||||||
|
|
||||||
|
// Does the line between p1 and p2 intersect the ray (half line) from (-inf, pos.y) to pos?
|
||||||
|
bool intersectLineRay(const Vector2D& p1, const Vector2D& p2, const Vector2D& pos);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 "../util/prec.hpp"
|
||||||
|
#include "../util/reflect.hpp"
|
||||||
|
#include "gfx.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Reflection for combining modes
|
||||||
|
|
||||||
|
IMPLEMENT_REFLECTION_ENUM(ImageCombine) {
|
||||||
|
VALUE_N("normal", COMBINE_NORMAL);
|
||||||
|
VALUE_N("add", COMBINE_ADD);
|
||||||
|
VALUE_N("subtract", COMBINE_SUBTRACT);
|
||||||
|
VALUE_N("stamp", COMBINE_STAMP);
|
||||||
|
VALUE_N("difference", COMBINE_DIFFERENCE);
|
||||||
|
VALUE_N("negation", COMBINE_NEGATION);
|
||||||
|
VALUE_N("multiply", COMBINE_MULTIPLY);
|
||||||
|
VALUE_N("darken", COMBINE_DARKEN);
|
||||||
|
VALUE_N("lighten", COMBINE_LIGHTEN);
|
||||||
|
VALUE_N("color dodge", COMBINE_COLOR_DODGE);
|
||||||
|
VALUE_N("color burn", COMBINE_COLOR_BURN);
|
||||||
|
VALUE_N("screen", COMBINE_SCREEN);
|
||||||
|
VALUE_N("overlay", COMBINE_OVERLAY);
|
||||||
|
VALUE_N("hard light", COMBINE_HARD_LIGHT);
|
||||||
|
VALUE_N("soft light", COMBINE_SOFT_LIGHT);
|
||||||
|
VALUE_N("reflect", COMBINE_REFLECT);
|
||||||
|
VALUE_N("glow", COMBINE_GLOW);
|
||||||
|
VALUE_N("freeze", COMBINE_FREEZE);
|
||||||
|
VALUE_N("heat", COMBINE_HEAT);
|
||||||
|
VALUE_N("and", COMBINE_AND);
|
||||||
|
VALUE_N("or", COMBINE_OR);
|
||||||
|
VALUE_N("xor", COMBINE_XOR);
|
||||||
|
VALUE_N("shadow", COMBINE_SHADOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Combining functions
|
||||||
|
|
||||||
|
// Functor for combining functions for a given combining type
|
||||||
|
template <ImageCombine combine> struct Combine {
|
||||||
|
static inline int f(int a, int b);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Give a combining function for enum value 'combine'
|
||||||
|
#define COMBINE_FUN(combine,fun) \
|
||||||
|
template <> int Combine<combine>::f(int a, int b) { return fun; }
|
||||||
|
|
||||||
|
// Based on
|
||||||
|
// http://www.pegtop.net/delphi/articles/blendmodes/
|
||||||
|
|
||||||
|
COMBINE_FUN(COMBINE_NORMAL, b )
|
||||||
|
COMBINE_FUN(COMBINE_ADD, top(a + b) )
|
||||||
|
COMBINE_FUN(COMBINE_SUBTRACT, bot(a - b) )
|
||||||
|
COMBINE_FUN(COMBINE_STAMP, col(a - 2 * b + 256) )
|
||||||
|
COMBINE_FUN(COMBINE_DIFFERENCE, abs(a - b) )
|
||||||
|
COMBINE_FUN(COMBINE_NEGATION, 255 - abs(255 - a - b) )
|
||||||
|
COMBINE_FUN(COMBINE_MULTIPLY, (a * b) / 255 )
|
||||||
|
COMBINE_FUN(COMBINE_DARKEN, min(a, b) )
|
||||||
|
COMBINE_FUN(COMBINE_LIGHTEN, max(a, b) )
|
||||||
|
COMBINE_FUN(COMBINE_COLOR_DODGE,b == 255 ? 255 : top(a * 255 / (255 - b)) )
|
||||||
|
COMBINE_FUN(COMBINE_COLOR_BURN, b == 0 ? 0 : bot(255 - (255-a) * 255 / b) )
|
||||||
|
COMBINE_FUN(COMBINE_SCREEN, 255 - (((255 - a) * (255 - b)) / 255) )
|
||||||
|
COMBINE_FUN(COMBINE_OVERLAY, a < 128
|
||||||
|
? (a * b) >> 7
|
||||||
|
: 255 - (((255 - a) * (255 - b)) >> 7) )
|
||||||
|
COMBINE_FUN(COMBINE_HARD_LIGHT, b < 128
|
||||||
|
? (a * b) >> 7
|
||||||
|
: 255 - (((255 - a) * (255 - b)) >> 7) )
|
||||||
|
COMBINE_FUN(COMBINE_SOFT_LIGHT, b)
|
||||||
|
COMBINE_FUN(COMBINE_REFLECT, b == 255 ? 255 : top(a * a / (255 - b)) )
|
||||||
|
COMBINE_FUN(COMBINE_GLOW, a == 255 ? 255 : top(b * b / (255 - a)) )
|
||||||
|
COMBINE_FUN(COMBINE_FREEZE, b == 0 ? 0 : bot(255 - (255 - a) * (255 - a) / b) )
|
||||||
|
COMBINE_FUN(COMBINE_HEAT, a == 0 ? 0 : bot(255 - (255 - b) * (255 - b) / a) )
|
||||||
|
COMBINE_FUN(COMBINE_AND, a & b )
|
||||||
|
COMBINE_FUN(COMBINE_OR, a | b )
|
||||||
|
COMBINE_FUN(COMBINE_XOR, a ^ b )
|
||||||
|
COMBINE_FUN(COMBINE_SHADOW, (b * a * a) / (255 * 255) )
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Combining
|
||||||
|
|
||||||
|
/// Combine image b onto image a using some combining mode.
|
||||||
|
/// The results are stored in the image A.
|
||||||
|
template <ImageCombine combine>
|
||||||
|
void combineImageDo(Image& a, Image b) {
|
||||||
|
UInt size = a.GetWidth() * a.GetHeight() * 3;
|
||||||
|
Byte *dataA = a.GetData(), *dataB = b.GetData();
|
||||||
|
// for each pixel: apply function
|
||||||
|
for (UInt i = 0 ; i < size ; ++i) {
|
||||||
|
dataA[i] = Combine<combine>::f(dataA[i], dataB[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void combineImage(Image& a, const Image& b, ImageCombine combine) {
|
||||||
|
// Images must have same size
|
||||||
|
assert(a.GetWidth() == b.GetWidth());
|
||||||
|
assert(a.GetHeight() == b.GetHeight());
|
||||||
|
// Copy alpha channel?
|
||||||
|
if (b.HasAlpha()) {
|
||||||
|
if (!a.HasAlpha()) a.InitAlpha();
|
||||||
|
memcpy(a.GetAlpha(), b.GetAlpha(), a.GetWidth() * a.GetHeight());
|
||||||
|
}
|
||||||
|
// Combine image data, by dispatching to combineImageDo
|
||||||
|
switch(combine) {
|
||||||
|
#define DISPATCH(comb) case comb: combineImageDo<comb>(a,b); return
|
||||||
|
DISPATCH(COMBINE_NORMAL);
|
||||||
|
DISPATCH(COMBINE_ADD);
|
||||||
|
DISPATCH(COMBINE_SUBTRACT);
|
||||||
|
DISPATCH(COMBINE_STAMP);
|
||||||
|
DISPATCH(COMBINE_DIFFERENCE);
|
||||||
|
DISPATCH(COMBINE_NEGATION);
|
||||||
|
DISPATCH(COMBINE_MULTIPLY);
|
||||||
|
DISPATCH(COMBINE_DARKEN);
|
||||||
|
DISPATCH(COMBINE_LIGHTEN);
|
||||||
|
DISPATCH(COMBINE_COLOR_DODGE);
|
||||||
|
DISPATCH(COMBINE_COLOR_BURN);
|
||||||
|
DISPATCH(COMBINE_SCREEN);
|
||||||
|
DISPATCH(COMBINE_OVERLAY);
|
||||||
|
DISPATCH(COMBINE_HARD_LIGHT);
|
||||||
|
DISPATCH(COMBINE_SOFT_LIGHT);
|
||||||
|
DISPATCH(COMBINE_REFLECT);
|
||||||
|
DISPATCH(COMBINE_GLOW);
|
||||||
|
DISPATCH(COMBINE_FREEZE);
|
||||||
|
DISPATCH(COMBINE_HEAT);
|
||||||
|
DISPATCH(COMBINE_AND);
|
||||||
|
DISPATCH(COMBINE_OR);
|
||||||
|
DISPATCH(COMBINE_XOR);
|
||||||
|
DISPATCH(COMBINE_SHADOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawCombineImage(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine) {
|
||||||
|
}
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_GFX_GFX
|
||||||
|
#define HEADER_GFX_GFX
|
||||||
|
|
||||||
|
/** @file gfx/gfx.hpp
|
||||||
|
*
|
||||||
|
* Graphics/image processing functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include "../util/prec.hpp"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Resampling
|
||||||
|
|
||||||
|
/// Resample (resize) an image, uses bilenear filtering
|
||||||
|
/** The algorithm first resizes in horizontally, then vertically,
|
||||||
|
* the two passes are essentially the same:
|
||||||
|
* - for each row:
|
||||||
|
* - each input pixel becomes a fixed amount of output (in 1<<shift fixed point math)
|
||||||
|
* - for each output pixel:
|
||||||
|
* - 'eat' input pixels until the total is 1<<shift
|
||||||
|
* - write the total to the output pixel
|
||||||
|
* - to ensure the sum of all the pixel amounts is exacly width<<shift an extra rest amount
|
||||||
|
* is 'eaten' from the first pixel
|
||||||
|
*
|
||||||
|
* Uses fixed point numbers internally
|
||||||
|
*/
|
||||||
|
void resample(const Image& imgIn, Image& imgOut);
|
||||||
|
|
||||||
|
/// Resamples an image, first clips the input image to a specified rectangle,
|
||||||
|
/// that rectangle is resampledinto the entire output image
|
||||||
|
void resample_and_clip(const Image& imgIn, Image& imgOut, wxRect rect);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Image rotation
|
||||||
|
|
||||||
|
/// Rotates an image counter clockwise
|
||||||
|
/// angle must be a multiple of 90, i.e. {0,90,180,270}
|
||||||
|
Image rotateImageBy(const Image& image, int angle);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Blending
|
||||||
|
|
||||||
|
/// Blends two images together, using a horizontal gradient
|
||||||
|
/** The result is stored in img1
|
||||||
|
* To the left the color is that of img1, to the right of img2
|
||||||
|
*/
|
||||||
|
void hblend(Image& img1, const Image& img2);
|
||||||
|
|
||||||
|
/// Blends two images together, using a vertical gradient
|
||||||
|
void vblend(Image& img1, const Image& img2);
|
||||||
|
|
||||||
|
/// Blends two images together, using a third image as a mask
|
||||||
|
/** The result is stored in img1
|
||||||
|
* mask is used as a mask, white pixels are taken from img1, black pixels from img2
|
||||||
|
* color channels are blended separatly
|
||||||
|
*/
|
||||||
|
void maskBlend(Image& img1, const Image& img2, const Image& mask);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Combining
|
||||||
|
|
||||||
|
/// Ways in which images can be combined, similair to what Photoshop supports
|
||||||
|
enum ImageCombine
|
||||||
|
{ COMBINE_NORMAL
|
||||||
|
, COMBINE_ADD
|
||||||
|
, COMBINE_SUBTRACT
|
||||||
|
, COMBINE_STAMP
|
||||||
|
, COMBINE_DIFFERENCE
|
||||||
|
, COMBINE_NEGATION
|
||||||
|
, COMBINE_MULTIPLY
|
||||||
|
, COMBINE_DARKEN
|
||||||
|
, COMBINE_LIGHTEN
|
||||||
|
, COMBINE_COLOR_DODGE
|
||||||
|
, COMBINE_COLOR_BURN
|
||||||
|
, COMBINE_SCREEN
|
||||||
|
, COMBINE_OVERLAY
|
||||||
|
, COMBINE_HARD_LIGHT
|
||||||
|
, COMBINE_SOFT_LIGHT
|
||||||
|
, COMBINE_REFLECT
|
||||||
|
, COMBINE_GLOW
|
||||||
|
, COMBINE_FREEZE
|
||||||
|
, COMBINE_HEAT
|
||||||
|
, COMBINE_AND
|
||||||
|
, COMBINE_OR
|
||||||
|
, COMBINE_XOR
|
||||||
|
, COMBINE_SHADOW
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Combine image b onto image a using some combining function.
|
||||||
|
/// The results are stored in the image A.
|
||||||
|
/// This image gets the alpha channel from B, it should then be
|
||||||
|
/// drawn onto the area where A originated.
|
||||||
|
void combineImage(Image& a, const Image& b, ImageCombine combine);
|
||||||
|
|
||||||
|
/// Draw an image to a DC using a combining function
|
||||||
|
void drawCombineImage(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Utility
|
||||||
|
|
||||||
|
inline int bot(int x) { return max(0, x); } //^ bottom range check for color values
|
||||||
|
inline int top(int x) { return min(255, x); } //^ top range check for color values
|
||||||
|
inline int col(int x) { return top(bot(x)); } //^ top and bottom range check for color values
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 <gfx/polynomial.hpp>
|
||||||
|
#include <complex>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Solving
|
||||||
|
|
||||||
|
UInt solveLinear(double a, double b, double* root) {
|
||||||
|
if (a == 0) {
|
||||||
|
if (b == 0) {
|
||||||
|
root[0] = 0;
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root[0] = -b / a;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt solveQuadratic(double a, double b, double c, double* roots) {
|
||||||
|
if (a == 0) {
|
||||||
|
return solveLinear(b, c, roots);
|
||||||
|
} else {
|
||||||
|
double d = b*b - 4*a*c;
|
||||||
|
if (d < 0) return 0;
|
||||||
|
roots[0] = (-b - sqrt(d)) / (2*a);
|
||||||
|
roots[1] = (-b + sqrt(d)) / (2*a);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt solveCubic(double a, double b, double c, double d, double* roots) {
|
||||||
|
if (a == 0) {
|
||||||
|
return solveQuadratic(b, c, d, roots);
|
||||||
|
} else {
|
||||||
|
return solveCubic(b/a, c/a, d/a, roots);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cubic root
|
||||||
|
template <typename T>
|
||||||
|
inline T curt(T x) { return pow(x, 1.0 / 3); }
|
||||||
|
|
||||||
|
UInt solveCubic(double a, double b, double c, double* roots) {
|
||||||
|
double p = b - a*a / 3;
|
||||||
|
double q = c + (2 * a*a*a - 9 * a * b) / 27;
|
||||||
|
if (p == 0 && q == 0) {
|
||||||
|
roots[0] = -a / 3;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
complex<double> u;
|
||||||
|
if (q > 0) {
|
||||||
|
u = curt(q/2 + sqrt(complex<double>(q*q / 4 + p*p*p / 27)));
|
||||||
|
} else {
|
||||||
|
u = curt(q/2 - sqrt(complex<double>(q*q / 4 + p*p*p / 27)));
|
||||||
|
}
|
||||||
|
// now for the complex part
|
||||||
|
// rot1(1, 0)
|
||||||
|
complex<double> rot2(-0.5, sqrt(3.0) / 2);
|
||||||
|
complex<double> rot3(-0.5, -sqrt(3.0) / 2);
|
||||||
|
complex<double> x1 = p / (3.0 * u) - u - a / 3.0;
|
||||||
|
complex<double> x2 = p / (3.0 * u * rot2) - u * rot2 - a / 3.0;
|
||||||
|
complex<double> x3 = p / (3.0 * u * rot3) - u * rot3 - a / 3.0;
|
||||||
|
// check if the solutions are real
|
||||||
|
UInt count = 0;
|
||||||
|
if (abs(x1.imag()) < 0.00001) {
|
||||||
|
roots[count] = x1.real();
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
if (abs(x2.imag()) < 0.00001) {
|
||||||
|
roots[count] = x2.real();
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
if (abs(x3.imag()) < 0.00001) {
|
||||||
|
roots[count] = x3.real();
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
@@ -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) |
|
||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
#ifndef HEADER_GFX_POLYNOMIAL
|
||||||
|
#define HEADER_GFX_POLYNOMIAL
|
||||||
|
|
||||||
|
/** @file gfx/polynomial.hpp
|
||||||
|
*
|
||||||
|
* Solutions to polynomials, used by bezier curve algorithms
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/prec.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Solving
|
||||||
|
|
||||||
|
/// Solve a linear equation a x + b = 0
|
||||||
|
/** Returns the number of real roots, and the roots themselfs in the output parameter.
|
||||||
|
*/
|
||||||
|
UInt solveLinear(double a, double b, double* root);
|
||||||
|
|
||||||
|
/// Solve a quadratic equation a x^2 + b x + c == 0
|
||||||
|
/** Returns the number of real roots, and the roots themselfs in the output parameter.
|
||||||
|
*/
|
||||||
|
UInt solveQuadratic(double a, double b, double c, double* roots);
|
||||||
|
|
||||||
|
// Solve a cubic equation a x^3 + b x^2 + c x + d == 0
|
||||||
|
/** Returns the number of real roots, and the roots themselfs in the output parameter.
|
||||||
|
*/
|
||||||
|
UInt solveCubic(double a, double b, double c, double d, double* roots);
|
||||||
|
|
||||||
|
// Solve a cubic equation x^3 + a x^2 + b x + c == 0
|
||||||
|
/** Returns the number of real roots, and the roots themselfs in the output parameter.
|
||||||
|
* Based on http://en.wikipedia.org/wiki/Cubic_equation
|
||||||
|
*/
|
||||||
|
UInt solveCubic(double a, double b, double c, double* roots);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 "../util/prec.hpp"
|
||||||
|
#include "gfx.hpp"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Implementation
|
||||||
|
|
||||||
|
// Rotates an image
|
||||||
|
// 'Rotater' is a function object that knows how to 'rotate' a pixel coordinate
|
||||||
|
template <class Rotater>
|
||||||
|
Image rotateImageImpl(Image img) {
|
||||||
|
UInt width = img.GetWidth(), height = img.GetHeight();
|
||||||
|
// initialize the return image
|
||||||
|
Image ret;
|
||||||
|
Rotater::init(ret, width, height);
|
||||||
|
Byte* in = img.GetData(), *out = ret.GetData();
|
||||||
|
// rotate each pixel
|
||||||
|
for (UInt y = 0 ; y < height ; ++y) {
|
||||||
|
for (UInt x = 0 ; x < width ; ++x) {
|
||||||
|
memcpy(out + 3 * Rotater::offset(x, y, width, height), in, 3);
|
||||||
|
in += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// don't forget alpha
|
||||||
|
if (img.HasAlpha()) {
|
||||||
|
ret.InitAlpha();
|
||||||
|
in = img.GetAlpha();
|
||||||
|
out = ret.GetAlpha();
|
||||||
|
for (UInt y = 0 ; y < height ; ++y) {
|
||||||
|
for (UInt x = 0 ; x < width ; ++x) {
|
||||||
|
out[Rotater::offset(x, y, width, height)] = *in;
|
||||||
|
in += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ret is rotated image
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Rotations
|
||||||
|
|
||||||
|
// Function object to handle rotation
|
||||||
|
struct Rotate90 {
|
||||||
|
/// Init a rotated image, where the source is w * h pixels
|
||||||
|
inline static void init(Image& img, UInt w, UInt h) {
|
||||||
|
img.Create(h, w, false);
|
||||||
|
}
|
||||||
|
/// Offset in the target data, x, y, w, h are SOURCE coordintes
|
||||||
|
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
|
||||||
|
int mx = y;
|
||||||
|
int my = w - x - 1;
|
||||||
|
return h * my + mx; // note: h, since that is the width of the target image
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rotate180 {
|
||||||
|
inline static void init(Image& img, UInt w, UInt h) {
|
||||||
|
img.Create(w, h, false);
|
||||||
|
}
|
||||||
|
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
|
||||||
|
UInt mx = w - x - 1;
|
||||||
|
UInt my = h - y - 1;
|
||||||
|
return w * my + mx;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rotate270 {
|
||||||
|
inline static void init(Image& img, UInt w, UInt h) {
|
||||||
|
img.Create(h, w, false);
|
||||||
|
}
|
||||||
|
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
|
||||||
|
UInt mx = h - y - 1;
|
||||||
|
UInt my = x;
|
||||||
|
return h * my + mx;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Interface
|
||||||
|
|
||||||
|
Image rotateImageBy(const Image& image, int angle) {
|
||||||
|
if (angle == 90) {
|
||||||
|
return rotateImageImpl<Rotate90>(image);
|
||||||
|
} else if (angle == 180){
|
||||||
|
return rotateImageImpl<Rotate180>(image);
|
||||||
|
} else if (angle == 270){
|
||||||
|
return rotateImageImpl<Rotate270>(image);
|
||||||
|
} else{
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
Microsoft Visual Studio Solution File, Format Version 8.00
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mse", "mse.vcproj", "{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfiguration) = preSolution
|
||||||
|
Debug = Debug
|
||||||
|
Debug Unicode = Debug Unicode
|
||||||
|
Release = Release
|
||||||
|
Release Profile Unicode = Release Profile Unicode
|
||||||
|
Release Unicode = Release Unicode
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfiguration) = postSolution
|
||||||
|
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Debug.ActiveCfg = Debug|Win32
|
||||||
|
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Debug.Build.0 = Debug|Win32
|
||||||
|
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Debug Unicode.ActiveCfg = Debug Unicode|Win32
|
||||||
|
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Debug Unicode.Build.0 = Debug Unicode|Win32
|
||||||
|
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Release.ActiveCfg = Release|Win32
|
||||||
|
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Release.Build.0 = Release|Win32
|
||||||
|
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Release Profile Unicode.ActiveCfg = Release Unicode|Win32
|
||||||
|
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Release Profile Unicode.Build.0 = Release Unicode|Win32
|
||||||
|
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Release Unicode.ActiveCfg = Release Unicode|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityAddIns) = postSolution
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
+846
@@ -0,0 +1,846 @@
|
|||||||
|
<?xml version="1.0" encoding="Windows-1252"?>
|
||||||
|
<VisualStudioProject
|
||||||
|
ProjectType="Visual C++"
|
||||||
|
Version="7.10"
|
||||||
|
Name="mse"
|
||||||
|
ProjectGUID="{DB9DCD62-4679-47DD-8BEE-8A0191161A51}"
|
||||||
|
Keyword="Win32Proj">
|
||||||
|
<Platforms>
|
||||||
|
<Platform
|
||||||
|
Name="Win32"/>
|
||||||
|
</Platforms>
|
||||||
|
<Configurations>
|
||||||
|
<Configuration
|
||||||
|
Name="Debug|Win32"
|
||||||
|
OutputDirectory="../build/$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="../build/$(ConfigurationName)"
|
||||||
|
ConfigurationType="1"
|
||||||
|
CharacterSet="2">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="0"
|
||||||
|
AdditionalIncludeDirectories=".; "$(WX_PATH)/include"; "$(WX_PATH)\lib\vc_lib\mswd""
|
||||||
|
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;__WINDOWS__;__WXMSW__;DEBUG=1;__WXDEBUG__;__WIN95__;__WIN32__;WINVER=0x0400;STRICT"
|
||||||
|
MinimalRebuild="TRUE"
|
||||||
|
BasicRuntimeChecks="3"
|
||||||
|
RuntimeLibrary="5"
|
||||||
|
BufferSecurityCheck="TRUE"
|
||||||
|
RuntimeTypeInfo="TRUE"
|
||||||
|
UsePrecompiledHeader="2"
|
||||||
|
PrecompiledHeaderThrough="util/prec.hpp"
|
||||||
|
PrecompiledHeaderFile=""
|
||||||
|
AssemblerListingLocation="$(OutDir)"
|
||||||
|
ProgramDataBaseFileName="$(IntDir)/vc70.pdb"
|
||||||
|
BrowseInformation="1"
|
||||||
|
WarningLevel="4"
|
||||||
|
WarnAsError="TRUE"
|
||||||
|
Detect64BitPortabilityProblems="TRUE"
|
||||||
|
DebugInformationFormat="4"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalDependencies="rpcrt4.lib wsock32.lib comctl32.lib odbc32.lib odbccp32.lib wxbase26d.lib wxmsw26d_core.lib"
|
||||||
|
OutputFile="$(OutDir)/mse.exe"
|
||||||
|
LinkIncremental="2"
|
||||||
|
IgnoreDefaultLibraryNames="libcd.lib,libcid.lib"
|
||||||
|
GenerateDebugInformation="TRUE"
|
||||||
|
ProgramDatabaseFile="$(OutDir)/mse.pdb"
|
||||||
|
SubSystem="2"
|
||||||
|
TargetMachine="1"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
PreprocessorDefinitions="_DEBUG"
|
||||||
|
MkTypLibCompatible="TRUE"
|
||||||
|
SuppressStartupBanner="TRUE"
|
||||||
|
TargetEnvironment="1"
|
||||||
|
TypeLibraryName="$(OutDir)/mse.tlb"
|
||||||
|
HeaderFileName=""/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebDeploymentTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedWrapperGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||||
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Release|Win32"
|
||||||
|
OutputDirectory="../build/$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="../build/$(ConfigurationName)"
|
||||||
|
ConfigurationType="1"
|
||||||
|
CharacterSet="2"
|
||||||
|
WholeProgramOptimization="TRUE">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="2"
|
||||||
|
GlobalOptimizations="TRUE"
|
||||||
|
InlineFunctionExpansion="1"
|
||||||
|
OmitFramePointers="TRUE"
|
||||||
|
OptimizeForProcessor="0"
|
||||||
|
OptimizeForWindowsApplication="TRUE"
|
||||||
|
AdditionalIncludeDirectories=".; "$(WX_PATH)/include"; "$(WX_PATH)\lib\vc_lib\msw""
|
||||||
|
PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;__WINDOWS__;__WXMSW__;__WIN95__;__WIN32__;WINVER=0x0400;STRICT"
|
||||||
|
StringPooling="TRUE"
|
||||||
|
RuntimeLibrary="4"
|
||||||
|
BufferSecurityCheck="FALSE"
|
||||||
|
EnableFunctionLevelLinking="TRUE"
|
||||||
|
RuntimeTypeInfo="TRUE"
|
||||||
|
UsePrecompiledHeader="2"
|
||||||
|
PrecompiledHeaderThrough="util/prec.hpp"
|
||||||
|
PrecompiledHeaderFile=""
|
||||||
|
AssemblerListingLocation="$(OutDir)"
|
||||||
|
ProgramDataBaseFileName="$(IntDir)/vc70.pdb"
|
||||||
|
WarningLevel="3"
|
||||||
|
WarnAsError="FALSE"
|
||||||
|
Detect64BitPortabilityProblems="TRUE"
|
||||||
|
DebugInformationFormat="3"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalDependencies="rpcrt4.lib wsock32.lib comctl32.lib wxbase26.lib wxmsw26_core.lib wxjpeg.lib wxpng.lib wxtiff.lib wxzlib.lib"
|
||||||
|
OutputFile="$(OutDir)/mse.exe"
|
||||||
|
LinkIncremental="1"
|
||||||
|
SuppressStartupBanner="TRUE"
|
||||||
|
IgnoreDefaultLibraryNames="libc.lib,libci.lib"
|
||||||
|
GenerateDebugInformation="TRUE"
|
||||||
|
ProgramDatabaseFile="$(OutDir)/mse.pdb"
|
||||||
|
SubSystem="2"
|
||||||
|
OptimizeReferences="2"
|
||||||
|
EnableCOMDATFolding="2"
|
||||||
|
TargetMachine="1"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
PreprocessorDefinitions="NDEBUG"
|
||||||
|
MkTypLibCompatible="TRUE"
|
||||||
|
SuppressStartupBanner="TRUE"
|
||||||
|
TargetEnvironment="1"
|
||||||
|
TypeLibraryName="$(OutDir)/mse.tlb"
|
||||||
|
HeaderFileName=""/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebDeploymentTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedWrapperGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||||
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Debug Unicode|Win32"
|
||||||
|
OutputDirectory="../build/$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="../build/$(ConfigurationName)"
|
||||||
|
ConfigurationType="1"
|
||||||
|
CharacterSet="1">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="0"
|
||||||
|
AdditionalIncludeDirectories=".; "$(WX_PATH)/include"; "$(WX_PATH)\lib\vc_lib\mswud""
|
||||||
|
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;__WINDOWS__;__WXMSW__;DEBUG=1;__WXDEBUG__;__WIN95__;__WIN32__;WINVER=0x0400;STRICT;UNICODE;_UNICODE;wxUSE_UNICODE"
|
||||||
|
MinimalRebuild="TRUE"
|
||||||
|
BasicRuntimeChecks="3"
|
||||||
|
RuntimeLibrary="5"
|
||||||
|
BufferSecurityCheck="TRUE"
|
||||||
|
RuntimeTypeInfo="TRUE"
|
||||||
|
UsePrecompiledHeader="2"
|
||||||
|
PrecompiledHeaderThrough="util/prec.hpp"
|
||||||
|
PrecompiledHeaderFile=""
|
||||||
|
AssemblerListingLocation="$(OutDir)"
|
||||||
|
ProgramDataBaseFileName="$(IntDir)/vc70.pdb"
|
||||||
|
BrowseInformation="1"
|
||||||
|
WarningLevel="4"
|
||||||
|
WarnAsError="TRUE"
|
||||||
|
Detect64BitPortabilityProblems="TRUE"
|
||||||
|
DebugInformationFormat="4"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalDependencies="rpcrt4.lib wsock32.lib comctl32.lib wxbase26ud.lib wxmsw26ud_core.lib wxjpegd.lib wxpngd.lib wxtiffd.lib wxzlibd.lib"
|
||||||
|
OutputFile="$(OutDir)/mse.exe"
|
||||||
|
LinkIncremental="2"
|
||||||
|
IgnoreDefaultLibraryNames="libcd.lib,libcid.lib"
|
||||||
|
GenerateDebugInformation="TRUE"
|
||||||
|
ProgramDatabaseFile="$(OutDir)/mse.pdb"
|
||||||
|
SubSystem="2"
|
||||||
|
TargetMachine="1"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
PreprocessorDefinitions="_DEBUG"
|
||||||
|
MkTypLibCompatible="TRUE"
|
||||||
|
SuppressStartupBanner="TRUE"
|
||||||
|
TargetEnvironment="1"
|
||||||
|
TypeLibraryName="$(OutDir)/mse.tlb"
|
||||||
|
HeaderFileName=""/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebDeploymentTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedWrapperGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||||
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Release Unicode|Win32"
|
||||||
|
OutputDirectory="../build/$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="../build/$(ConfigurationName)"
|
||||||
|
ConfigurationType="1"
|
||||||
|
CharacterSet="1"
|
||||||
|
WholeProgramOptimization="TRUE">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="3"
|
||||||
|
GlobalOptimizations="TRUE"
|
||||||
|
InlineFunctionExpansion="1"
|
||||||
|
FavorSizeOrSpeed="2"
|
||||||
|
OmitFramePointers="TRUE"
|
||||||
|
OptimizeForProcessor="3"
|
||||||
|
OptimizeForWindowsApplication="TRUE"
|
||||||
|
AdditionalIncludeDirectories=".; "$(WX_PATH)/include"; "$(WX_PATH)\lib\vc_lib\mswu""
|
||||||
|
PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;__WINDOWS__;__WXMSW__;__WIN95__;__WIN32__;WINVER=0x0400;STRICT;UNICODE;_UNICODE;wxUSE_UNICODE"
|
||||||
|
StringPooling="TRUE"
|
||||||
|
RuntimeLibrary="4"
|
||||||
|
BufferSecurityCheck="FALSE"
|
||||||
|
EnableFunctionLevelLinking="TRUE"
|
||||||
|
EnableEnhancedInstructionSet="1"
|
||||||
|
RuntimeTypeInfo="TRUE"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
PrecompiledHeaderThrough="util/prec.hpp"
|
||||||
|
PrecompiledHeaderFile=""
|
||||||
|
AssemblerListingLocation="$(OutDir)"
|
||||||
|
ProgramDataBaseFileName="$(IntDir)/vc70.pdb"
|
||||||
|
WarningLevel="3"
|
||||||
|
WarnAsError="FALSE"
|
||||||
|
Detect64BitPortabilityProblems="TRUE"
|
||||||
|
DebugInformationFormat="3"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalDependencies="rpcrt4.lib wsock32.lib comctl32.lib wxbase26u.lib wxmsw26u_core.lib wxjpeg.lib wxpng.lib wxtiff.lib wxzlib.lib"
|
||||||
|
OutputFile="$(OutDir)/mse.exe"
|
||||||
|
LinkIncremental="1"
|
||||||
|
SuppressStartupBanner="TRUE"
|
||||||
|
IgnoreDefaultLibraryNames="libc.lib,libci.lib"
|
||||||
|
GenerateDebugInformation="TRUE"
|
||||||
|
ProgramDatabaseFile="$(OutDir)/mse.pdb"
|
||||||
|
SubSystem="2"
|
||||||
|
OptimizeReferences="2"
|
||||||
|
EnableCOMDATFolding="2"
|
||||||
|
TargetMachine="1"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
PreprocessorDefinitions="NDEBUG"
|
||||||
|
MkTypLibCompatible="TRUE"
|
||||||
|
SuppressStartupBanner="TRUE"
|
||||||
|
TargetEnvironment="1"
|
||||||
|
TypeLibraryName="$(OutDir)/mse.tlb"
|
||||||
|
HeaderFileName=""/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebDeploymentTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedWrapperGeneratorTool"/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||||
|
</Configuration>
|
||||||
|
</Configurations>
|
||||||
|
<References>
|
||||||
|
</References>
|
||||||
|
<Files>
|
||||||
|
<Filter
|
||||||
|
Name="gui"
|
||||||
|
Filter="">
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\icon_menu.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\icon_menu.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\util.cpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\util.hpp">
|
||||||
|
</File>
|
||||||
|
<Filter
|
||||||
|
Name="main"
|
||||||
|
Filter="">
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\main\panel.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\main\window.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\main\window.hpp">
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="control"
|
||||||
|
Filter="">
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="editor"
|
||||||
|
Filter="">
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="symbol"
|
||||||
|
Filter="">
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\basic_shape_editor.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\basic_shape_editor.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\control.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\control.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\editor.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\editor.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\part_list.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\part_list.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\point_editor.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\point_editor.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\select_editor.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\select_editor.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\viewer.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\viewer.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\window.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Debug|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)2.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Debug Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)2.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gui\symbol\window.hpp">
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="resource"
|
||||||
|
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
|
||||||
|
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
|
||||||
|
<File
|
||||||
|
RelativePath=".\resource\mse.rc">
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="symbol"
|
||||||
|
Filter="">
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="data"
|
||||||
|
Filter="">
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\card.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\card_style.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\field.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\game.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\locale.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\set.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\symbol.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\symbol.hpp">
|
||||||
|
</File>
|
||||||
|
<Filter
|
||||||
|
Name="action"
|
||||||
|
Filter="">
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\action\symbol.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Debug|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/symbol_action.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/symbol_action.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Debug Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/symbol_action.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/symbol_action.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\action\symbol.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\action\symbol_part.cpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\data\action\symbol_part.hpp">
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="util"
|
||||||
|
Filter="">
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\action_stack.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\action_stack.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\error.cpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\error.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\for_each.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\index_map.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\prec.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\real_point.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\reflect.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\rotation.cpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\rotation.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\smart_ptr.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\string.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\string.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\vector2d.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\window_id.hpp">
|
||||||
|
</File>
|
||||||
|
<Filter
|
||||||
|
Name="io"
|
||||||
|
Filter="">
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\io\package.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\io\package.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\io\reader.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\io\reader.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\io\writer.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\util\io\writer.hpp">
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="gfx"
|
||||||
|
Filter="">
|
||||||
|
<File
|
||||||
|
RelativePath=".\gfx\bezier.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gfx\bezier.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gfx\combine_image.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gfx\gfx.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gfx\polynomial.cpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gfx\polynomial.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gfx\resample_text.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gfx\rotate_image.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="script"
|
||||||
|
Filter="">
|
||||||
|
</Filter>
|
||||||
|
<File
|
||||||
|
RelativePath=".\code_template.cpp">
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
<FileConfiguration
|
||||||
|
Name="Release Unicode|Win32">
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||||
|
</FileConfiguration>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\code_template.hpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\main.cpp">
|
||||||
|
</File>
|
||||||
|
</Files>
|
||||||
|
<Globals>
|
||||||
|
</Globals>
|
||||||
|
</VisualStudioProject>
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 <algorithm>
|
||||||
|
#include "action_stack.hpp"
|
||||||
|
#include "for_each.hpp"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Action stack
|
||||||
|
|
||||||
|
DECLARE_TYPEOF_COLLECTION(Action*);
|
||||||
|
DECLARE_TYPEOF_COLLECTION(ActionListener*);
|
||||||
|
|
||||||
|
ActionStack::ActionStack()
|
||||||
|
: savePoint(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ActionStack::~ActionStack() {
|
||||||
|
// we own the actions, delete them
|
||||||
|
FOR_EACH(a, undoActions) delete a;
|
||||||
|
FOR_EACH(a, redoActions) delete a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionStack::add(Action* action, bool allowMerge) {
|
||||||
|
if (!action) return; // no action
|
||||||
|
action->perform(false); // TODO: delete action if perform throws
|
||||||
|
redoActions.clear();
|
||||||
|
tellListeners(*action);
|
||||||
|
// try to merge?
|
||||||
|
if (allowMerge && !undoActions.empty() && undoActions.back()->merge(action)) {
|
||||||
|
// merged with top undo action
|
||||||
|
delete action;
|
||||||
|
} else {
|
||||||
|
undoActions.push_back(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionStack::undo() {
|
||||||
|
assert(canUndo());
|
||||||
|
Action* action = undoActions.back();
|
||||||
|
action->perform(true);
|
||||||
|
// move to redo stack
|
||||||
|
undoActions.pop_back();
|
||||||
|
redoActions.push_back(action);
|
||||||
|
}
|
||||||
|
void ActionStack::redo() {
|
||||||
|
assert(canRedo());
|
||||||
|
Action* action = redoActions.back();
|
||||||
|
action->perform(false);
|
||||||
|
// move to undo stack
|
||||||
|
redoActions.pop_back();
|
||||||
|
undoActions.push_back(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ActionStack::canUndo() const {
|
||||||
|
return !undoActions.empty();
|
||||||
|
}
|
||||||
|
bool ActionStack::canRedo() const {
|
||||||
|
return !redoActions.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
String ActionStack::undoName() const {
|
||||||
|
if (canUndo()) {
|
||||||
|
return _("Undo ") + capitalize(undoActions.back()->getName(true));
|
||||||
|
} else {
|
||||||
|
return _("Undo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String ActionStack::redoName() const {
|
||||||
|
if (canRedo()) {
|
||||||
|
return _("Redo ") + capitalize(redoActions.back()->getName(false));
|
||||||
|
} else {
|
||||||
|
return _("Redo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ActionStack::atSavePoint() const {
|
||||||
|
return (undoActions.empty() && savePoint == nullptr)
|
||||||
|
|| (undoActions.back() == savePoint);
|
||||||
|
}
|
||||||
|
void ActionStack::setSavePoint() {
|
||||||
|
if (undoActions.empty()) {
|
||||||
|
savePoint = nullptr;
|
||||||
|
} else {
|
||||||
|
savePoint = undoActions.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionStack::addListener(ActionListener* listener) {
|
||||||
|
listeners.push_back(listener);
|
||||||
|
}
|
||||||
|
void ActionStack::removeListener(ActionListener* listener) {
|
||||||
|
listeners.erase(
|
||||||
|
std::remove(
|
||||||
|
listeners.begin(),
|
||||||
|
listeners.end(),
|
||||||
|
listener
|
||||||
|
),
|
||||||
|
listeners.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
void ActionStack::tellListeners(const Action& action) {
|
||||||
|
FOR_EACH(l, listeners) l->onAction(action);
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_ACTION_STACK
|
||||||
|
#define HEADER_UTIL_ACTION_STACK
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "string.hpp"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Action
|
||||||
|
|
||||||
|
/// Base class for actions that can be stored in an ActionStack.
|
||||||
|
/** An action is something that can be done to modify an object.
|
||||||
|
* It must store the necessary information to also undo the action.
|
||||||
|
*/
|
||||||
|
class Action {
|
||||||
|
public:
|
||||||
|
virtual ~Action() {};
|
||||||
|
|
||||||
|
/// Name of the action, for use in strings like "Undo <name>"
|
||||||
|
virtual String getName(bool toUndo) const = 0;
|
||||||
|
|
||||||
|
/// Perform the action
|
||||||
|
/// Must be implemented in derived class
|
||||||
|
/** Perform will only ever be called alternatingly with toUndo = true/false,
|
||||||
|
* the first time with toUndo = false
|
||||||
|
*/
|
||||||
|
/// @param toUndo if true, undo the action instead of doing it
|
||||||
|
virtual void perform(bool toUndo) = 0;
|
||||||
|
|
||||||
|
/// Try to merge another action to the end of this action.
|
||||||
|
/// Either: return false and do nothing
|
||||||
|
/// Or: return true and change this action to incorporate both actions
|
||||||
|
virtual bool merge(const Action* action) { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Action listeners
|
||||||
|
|
||||||
|
/// Base class/interface for objects that listen to actions
|
||||||
|
class ActionListener {
|
||||||
|
public:
|
||||||
|
virtual void onAction(const Action& a) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Action stack
|
||||||
|
|
||||||
|
/// A stack of actions that can be done and undone.
|
||||||
|
/** This class handles the undo and redo functionality of a particular object.
|
||||||
|
*
|
||||||
|
* This class also takes on the role of Observable, ActionListeners can register themselfs.
|
||||||
|
* They will be notified when an action is added.
|
||||||
|
*/
|
||||||
|
class ActionStack {
|
||||||
|
public:
|
||||||
|
ActionStack();
|
||||||
|
~ActionStack();
|
||||||
|
|
||||||
|
/// Add an action to the stack, and perform that action.
|
||||||
|
/// Tells all listeners about the action.
|
||||||
|
/// The ActionStack takes ownership of the action
|
||||||
|
void add(Action* action, bool allowMerge = true);
|
||||||
|
|
||||||
|
/// Undoes the last action that was (re)done
|
||||||
|
/// @pre canUndo()
|
||||||
|
void undo();
|
||||||
|
/// Redoes the last action that was undone
|
||||||
|
/// @pre canRedo()
|
||||||
|
void redo();
|
||||||
|
|
||||||
|
/// Is undoing possible?
|
||||||
|
bool canUndo() const;
|
||||||
|
/// Is redoing possible?
|
||||||
|
bool canRedo() const;
|
||||||
|
|
||||||
|
/// Name of the action that will be undone next, in the form "Undo <Action>"
|
||||||
|
/// If there is no action to undo returns "Undo"
|
||||||
|
String undoName() const;
|
||||||
|
/// Name of the action that will be redone next "Redo <Action>"
|
||||||
|
/// If there is no action to undo returns "Redo"
|
||||||
|
String redoName() const;
|
||||||
|
|
||||||
|
/// Is the file currently at a 'savepoint'?
|
||||||
|
/// This is the last point at which the file was saved
|
||||||
|
bool atSavePoint() const;
|
||||||
|
/// Indicate that the file is at a savepoint.
|
||||||
|
void setSavePoint();
|
||||||
|
|
||||||
|
/// Add an action listener
|
||||||
|
void addListener(ActionListener* listener);
|
||||||
|
/// Remove an action listener
|
||||||
|
void removeListener(ActionListener* listener);
|
||||||
|
/// Tell all listeners about an action
|
||||||
|
void tellListeners(const Action&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Actions to be undone
|
||||||
|
/// Owns the action objects!
|
||||||
|
vector<Action*> undoActions;
|
||||||
|
/// Actions to be redone
|
||||||
|
/// Owns the action objects!
|
||||||
|
vector<Action*> redoActions;
|
||||||
|
/// Point at which the file was saved, corresponds to the top of the undo stack at that point
|
||||||
|
Action* savePoint;
|
||||||
|
/// Objects that are listening to actions
|
||||||
|
vector<ActionListener*> listeners;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Utilities
|
||||||
|
|
||||||
|
/// Tests if variable has the type Type
|
||||||
|
/** Uses dynamic cast, so Type must have a virtual function.
|
||||||
|
*/
|
||||||
|
#define TYPE_CASE_(variable, Type) \
|
||||||
|
if (dynamic_cast<const Type*>(&variable))
|
||||||
|
|
||||||
|
/// Tests if variable has the type Type. If this is the case, makes
|
||||||
|
/// variable have type Type inside the statement
|
||||||
|
/** Uses dynamic cast, so Type must have a virtual function.
|
||||||
|
*/
|
||||||
|
#define TYPE_CASE(variable, Type) \
|
||||||
|
pair<const Type*,bool> Type##variable \
|
||||||
|
(dynamic_cast<const Type*>(&variable), true); \
|
||||||
|
if (Type##variable.first) \
|
||||||
|
for (const Type& variable = *Type##variable.first ; \
|
||||||
|
Type##variable.second ; \
|
||||||
|
Type##variable.second = false)
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 <util/error.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Error types
|
||||||
|
|
||||||
|
Error::Error(const String& message)
|
||||||
|
: message(message)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Error::~Error() {}
|
||||||
|
|
||||||
|
String Error::what() const {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_ERROR
|
||||||
|
#define HEADER_UTIL_ERROR
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/prec.hpp>
|
||||||
|
|
||||||
|
/** @file util/error.hpp
|
||||||
|
*
|
||||||
|
* Classes and functions for handling errors/exceptions
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Error types
|
||||||
|
|
||||||
|
/// Our own exception class
|
||||||
|
class Error {
|
||||||
|
public:
|
||||||
|
Error(const String& message);
|
||||||
|
virtual ~Error();
|
||||||
|
|
||||||
|
/// Return the error message
|
||||||
|
virtual String what() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
String message; //^ The error message
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Internal errors
|
||||||
|
class InternalError : public Error {
|
||||||
|
public:
|
||||||
|
inline InternalError(const String& str)
|
||||||
|
: Error(_("An internal error occured, please contact the author:\n") + str)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : File errors
|
||||||
|
|
||||||
|
// Errors related to packages
|
||||||
|
class PackageError : public Error {
|
||||||
|
public:
|
||||||
|
inline PackageError(const String& str) : Error(str) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Parse errors
|
||||||
|
|
||||||
|
// Parse errors
|
||||||
|
class ParseError : public Error {
|
||||||
|
public:
|
||||||
|
inline ParseError(const String& str) : Error(str) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -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_UTIL_FOR_EACH
|
||||||
|
#define HEADER_UTIL_FOR_EACH
|
||||||
|
|
||||||
|
/** @file util/for_each.hpp
|
||||||
|
*
|
||||||
|
* Macros to simplify looping over collections.
|
||||||
|
* This header contains some evil template and macro hackery.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Typeof magic
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
// GCC has a buildin typeof function, so it doesn't need (as much) hacks
|
||||||
|
#define DECLARE_TYPEOF(T)
|
||||||
|
#define DECLARE_TYPEOF_COLLECTION(T)
|
||||||
|
|
||||||
|
#define TYPEOF(Value) __typeof(Value)
|
||||||
|
#define TYPEOF_IT(Value) __typeof(Value.begin())
|
||||||
|
#define TYPEOF_CIT(Value) __typeof(Value.begin())
|
||||||
|
#define TYPEOF_RIT(Value) __typeof(Value.rbegin())
|
||||||
|
#define TYPEOF_REF(Value) __typeof(*Value.begin())&
|
||||||
|
|
||||||
|
#else
|
||||||
|
/// Helper for typeof tricks
|
||||||
|
template<const type_info &ref_type_info> struct TypeOf {};
|
||||||
|
|
||||||
|
/// The type of a value
|
||||||
|
#define TYPEOF(Value) TypeOf<typeid(Value)>::type
|
||||||
|
/// The type of an iterator
|
||||||
|
#define TYPEOF_IT(Value) TypeOf<typeid(Value)>::iterator
|
||||||
|
/// The type of a const iterator
|
||||||
|
#define TYPEOF_CIT(Value) TypeOf<typeid(Value)>::const_iterator
|
||||||
|
/// The type of a reverse iterator
|
||||||
|
#define TYPEOF_RIT(Value) TypeOf<typeid(Value)>::reverse_iterator
|
||||||
|
/// The type of a value
|
||||||
|
#define TYPEOF_REF(Value) TypeOf<typeid(Value)>::reference
|
||||||
|
|
||||||
|
/// Declare typeof magic for a specific type
|
||||||
|
#define DECLARE_TYPEOF(T) \
|
||||||
|
template<> struct TypeOf<typeid(T)> { \
|
||||||
|
typedef T type; \
|
||||||
|
typedef T::iterator iterator; \
|
||||||
|
typedef T::const_iterator const_iterator; \
|
||||||
|
typedef T::reverse_iterator reverse_iterator; \
|
||||||
|
typedef T::reference reference; \
|
||||||
|
}
|
||||||
|
/// Declare typeof magic for a specific type that doesn't support reverse iterators
|
||||||
|
#define DECLARE_TYPEOF_NO_REV(T) \
|
||||||
|
template<> struct TypeOf<typeid(T)> { \
|
||||||
|
typedef T type; \
|
||||||
|
typedef T::iterator iterator; \
|
||||||
|
typedef T::const_iterator const_iterator; \
|
||||||
|
typedef T::reference reference; \
|
||||||
|
}
|
||||||
|
/// Declare typeof magic for a specific type, using const iterators
|
||||||
|
#define DECLARE_TYPEOF_CONST(T) \
|
||||||
|
template<> struct TypeOf<typeid(T)> { \
|
||||||
|
typedef T type; \
|
||||||
|
typedef T::const_iterator iterator; \
|
||||||
|
typedef T::const_iterator const_iterator; \
|
||||||
|
typedef T::const_reverse_iterator reverse_iterator; \
|
||||||
|
typedef T::const_reference reference; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Declare typeof magic for a specific std::vector type
|
||||||
|
#define DECLARE_TYPEOF_COLLECTION(T) DECLARE_TYPEOF(vector<T>); \
|
||||||
|
DECLARE_TYPEOF_CONST(set<T>)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Looping macros with iterators
|
||||||
|
|
||||||
|
/// Iterate over a collection, using an iterator it of type Type
|
||||||
|
/// Usage: FOR_EACH_IT_T(Type,it,collect) { body-of-loop }
|
||||||
|
#define FOR_EACH_IT_T(Type,Iterator,Collection) \
|
||||||
|
for(Type Iterator = Collection.begin() ; \
|
||||||
|
Iterator != Collection.end() ; \
|
||||||
|
++Iterator)
|
||||||
|
|
||||||
|
/// Iterate over a collection whos type must be declared with DECLARE_TYPEOF
|
||||||
|
/// Usage: FOR_EACH_IT(it,collect) { body-of-loop }
|
||||||
|
#define FOR_EACH_IT(Iterator,Collection) \
|
||||||
|
FOR_EACH_IT_T(TYPEOF_IT(Collection), Iterator, Collection)
|
||||||
|
|
||||||
|
/// Iterate over a collection whos type must be declared with DECLARE_TYPEOF
|
||||||
|
/// Uses a const_iterator
|
||||||
|
/// Usage: FOR_EACH_IT(it,collect) { body-of-loop }
|
||||||
|
#define FOR_EACH_CONST_IT(Iterator,Collection) \
|
||||||
|
FOR_EACH_IT_T(TYPEOF_CIT(Collection), Iterator, Collection)
|
||||||
|
|
||||||
|
/// Iterate over a collection in whos type must be declared with DECLARE_TYPEOF
|
||||||
|
/// Iterates using a reverse_iterator
|
||||||
|
/// Usage: FOR_EACH_REVERSE_IT(it,collect) { body-of-loop }
|
||||||
|
#define FOR_EACH_REVERSE_IT(Iterator,Collection) \
|
||||||
|
for(TYPEOF_RIT(Collection) \
|
||||||
|
Iterator = Collection.rbegin() ; \
|
||||||
|
Iterator != Collection.rend() ; \
|
||||||
|
++Iterator)
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Looping macros
|
||||||
|
|
||||||
|
/// Iterate over a collection, with an iterator of type TypeIt, and elements of type TypeElem
|
||||||
|
/// Usage: FOR_EACH_T(TypeIt,TypeElem,e,collect) { body-of-loop }
|
||||||
|
/** We need a hack to be able to declare a local variable without needing braces.
|
||||||
|
* To do this we use a nested for loop that is only executed once, and which is optimized away.
|
||||||
|
* To terminate this loop we need an extra bool, which we set to false after the first iteration.
|
||||||
|
*/
|
||||||
|
#define FOR_EACH_T(TypeIt,TypeElem,Elem,Collection) \
|
||||||
|
for(std::pair<TypeIt,bool> Elem##_IT(Collection.begin(), true) ; \
|
||||||
|
Elem##_IT.first != Collection.end() ; \
|
||||||
|
++Elem##_IT.first, Elem##_IT.second = true) \
|
||||||
|
for(TypeElem Elem = *Elem##_IT.first ; \
|
||||||
|
Elem##_IT.second ; \
|
||||||
|
Elem##_IT.second = false)
|
||||||
|
|
||||||
|
/// Iterate over a collection whos type must be declared with DECLARE_TYPEOF
|
||||||
|
/// Usage: FOR_EACH(e,collect) { body-of-loop }
|
||||||
|
#define FOR_EACH(Elem,Collection) \
|
||||||
|
FOR_EACH_T(TYPEOF_IT(Collection), TYPEOF_REF(Collection), Elem, Collection)
|
||||||
|
|
||||||
|
/// Iterate over a collection whos type must be declared with DECLARE_TYPEOF
|
||||||
|
/// Iterates using a reverse_iterator
|
||||||
|
/// Usage: FOR_EACH_REVERSE(e,collect) { body-of-loop }
|
||||||
|
#define FOR_EACH_REVERSE(Elem,Collection) \
|
||||||
|
for(std::pair<TYPEOF_RIT(Collection),bool> Elem##_IT(Collection.rbegin(), true) ; \
|
||||||
|
Elem##_IT.first != Collection.rend() ; \
|
||||||
|
++Elem##_IT.first, Elem##_IT.second = true) \
|
||||||
|
for(TYPEOF_REF(Collection) Elem = *Elem##_IT.first ; \
|
||||||
|
Elem##_IT.second ; \
|
||||||
|
Elem##_IT.second = false)
|
||||||
|
|
||||||
|
/// Iterate over two collection in parallel
|
||||||
|
/// Usage: FOR_EACH_2_T(TypeIt1,TypeElem1,e1,collect1,TypeIt2,TypeElem2,e2,collect2) { body-of-loop }
|
||||||
|
/** Note: This has got to be one of the craziest pieces of code I have ever written :)
|
||||||
|
* It is just an extension of the idea of FOR_EACH_T.
|
||||||
|
*/
|
||||||
|
#define FOR_EACH_2_T(TypeIt1,TypeElem1,Elem1,Coll1,TypeIt2,TypeElem2,Elem2,Coll2) \
|
||||||
|
for(std::pair<std::pair<TypeIt1,TypeIt2>, bool> \
|
||||||
|
Elem1##_IT(make_pair(Coll1.begin(), Coll2.begin()), true) ; \
|
||||||
|
Elem1##_IT.first.first != Coll1.end() && \
|
||||||
|
Elem1##_IT.first.second != Coll2.end() ; \
|
||||||
|
++Elem1##_IT.first.first, ++Elem1##_IT.first.second, \
|
||||||
|
Elem1##_IT.second = true) \
|
||||||
|
for(TypeElem1 Elem1 = *Elem1##_IT.first.first ; \
|
||||||
|
Elem1##_IT.second ; \
|
||||||
|
Elem1##_IT.second = false) \
|
||||||
|
for(TypeElem2 Elem2 = *Elem1##_IT.first.second ; \
|
||||||
|
Elem1##_IT.second ; \
|
||||||
|
Elem1##_IT.second = false)
|
||||||
|
|
||||||
|
/// Iterate over two collections in parallel,
|
||||||
|
/// their type must be declared with DECLARE_TYPEOF.
|
||||||
|
/// Usage: FOR_EACH_2(e1,collect1, e2,collect2) { body-of-loop }
|
||||||
|
#define FOR_EACH_2(Elem1,Collection1, Elem2,Collection2) \
|
||||||
|
FOR_EACH_2_T(TYPEOF_IT(Collection1), TYPEOF_REF(Collection1), Elem1, Collection1, \
|
||||||
|
TYPEOF_IT(Collection2), TYPEOF_REF(Collection2), Elem2, Collection2)
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -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_UTIL_INDEX_MAP
|
||||||
|
#define HEADER_UTIL_INDEX_MAP
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : IndexMap
|
||||||
|
|
||||||
|
/// A kind of map of K->V, with the following properties:
|
||||||
|
/** - K must have a unique member ->index of type UInt
|
||||||
|
* - There must exist a function initObject(K, V&)
|
||||||
|
* that stores a new V object for a given key in v
|
||||||
|
* - O(1) inserts and lookups
|
||||||
|
*/
|
||||||
|
template <typename Key, typename Value>
|
||||||
|
class IndexMap : private vector<Value> {
|
||||||
|
public:
|
||||||
|
using vector<Value>::empty;
|
||||||
|
using vector<Value>::size;
|
||||||
|
using vector<Value>::iterator;
|
||||||
|
using vector<Value>::begin;
|
||||||
|
using vector<Value>::end;
|
||||||
|
|
||||||
|
/// Initialize this map with default values given a list of keys, has no effect if !empty()
|
||||||
|
/** Requires a function
|
||||||
|
* void initObject(Key, Value&)
|
||||||
|
*/
|
||||||
|
void init(const vector<Key>& keys) {
|
||||||
|
if (!this->empty()) return;
|
||||||
|
this->reserve(keys.size());
|
||||||
|
FOR_EACH(it, keys) {
|
||||||
|
Key& k = *it;
|
||||||
|
if (k->index >= this->size()) this->resize(k->index + 1);
|
||||||
|
initObject(k, (*this)[k->index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a value given its key
|
||||||
|
inline Value operator [] (const Key& key) {
|
||||||
|
assert(key);
|
||||||
|
assert(this->size() > key->index);
|
||||||
|
return at(key->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is a value contained in this index map?
|
||||||
|
/// requires a function Key Value::getKey()
|
||||||
|
inline bool contains(const Value& value) const {
|
||||||
|
assert(value);
|
||||||
|
size_t index = value->getKey()->index;
|
||||||
|
return index < this.size() && (*this)[index] == value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is a key in the domain of this index map?
|
||||||
|
/// requires a function Key Value::getKey()
|
||||||
|
inline bool containsKey(const Key& key) const {
|
||||||
|
assert(key);
|
||||||
|
return key->index < this.size() && (*this)[key->index]->getKey() == key
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using vector<Value>::operator [];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 "reader.hpp"
|
||||||
|
#include <util/vector2d.hpp>
|
||||||
|
#include <util/error.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Reader
|
||||||
|
|
||||||
|
Reader::Reader(const InputStreamP& input, String filename)
|
||||||
|
: input(input), filename(filename), lineNumber(0)
|
||||||
|
, indent(0), expectedIndent(0), justOpened(false)
|
||||||
|
, stream(*input)
|
||||||
|
{
|
||||||
|
moveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Reader::enterBlock(const Char* name) {
|
||||||
|
if (justOpened) moveNext(); // on the key of the parent block, first move inside it
|
||||||
|
if (indent != expectedIndent) return false; // not enough indentation
|
||||||
|
if (name == key) {
|
||||||
|
justOpened = true;
|
||||||
|
expectedIndent += 1; // the indent inside the block must be at least this much
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reader::exitBlock() {
|
||||||
|
assert(expectedIndent > 0);
|
||||||
|
expectedIndent -= 1;
|
||||||
|
multiLineStr.clear();
|
||||||
|
if (justOpened) moveNext(); // leave this key
|
||||||
|
// Dump the remainder of the block
|
||||||
|
// TODO: issue warnings?
|
||||||
|
while (indent > expectedIndent) {
|
||||||
|
moveNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reader::moveNext() {
|
||||||
|
justOpened = false;
|
||||||
|
key.clear();
|
||||||
|
multiLineStr.clear();
|
||||||
|
indent = -1; // if no line is read it never has the expected indentation
|
||||||
|
// repeat until we have a good line
|
||||||
|
while (key.empty() && !input->Eof()) {
|
||||||
|
readLine();
|
||||||
|
}
|
||||||
|
// did we reach the end of the file?
|
||||||
|
if (key.empty() && input->Eof()) {
|
||||||
|
lineNumber += 1;
|
||||||
|
indent = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reader::readLine() {
|
||||||
|
// fix UTF8 in ascii builds; skip BOM
|
||||||
|
line = decodeUTF8BOM(stream.ReadLine());
|
||||||
|
// read indentation
|
||||||
|
indent = 0;
|
||||||
|
while ((UInt)indent < line.size() && line.GetChar(indent) == _('\t')) {
|
||||||
|
indent += 1;
|
||||||
|
}
|
||||||
|
// read key / value
|
||||||
|
size_t pos = line.find_first_of(_(':'), indent);
|
||||||
|
key = trim(line.substr(indent, pos - indent));
|
||||||
|
value = pos == String::npos ? _("") : trimLeft(line.substr(pos+1));
|
||||||
|
// we read a line
|
||||||
|
lineNumber += 1;
|
||||||
|
// was it a comment?
|
||||||
|
if (!key.empty() && key.GetChar(0) == _('#')) {
|
||||||
|
key.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Handling basic types
|
||||||
|
|
||||||
|
template <> void Reader::handle(String& s) {
|
||||||
|
if (!multiLineStr.empty()) {
|
||||||
|
s = multiLineStr;
|
||||||
|
} else if (value.empty()) {
|
||||||
|
// a multiline string
|
||||||
|
bool first = true;
|
||||||
|
// read all lines that are indented enough
|
||||||
|
readLine();
|
||||||
|
while (indent >= expectedIndent) {
|
||||||
|
if (!first) value += '\n';
|
||||||
|
first = false;
|
||||||
|
multiLineStr += line.substr(expectedIndent); // strip expected indent
|
||||||
|
readLine();
|
||||||
|
}
|
||||||
|
// moveNext(), but without emptying multiLineStr
|
||||||
|
justOpened = false;
|
||||||
|
while (key.empty() && !input->Eof()) {
|
||||||
|
readLine();
|
||||||
|
}
|
||||||
|
s = multiLineStr;
|
||||||
|
} else {
|
||||||
|
s = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <> void Reader::handle(int& i) {
|
||||||
|
long l = 0;
|
||||||
|
value.ToLong(&l);
|
||||||
|
i = l;
|
||||||
|
}
|
||||||
|
template <> void Reader::handle(double& d) {
|
||||||
|
value.ToDouble(&d);
|
||||||
|
}
|
||||||
|
template <> void Reader::handle(bool& b) {
|
||||||
|
b = (value==_("true") || value==_("1") || value==_("yes"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Handling less basic util types
|
||||||
|
|
||||||
|
template <> void Reader::handle(Vector2D& vec) {
|
||||||
|
if (!wxSscanf(value.c_str(), _("(%lf,%lf)"), &vec.x, &vec.y)) {
|
||||||
|
throw ParseError(_("Expected (x,y)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_IO_READER
|
||||||
|
#define HEADER_UTIL_IO_READER
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include "../prec.hpp"
|
||||||
|
#include <wx/txtstrm.h>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Reader
|
||||||
|
|
||||||
|
typedef wxInputStream InputStream;
|
||||||
|
typedef shared_ptr<wxInputStream> InputStreamP;
|
||||||
|
|
||||||
|
/// The Reader can be used for reading (deserializing) objects
|
||||||
|
/** This class makes use of the reflection functionality, in effect
|
||||||
|
* an object tells the Reader what fields it would like to read.
|
||||||
|
* The reader then sees if the requested field is currently available.
|
||||||
|
*
|
||||||
|
* The handle functions ensure that afterwards the reader is at the line after the
|
||||||
|
* object that was just read.
|
||||||
|
*/
|
||||||
|
class Reader {
|
||||||
|
public:
|
||||||
|
/// Construct a reader that reads from the given input stream
|
||||||
|
/** filename is used only for error messages
|
||||||
|
*/
|
||||||
|
Reader(const InputStreamP& input, String filename = _(""));
|
||||||
|
|
||||||
|
/// Construct a reader that reads a file in a package
|
||||||
|
Reader(String filename);
|
||||||
|
|
||||||
|
/// Tell the reflection code we are reading
|
||||||
|
inline bool reading() const { return true; }
|
||||||
|
|
||||||
|
// --------------------------------------------------- : Handling objects
|
||||||
|
/// Handle an object: read it if it's name matches
|
||||||
|
template <typename T>
|
||||||
|
void handle(const Char* name, T& object) {
|
||||||
|
if (enterBlock(name)) {
|
||||||
|
handle(object);
|
||||||
|
exitBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads an object of type T from the input stream
|
||||||
|
template <typename T> void handle(T& object);
|
||||||
|
/// Reads a vector from the input stream
|
||||||
|
template <typename T> void handle(vector<T>& vector);
|
||||||
|
/// Reads a shared_ptr from the input stream
|
||||||
|
template <typename T> void handle(shared_ptr<T>& pointer);
|
||||||
|
/// Reads a map from the input stream
|
||||||
|
//template <typename K, typename V> void handle(map<K,V>& map);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// --------------------------------------------------- : Data
|
||||||
|
/// The line we read
|
||||||
|
String line;
|
||||||
|
/// The key and value of the last line we read
|
||||||
|
String key, value;
|
||||||
|
/// A string spanning multiple lines
|
||||||
|
String multiLineStr;
|
||||||
|
/// Indentation of the last line we read
|
||||||
|
int indent;
|
||||||
|
/// Indentation of the block we are in
|
||||||
|
int expectedIndent;
|
||||||
|
/// Did we just open a block (i.e. not read any more lines of it)?
|
||||||
|
bool justOpened;
|
||||||
|
|
||||||
|
/// Filename for error messages
|
||||||
|
String filename;
|
||||||
|
/// Line number for error messages
|
||||||
|
UInt lineNumber;
|
||||||
|
/// Input stream we are reading from
|
||||||
|
InputStreamP input;
|
||||||
|
/// Text stream wrapping the input stream
|
||||||
|
wxTextInputStream stream;
|
||||||
|
|
||||||
|
// --------------------------------------------------- : Reading the stream
|
||||||
|
|
||||||
|
/// Is there a block with the given key under the current cursor?
|
||||||
|
bool enterBlock(const Char* name);
|
||||||
|
/// Leave the block we are in
|
||||||
|
void exitBlock();
|
||||||
|
|
||||||
|
/// Move to the next non empty line
|
||||||
|
void moveNext();
|
||||||
|
/// Reads the next line from the input, and stores it in line/key/value/indent
|
||||||
|
void readLine();
|
||||||
|
|
||||||
|
/// Issue a warning: "Unexpected key: $key"
|
||||||
|
void unexpected();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Container types
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Reader::handle(vector<T>& vector) {
|
||||||
|
String vectorKey = key;
|
||||||
|
while (key == vectorKey) { // TODO : check indent
|
||||||
|
moveNext(); // skip key
|
||||||
|
vector.resize(vector.size() + 1);
|
||||||
|
handle(vector.back());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Reader::handle(shared_ptr<T>& pointer) {
|
||||||
|
if (!pointer) pointer.reset(new T);
|
||||||
|
handle(*pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Reflection
|
||||||
|
|
||||||
|
/// Implement reflection as used by Reader
|
||||||
|
#define REFLECT_OBJECT_READER(Cls) \
|
||||||
|
template<> void Reader::handle<Cls>(Cls& object) { \
|
||||||
|
object.reflect(*this); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Reflection for enumerations
|
||||||
|
|
||||||
|
/// Implement enum reflection as used by Reader
|
||||||
|
#define REFLECT_ENUM_READER(Enum) \
|
||||||
|
template<> void Reader::handle<Enum>(Enum& enum_) { \
|
||||||
|
EnumReader reader(value); \
|
||||||
|
reflect_ ## Enum(enum_, reader); \
|
||||||
|
if (!reader.isDone()) { \
|
||||||
|
/* warning message */ \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'Tag' to be used when reflecting enumerations for Reader
|
||||||
|
class EnumReader {
|
||||||
|
public:
|
||||||
|
inline EnumReader(String read)
|
||||||
|
: read(read), first(true), done(false) {}
|
||||||
|
|
||||||
|
/// Handle a possible value for the enum, if the name matches the name in the input
|
||||||
|
template <typename Enum>
|
||||||
|
inline void handle(const Char* name, Enum value, Enum& enum_) {
|
||||||
|
if (!done && read == name) {
|
||||||
|
first = true;
|
||||||
|
enum_ = value;
|
||||||
|
} else if (first) {
|
||||||
|
first = false;
|
||||||
|
enum_ = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isDone() const { return done; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
String read; //^ The string to match to a value name
|
||||||
|
bool first; //^ Has the first (default) value been matched?
|
||||||
|
bool done; //^ Was anything matched?
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 "writer.hpp"
|
||||||
|
#include <util/vector2d.hpp>
|
||||||
|
#include <util/error.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Writer
|
||||||
|
|
||||||
|
Writer::Writer(const OutputStreamP& output)
|
||||||
|
: output(output)
|
||||||
|
, stream(*output)
|
||||||
|
, indentation(0)
|
||||||
|
, justOpened(false)
|
||||||
|
{
|
||||||
|
stream.WriteString(BYTE_ORDER_MARK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Writer::enterBlock(const Char* name) {
|
||||||
|
// indenting into a sub-block?
|
||||||
|
if (justOpened) {
|
||||||
|
writeKey();
|
||||||
|
stream.WriteString(_(":\n"));
|
||||||
|
}
|
||||||
|
// don't write the key yet
|
||||||
|
indentation += 1;
|
||||||
|
openedKey = name;
|
||||||
|
justOpened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Writer::exitBlock() {
|
||||||
|
assert(indentation > 0);
|
||||||
|
indentation -= 1;
|
||||||
|
justOpened = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Writer::writeKey() {
|
||||||
|
writeIndentation();
|
||||||
|
writeUTF8(stream, openedKey);
|
||||||
|
}
|
||||||
|
void Writer::writeIndentation() {
|
||||||
|
for(int i = 1 ; i < indentation ; ++i) {
|
||||||
|
stream.PutChar(_('\t'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Handling basic types
|
||||||
|
|
||||||
|
void Writer::handle(const String& value) {
|
||||||
|
if (!justOpened) {
|
||||||
|
throw InternalError(_("Can only write a value in a key that was just opened"));
|
||||||
|
}
|
||||||
|
// write indentation and key
|
||||||
|
writeKey();
|
||||||
|
writeUTF8(stream, _(": "));
|
||||||
|
if (value.find_first_of(_('\n')) != String::npos) {
|
||||||
|
// multiline string
|
||||||
|
stream.PutChar(_('\n'));
|
||||||
|
indentation += 1;
|
||||||
|
// split lines, and write each line
|
||||||
|
size_t start = 0, end, size = value.size();
|
||||||
|
while (start < size) {
|
||||||
|
end = value.find_first_of(_("\n\r"), start); // until end of line
|
||||||
|
// write the line
|
||||||
|
writeIndentation();
|
||||||
|
writeUTF8(stream, value.substr(start, end - start));
|
||||||
|
stream.PutChar(_('\n'));
|
||||||
|
// Skip \r and \n
|
||||||
|
if (end == String::npos) break;
|
||||||
|
start = end + 1;
|
||||||
|
if (start < size) {
|
||||||
|
Char c1 = value.GetChar(start - 1);
|
||||||
|
Char c2 = value.GetChar(start);
|
||||||
|
// skip second character of \r\n or \n\r
|
||||||
|
if (c1 != c2 && (c2 == _('\r') || c2 == _('\n'))) start += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indentation -= 1;
|
||||||
|
} else {
|
||||||
|
writeUTF8(stream, value);
|
||||||
|
}
|
||||||
|
stream.PutChar(_('\n'));
|
||||||
|
justOpened = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> void Writer::handle(const int& value) {
|
||||||
|
handle(String() << value);
|
||||||
|
}
|
||||||
|
template <> void Writer::handle(const double& value) {
|
||||||
|
handle(String() << value);
|
||||||
|
}
|
||||||
|
template <> void Writer::handle(const bool& value) {
|
||||||
|
handle(value ? _("true") : _("false"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Handling less basic util types
|
||||||
|
|
||||||
|
template <> void Writer::handle(const Vector2D& vec) {
|
||||||
|
String formated;
|
||||||
|
formated.Printf(_("(%.10lf,%.10lf)"), vec.x, vec.y);
|
||||||
|
handle(formated);
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_IO_WRITER
|
||||||
|
#define HEADER_UTIL_IO_WRITER
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/prec.hpp>
|
||||||
|
#include <wx/txtstrm.h>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Writer
|
||||||
|
|
||||||
|
typedef wxOutputStream OutputStream;
|
||||||
|
typedef shared_ptr<wxOutputStream> OutputStreamP;
|
||||||
|
|
||||||
|
/// The Writer can be used for writing (serializing) objects
|
||||||
|
class Writer {
|
||||||
|
public:
|
||||||
|
/// Construct a writer that writes to the given output stream
|
||||||
|
Writer(const OutputStreamP& output);
|
||||||
|
|
||||||
|
/// Tell the reflection code we are not reading
|
||||||
|
inline bool reading() const { return false; }
|
||||||
|
|
||||||
|
// --------------------------------------------------- : Handling objects
|
||||||
|
/// Handle an object: write it under the given name
|
||||||
|
template <typename T>
|
||||||
|
void handle(const Char* name, const T& object) {
|
||||||
|
enterBlock(name);
|
||||||
|
handle(object);
|
||||||
|
exitBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a string to the output stream
|
||||||
|
void handle(const String& str);
|
||||||
|
void handle(const Char* str) { handle(String(str)); }
|
||||||
|
|
||||||
|
/// Write an object of type T to the output stream
|
||||||
|
template <typename T> void handle(const T& object);
|
||||||
|
/// Write a vector to the output stream
|
||||||
|
template <typename T> void handle(const vector<T>& vector);
|
||||||
|
/// Write a shared_ptr to the output stream
|
||||||
|
template <typename T> void handle(const shared_ptr<T>& pointer);
|
||||||
|
/// Write a map to the output stream
|
||||||
|
//template <typename K, typename V> void handle(map<K,V>& map);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// --------------------------------------------------- : Data
|
||||||
|
/// Indentation of the current block
|
||||||
|
int indentation;
|
||||||
|
/// Did we just open a block (i.e. not written any lines of it)?
|
||||||
|
bool justOpened;
|
||||||
|
/// Last key opened
|
||||||
|
String openedKey;
|
||||||
|
|
||||||
|
/// Output stream we are writing to
|
||||||
|
OutputStreamP output;
|
||||||
|
/// Text stream wrapping the output stream
|
||||||
|
wxTextOutputStream stream;
|
||||||
|
|
||||||
|
// --------------------------------------------------- : Writing to the stream
|
||||||
|
|
||||||
|
/// Start a new block with the given name
|
||||||
|
void enterBlock(const Char* name);
|
||||||
|
/// Leave the block we are in
|
||||||
|
void exitBlock();
|
||||||
|
|
||||||
|
/// Write the openedKey and the required indentation
|
||||||
|
void writeKey();
|
||||||
|
/// Output some taps to represent the indentation level
|
||||||
|
void writeIndentation();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Container types
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Writer::handle(const vector<T>& vector) {
|
||||||
|
/*String vectorKey = key;
|
||||||
|
while (key == vectorKey) { // TODO : check indent
|
||||||
|
moveNext(); // skip key
|
||||||
|
vector.resize(vector.size() + 1);
|
||||||
|
handle(vector.back());
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Writer::handle(const shared_ptr<T>& pointer) {
|
||||||
|
if (pointer) handle(*pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Reflection
|
||||||
|
|
||||||
|
/// Implement reflection as used by Writer
|
||||||
|
#define REFLECT_OBJECT_WRITER(Cls) \
|
||||||
|
template<> void Writer::handle<Cls>(const Cls& object) { \
|
||||||
|
const_cast<Cls&>(object).reflect(*this); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Reflection for enumerations
|
||||||
|
|
||||||
|
/// Implement enum reflection as used by Writer
|
||||||
|
#define REFLECT_ENUM_WRITER(Enum) \
|
||||||
|
template<> void Writer::handle<Enum>(const Enum& enum_) { \
|
||||||
|
EnumWriter writer(*this); \
|
||||||
|
reflect_ ## Enum(const_cast<Enum&>(enum_), writer); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'Tag' to be used when reflecting enumerations for Writer
|
||||||
|
class EnumWriter {
|
||||||
|
public:
|
||||||
|
inline EnumWriter(Writer& writer)
|
||||||
|
: writer(writer) {}
|
||||||
|
|
||||||
|
/// Handle a possible value for the enum, if the name matches the name in the input
|
||||||
|
template <typename Enum>
|
||||||
|
inline void handle(const Char* name, Enum value, Enum enum_) {
|
||||||
|
if (enum_ == value) {
|
||||||
|
writer.handle(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Writer& writer; //^ The writer to write output to
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_PREC
|
||||||
|
#define HEADER_UTIL_PREC
|
||||||
|
|
||||||
|
/** @file util/prec.hpp
|
||||||
|
*
|
||||||
|
* Precompiled header, and aliasses for common types
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Compiler specific
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning (disable: 4100) // unreferenced formal parameter
|
||||||
|
# pragma warning (disable: 4800) // 'int' : forcing value to bool 'true' or 'false' (performance warning)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
// Wx headers
|
||||||
|
#include <wx/setup.h>
|
||||||
|
#include <wx/wxprec.h>
|
||||||
|
#include <wx/image.h>
|
||||||
|
#include <wx/datetime.h>
|
||||||
|
|
||||||
|
// Std headers
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// MSE utility headers (ones unlikely to change and used everywhere)
|
||||||
|
#include "for_each.hpp"
|
||||||
|
#include "string.hpp"
|
||||||
|
#include "smart_ptr.hpp"
|
||||||
|
#include "index_map.hpp"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Wx Aliasses
|
||||||
|
|
||||||
|
// Remove some of the wxUglyness
|
||||||
|
|
||||||
|
typedef wxPanel Panel;
|
||||||
|
typedef wxWindow Window;
|
||||||
|
typedef wxFrame Frame;
|
||||||
|
|
||||||
|
typedef wxBitmap Bitmap;
|
||||||
|
typedef wxImage Image;
|
||||||
|
typedef wxColour Color;
|
||||||
|
typedef wxDC DC;
|
||||||
|
|
||||||
|
typedef wxDateTime DateTime;
|
||||||
|
|
||||||
|
typedef wxOutputStream OutputStream;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Other aliasses
|
||||||
|
|
||||||
|
typedef unsigned char Byte;
|
||||||
|
typedef unsigned int UInt;
|
||||||
|
|
||||||
|
/// Null pointer
|
||||||
|
#define nullptr 0
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_REAL_POINT
|
||||||
|
#define HEADER_UTIL_REAL_POINT
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/prec.hpp>
|
||||||
|
#include <util/vector2d.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Point using doubles
|
||||||
|
|
||||||
|
/// A point using real (double) coordinates
|
||||||
|
typedef Vector2D RealPoint;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Size using doubles
|
||||||
|
|
||||||
|
/// A size (width,height) using real (double) coordinates
|
||||||
|
class RealSize {
|
||||||
|
public:
|
||||||
|
double width;
|
||||||
|
double height;
|
||||||
|
|
||||||
|
inline RealSize()
|
||||||
|
: width(0), height(0)
|
||||||
|
{}
|
||||||
|
inline RealSize(double w, double h)
|
||||||
|
: width(w), height(h)
|
||||||
|
{}
|
||||||
|
inline RealSize(wxSize s)
|
||||||
|
: width(s.GetWidth()), height(s.GetHeight())
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// Addition of two sizes
|
||||||
|
inline void operator += (const RealSize& s2) {
|
||||||
|
width += s2.width;
|
||||||
|
height += s2.height;
|
||||||
|
}
|
||||||
|
/// Addition of two sizes
|
||||||
|
inline RealSize operator + (const RealSize& s2) const {
|
||||||
|
return RealSize(width + s2.width, height + s2.height);
|
||||||
|
}
|
||||||
|
/// Difference of two sizes
|
||||||
|
inline void operator -= (const RealSize& s2){
|
||||||
|
width -= s2.width;
|
||||||
|
height -= s2.height;
|
||||||
|
}
|
||||||
|
/// Difference of two sizes
|
||||||
|
inline RealSize operator - (const RealSize& s2) const {
|
||||||
|
return RealSize(width - s2.width, height - s2.height);
|
||||||
|
}
|
||||||
|
/// Inversion of a size, inverts both components
|
||||||
|
inline RealSize operator - () const {
|
||||||
|
return RealSize(-width, -height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiplying a size by a scalar r, multiplies both components
|
||||||
|
inline void operator *= (double r) {
|
||||||
|
width *= r;
|
||||||
|
height *= r;
|
||||||
|
}
|
||||||
|
/// Multiplying a size by a scalar r, multiplies both components
|
||||||
|
inline RealSize operator * (double r) const {
|
||||||
|
return RealSize(width * r, height * r);
|
||||||
|
}
|
||||||
|
/// Dividing a size by a scalar r, divides both components
|
||||||
|
inline RealSize operator / (double r) const {
|
||||||
|
return RealSize(width / r, height / r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Can be converted to a wxSize, with integer components
|
||||||
|
inline operator wxSize() {
|
||||||
|
return wxSize(realRound(width), realRound(height));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Rectangle using doubles
|
||||||
|
|
||||||
|
/// A rectangle (postion and size) using real (double) coordinats
|
||||||
|
class RealRect {
|
||||||
|
public:
|
||||||
|
/// Position of the top left corner
|
||||||
|
RealPoint position;
|
||||||
|
/// Size of the rectangle
|
||||||
|
RealSize size;
|
||||||
|
|
||||||
|
inline RealRect(const RealPoint& position, const RealSize& size)
|
||||||
|
: position(position), size(size)
|
||||||
|
{}
|
||||||
|
inline RealRect(double x, double y, double w, double h)
|
||||||
|
: position(x,y), size(w,h)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Operators
|
||||||
|
|
||||||
|
inline RealPoint operator + (const RealSize& s, const RealPoint& p) {
|
||||||
|
return RealPoint(p.x + s.width, p.y + s.height);
|
||||||
|
}
|
||||||
|
inline RealPoint operator + (const RealPoint& p, const RealSize& s) {
|
||||||
|
return RealPoint(p.x + s.width, p.y + s.height);
|
||||||
|
}
|
||||||
|
inline RealPoint operator - (const RealPoint& p, const RealSize& s) {
|
||||||
|
return RealPoint(p.x - s.width, p.y - s.height);
|
||||||
|
}
|
||||||
|
inline void operator += (RealPoint& p, const RealSize& s) {
|
||||||
|
p.x += s.width;
|
||||||
|
p.y += s.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_REFLECT
|
||||||
|
#define HEADER_UTIL_REFLECT
|
||||||
|
|
||||||
|
/** @file util/reflect.hpp
|
||||||
|
*
|
||||||
|
* Reflection of classes, currently reflection is used for (de)serialization.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include "io/reader.hpp"
|
||||||
|
#include "io/writer.hpp"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Declaring reflection
|
||||||
|
|
||||||
|
/// Declare that a class supports reflection
|
||||||
|
/// Reflection allows the member variables of a class to be inspected at runtime.
|
||||||
|
#define DECLARE_REFLECTION() \
|
||||||
|
protected: \
|
||||||
|
template<class Tag> void reflect_impl(Tag& tag); \
|
||||||
|
friend class Reader; \
|
||||||
|
friend class Writer; \
|
||||||
|
void reflect(Reader& reader); \
|
||||||
|
void reflect(Writer& writer)
|
||||||
|
|
||||||
|
/// Declare that a class supports reflection, which can be overridden in derived classes
|
||||||
|
#define DECLARE_REFLECTION_VIRTUAL() \
|
||||||
|
protected: \
|
||||||
|
template<class Tag> void reflect_impl(Tag& tag); \
|
||||||
|
friend class Reader; \
|
||||||
|
friend class Writer; \
|
||||||
|
virtual void reflect(Reader& reader); \
|
||||||
|
virtual void reflect(Writer& writer)
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Implementing reflection
|
||||||
|
|
||||||
|
/// Implement the refelection of a class type Cls
|
||||||
|
/// Reflection allows the member variables of a class to be inspected at runtime.
|
||||||
|
///
|
||||||
|
/// Currently creates the methods:
|
||||||
|
/// - Reader::handle(Cls&)
|
||||||
|
/// - Writer::handle(Cls&)
|
||||||
|
/** Usage:
|
||||||
|
* \begincode
|
||||||
|
* IMPLEMENT_REFLECTION(MyClass) {
|
||||||
|
* REFLECT(a_variable_in_my_class);
|
||||||
|
* REFLECT(another_variable_in_my_class);
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
#define IMPLEMENT_REFLECTION(Cls) \
|
||||||
|
REFLECT_OBJECT_READER(Cls) \
|
||||||
|
REFLECT_OBJECT_WRITER(Cls) \
|
||||||
|
/* Extra level, so it can be declared virtual */ \
|
||||||
|
void Cls::reflect(Reader& reader) { \
|
||||||
|
reflect_impl(reader); \
|
||||||
|
} \
|
||||||
|
void Cls::reflect(Writer& writer) { \
|
||||||
|
reflect_impl(writer); \
|
||||||
|
} \
|
||||||
|
template <class Tag> \
|
||||||
|
void Cls::reflect_impl(Tag& tag)
|
||||||
|
|
||||||
|
/// Reflect a variable
|
||||||
|
#define REFLECT(var) tag.handle(_(#var), var)
|
||||||
|
/// Reflect a variable under the given name
|
||||||
|
#define REFLECT_N(name, var) tag.handle(_(name), var)
|
||||||
|
|
||||||
|
/// Declare that the variables of a base class should also be reflected
|
||||||
|
#define REFLECT_BASE(Base) Base::reflect_impl(tag)
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Reflecting enums
|
||||||
|
|
||||||
|
/// Implement the refelection of a enumeration type Enum
|
||||||
|
/** Usage:
|
||||||
|
* \begincode
|
||||||
|
* IMPLEMENT_REFLECTION_ENUM(MyEnum) {
|
||||||
|
* VALUE(value_of_enum_1);
|
||||||
|
* VALUE(value_of_enum_2);
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* When reading the first value declared is the default value
|
||||||
|
*
|
||||||
|
* Currently creates the methods:
|
||||||
|
* - Reader::handle(Enum&
|
||||||
|
* - Writer::handle(const Enum&)
|
||||||
|
*/
|
||||||
|
#define IMPLEMENT_REFLECTION_ENUM(Enum) \
|
||||||
|
template <class Tag> \
|
||||||
|
void reflect_ ## Enum (Enum& enum_, Tag& tag); \
|
||||||
|
REFLECT_ENUM_READER(Enum) \
|
||||||
|
REFLECT_ENUM_WRITER(Enum) \
|
||||||
|
template <class Tag> \
|
||||||
|
void reflect_ ## Enum (Enum& enum_, Tag& tag)
|
||||||
|
|
||||||
|
/// Declare a possible value of an enum
|
||||||
|
#define VALUE(val) tag.handle(_(#val), val, enum_)
|
||||||
|
|
||||||
|
/// Declare a possible value of an enum under the given name
|
||||||
|
#define VALUE_N(name, val) tag.handle(_(name), val, enum_)
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 <util/rotation.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Rotation
|
||||||
|
|
||||||
|
Rotation::Rotation(int angle, const RealRect& rect, double zoom)
|
||||||
|
: angle(angle)
|
||||||
|
, size(rect.size)
|
||||||
|
, origin(rect.position)
|
||||||
|
, zoom(zoom)
|
||||||
|
{
|
||||||
|
// set origin
|
||||||
|
if (revX()) origin.x += size.width;
|
||||||
|
if (revY()) origin.x += size.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
RealPoint Rotation::tr(const RealPoint& p) const {
|
||||||
|
return tr(RealSize(p.x, p.y)) + origin; // TODO : optimize?
|
||||||
|
}
|
||||||
|
RealSize Rotation::tr(const RealSize& s) const {
|
||||||
|
if (sideways()) {
|
||||||
|
return RealSize(negX(s.height), negY(s.width)) * zoom;
|
||||||
|
} else {
|
||||||
|
return RealSize(negX(s.width), negY(s.height)) * zoom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RealRect Rotation::tr(const RealRect& r) const {
|
||||||
|
return RealRect(tr(r.position), tr(r.size));
|
||||||
|
}
|
||||||
|
|
||||||
|
RealSize Rotation::trNoNeg(const RealSize& s) const {
|
||||||
|
if (sideways()) {
|
||||||
|
return RealSize(s.height, s.width) * zoom;
|
||||||
|
} else {
|
||||||
|
return RealSize(s.width, s.height) * zoom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RealRect Rotation::trNoNeg(const RealRect& r) const {
|
||||||
|
throw "TODO";
|
||||||
|
}
|
||||||
|
|
||||||
|
RealPoint Rotation::trInv(const RealPoint& p) const {
|
||||||
|
RealPoint p2 = (p - origin) / zoom;
|
||||||
|
if (sideways()) {
|
||||||
|
return RealPoint(negY(p2.y), negX(p2.x));
|
||||||
|
} else {
|
||||||
|
return RealPoint(negX(p2.x), negY(p2.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_ROTATION
|
||||||
|
#define HEADER_UTIL_ROTATION
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/prec.hpp>
|
||||||
|
#include <util/real_point.hpp>
|
||||||
|
#include <gfx/gfx.hpp>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Rotation
|
||||||
|
|
||||||
|
/// An object that can rotate coordinates inside a specified rectangle
|
||||||
|
/** This class has lots of tr*** functions, they convert
|
||||||
|
* internal coordinates to external/screen coordinates.
|
||||||
|
* tr***inv do the opposite.
|
||||||
|
*/
|
||||||
|
class Rotation {
|
||||||
|
public:
|
||||||
|
/// Construct a rotation object with the given rectangle of external coordinates
|
||||||
|
/// and a given rotation angle and zoom factor
|
||||||
|
Rotation(int angle, const RealRect& rect, double zoom = 1.0);
|
||||||
|
|
||||||
|
/// Change the zoom factor
|
||||||
|
inline void setZoom(double z) { zoom = z; }
|
||||||
|
/// Change the angle
|
||||||
|
void setAngle(int a);
|
||||||
|
|
||||||
|
/// Translate a size or length
|
||||||
|
inline double trS(double s) const { return s * zoom; }
|
||||||
|
|
||||||
|
/// Translate a single point
|
||||||
|
RealPoint tr(const RealPoint& p) const;
|
||||||
|
/// Translate a single size, the result may be negative
|
||||||
|
RealSize tr(const RealSize& s) const;
|
||||||
|
/// Translate a rectangle, the size of the result may be negative
|
||||||
|
RealRect tr(const RealRect& r) const;
|
||||||
|
|
||||||
|
/// Translate a size, the result will never be negative
|
||||||
|
RealSize trNoNeg(const RealSize& s) const;
|
||||||
|
/// Translate a rectangle, the result will never have a negative size
|
||||||
|
RealRect trNoNeg(const RealRect& r) const;
|
||||||
|
|
||||||
|
/// Translate a size or length back to internal 'coordinates'
|
||||||
|
inline double trInvS(double s) const { return s / zoom; }
|
||||||
|
|
||||||
|
/// Translate a point back to internal coordinates
|
||||||
|
RealPoint trInv(const RealPoint& p) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int angle; //^ The angle of rotation in degrees (counterclockwise)
|
||||||
|
RealSize size; //^ Size of the rectangle, in external coordinates
|
||||||
|
RealPoint origin; //^ tr(0,0)
|
||||||
|
double zoom; //^ Zoom factor, zoom = 2.0 means that 1 internal = 2 external
|
||||||
|
|
||||||
|
/// Is the rotation sideways (90 or 270 degrees)?
|
||||||
|
// Note: angle & 2 == 0 for angle in {0, 180} and != 0 for angle in {90, 270)
|
||||||
|
inline bool sideways() const { return (angle & 2) != 0; }
|
||||||
|
/// Is the x axis 'reversed' (after turning sideways)?
|
||||||
|
inline bool revX() const { return angle >= 180; }
|
||||||
|
/// Is the y axis 'reversed' (after turning sideways)?
|
||||||
|
inline bool revY() const { return angle == 90 || angle == 180; }
|
||||||
|
/// Negate if revX
|
||||||
|
inline double negX(double d) const { return revX() ? -d : d; }
|
||||||
|
/// Negate if revY
|
||||||
|
inline double negY(double d) const { return revY() ? -d : d; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Rotater
|
||||||
|
|
||||||
|
/// An object that changes a rotation RIIA style
|
||||||
|
/** Usage:
|
||||||
|
* \begincode
|
||||||
|
* Rotation a, b;
|
||||||
|
* Rotater(a,b);
|
||||||
|
* a.tr(x) // now acts as a.tr(b.tr(x))
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
class Rotater {
|
||||||
|
/// Compose a rotation by onto the rotation rot
|
||||||
|
/** rot is restored when this object is destructed
|
||||||
|
*/
|
||||||
|
Rotater(Rotation& rot, const Rotation& by);
|
||||||
|
~Rotater();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : RotatedDC
|
||||||
|
|
||||||
|
/// A DC with rotation applied
|
||||||
|
/** All draw** functions take internal coordinates.
|
||||||
|
*/
|
||||||
|
class RotatedDC : public Rotation {
|
||||||
|
public:
|
||||||
|
RotatedDC(int angle, const RealRect& rect, double zoom = 1.0);
|
||||||
|
RotatedDC(const Rotation& rotation);
|
||||||
|
|
||||||
|
// ----------------------------- : Drawing
|
||||||
|
|
||||||
|
void DrawText (const String& text, const RealPoint& pos);
|
||||||
|
void DrawBitmap(const Bitmap& bitmap, const RealPoint& pos);
|
||||||
|
void DrawImage (const Image& image, const RealPoint& pos, ImageCombine combine = COMBINE_NORMAL);
|
||||||
|
void DrawLine (const RealPoint& p1, const RealPoint& p2);
|
||||||
|
void DrawRectangle(const RealRect& r);
|
||||||
|
void DrawRoundedRectangle(const RealRect& r, double radius);
|
||||||
|
|
||||||
|
// ----------------------------- : Forwarded properties
|
||||||
|
|
||||||
|
void SetPen(const wxPen&);
|
||||||
|
void SetBrush(const wxBrush&);
|
||||||
|
void SetTextForeground(const Color&);
|
||||||
|
void SetLogicalFunction(int function);
|
||||||
|
|
||||||
|
RealSize getTextExtent(const String& string);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_SMART_PTR
|
||||||
|
#define HEADER_UTIL_SMART_PTR
|
||||||
|
|
||||||
|
/** @file util/shared_ptr.hpp
|
||||||
|
*
|
||||||
|
* Utilities related to boost smart pointers
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
using namespace boost;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Declaring
|
||||||
|
|
||||||
|
/// Declares the type TypeP as a shared_ptr<Type>
|
||||||
|
#define DECLARE_POINTER_TYPE(Type) \
|
||||||
|
class Type; \
|
||||||
|
typedef shared_ptr<Type> Type##P
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Creating
|
||||||
|
|
||||||
|
/// Allocate a new shared-pointed object
|
||||||
|
template <typename T>
|
||||||
|
inline shared_ptr<T> new_shared() {
|
||||||
|
return shared_ptr<T>(new T());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a new shared-pointed object, given one argument to pass to the ctor of T
|
||||||
|
template <typename T, typename A0>
|
||||||
|
inline shared_ptr<T> new_shared1(const A0& a0) {
|
||||||
|
return shared_ptr<T>(new T(a0));
|
||||||
|
}
|
||||||
|
/// Allocate a new shared-pointed object, given two arguments to pass to the ctor of T
|
||||||
|
template <typename T, typename A0, typename A1>
|
||||||
|
inline shared_ptr<T> new_shared2(const A0& a0, const A1& a1) {
|
||||||
|
return shared_ptr<T>(new T(a0, a1));
|
||||||
|
}
|
||||||
|
/// Allocate a new shared-pointed object, given three arguments to pass to the ctor of T
|
||||||
|
template <typename T, typename A0, typename A1, typename A2>
|
||||||
|
inline shared_ptr<T> new_shared3(const A0& a0, const A1& a1, const A2& a2) {
|
||||||
|
return shared_ptr<T>(new T(a0, a1, a2));
|
||||||
|
}
|
||||||
|
/// Allocate a new shared-pointed object, given four arguments to pass to the ctor of T
|
||||||
|
template <typename T, typename A0, typename A1, typename A2, typename A3>
|
||||||
|
inline shared_ptr<T> new_shared4(const A0& a0, const A1& a1, const A2& a2, const A3& a3) {
|
||||||
|
return shared_ptr<T>(new T(a0, a1, a2, a3));
|
||||||
|
}
|
||||||
|
/// Allocate a new shared-pointed object, given seven arguments to pass to the ctor of T
|
||||||
|
template <typename T, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
|
||||||
|
inline shared_ptr<T> new_shared7(const A0& a0, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6) {
|
||||||
|
return shared_ptr<T>(new T(a0, a1, a2, a3, a4, a5, a6));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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 "string.hpp"
|
||||||
|
#include "for_each.hpp"
|
||||||
|
#include <wx/txtstrm.h>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Unicode
|
||||||
|
|
||||||
|
String decodeUTF8BOM(const String& s) {
|
||||||
|
#ifdef UNICODE
|
||||||
|
if (!s.empty() && s.GetChar(0) == L'\xFEFF') {
|
||||||
|
// skip byte-order-mark
|
||||||
|
return s.substr(1);
|
||||||
|
} else {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
wxWCharBuffer buf = s.wc_str(wxConvUTF8);
|
||||||
|
if (buf && buf[size_t(0)] == L'\xFEFF') {
|
||||||
|
// skip byte-order-mark
|
||||||
|
return String(buf + 1, *wxConvCurrent);
|
||||||
|
} else {
|
||||||
|
return String(buf, *wxConvCurrent);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeUTF8(wxTextOutputStream& stream, const String& str) {
|
||||||
|
#ifdef UNICODE
|
||||||
|
stream.WriteString(str);
|
||||||
|
#else
|
||||||
|
wxWCharBuffer buf = str.wc_str(*wxConvCurrent);
|
||||||
|
stream.WriteString(wxString(buf, wxConvUTF8));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : String utilities
|
||||||
|
|
||||||
|
String trim(const String& s){
|
||||||
|
size_t start = s.find_first_not_of(_(" \t"));
|
||||||
|
size_t end = s.find_last_not_of( _(" \t"));
|
||||||
|
if (start == String::npos) {
|
||||||
|
return String();
|
||||||
|
} else {
|
||||||
|
return s.substr(start, end - start + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimLeft(const String& s) {
|
||||||
|
size_t start = s.find_first_not_of(_(' '));
|
||||||
|
if (start == String::npos) {
|
||||||
|
return String();
|
||||||
|
} else {
|
||||||
|
return s.substr(start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Words
|
||||||
|
|
||||||
|
String lastWord(const String& s) {
|
||||||
|
size_t endLastWord = s.find_last_not_of(_(' '));
|
||||||
|
size_t startLastWord = s.find_last_of( _(' '), endLastWord);
|
||||||
|
if (endLastWord == String::npos) {
|
||||||
|
return String(); // empty string
|
||||||
|
} else if (startLastWord == String::npos) {
|
||||||
|
return s.substr(0, endLastWord + 1);// first word
|
||||||
|
} else {
|
||||||
|
return s.substr(startLastWord + 1, endLastWord - startLastWord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String stripLastWord(const String& s) {
|
||||||
|
size_t endLastWord = s.find_last_not_of(_(' '));
|
||||||
|
size_t startLastWord = s.find_last_of(_(' '), endLastWord);
|
||||||
|
if (endLastWord == String::npos || startLastWord == String::npos) {
|
||||||
|
return String(); // single word or empty string
|
||||||
|
} else {
|
||||||
|
return s.substr(0, startLastWord + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Caseing
|
||||||
|
|
||||||
|
/// Quick check to see if the substring starting at the given iterator is equal
|
||||||
|
/// to some given string
|
||||||
|
bool is_substr(const String& s, String::iterator it, const Char* cmp) {
|
||||||
|
while (it != s.end() && *cmp != 0) {
|
||||||
|
if (*it++ != *cmp++) return false;
|
||||||
|
}
|
||||||
|
return *cmp == 0;
|
||||||
|
}
|
||||||
|
String capitalize(const String& s) {
|
||||||
|
String result = s;
|
||||||
|
bool afterSpace = true;
|
||||||
|
FOR_EACH_IT(it, result) {
|
||||||
|
if (*it == ' ') {
|
||||||
|
afterSpace = true;
|
||||||
|
} else if (afterSpace) {
|
||||||
|
afterSpace = false;
|
||||||
|
if (it != s.begin() &&
|
||||||
|
(is_substr(result,it,_("is ")) || is_substr(result,it,_("the ")) ||
|
||||||
|
is_substr(result,it,_("in ")) || is_substr(result,it,_("of ")) ||
|
||||||
|
is_substr(result,it,_("to ")) || is_substr(result,it,_("at ")) ||
|
||||||
|
is_substr(result,it,_("a " )))) {
|
||||||
|
// Short words are not capitalized, keep lower case
|
||||||
|
} else {
|
||||||
|
*it = toUpper(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_STRING
|
||||||
|
#define HEADER_UTIL_STRING
|
||||||
|
|
||||||
|
/** @file util/string.hpp
|
||||||
|
*
|
||||||
|
* String and character utility functions and macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include "prec.hpp"
|
||||||
|
#include "for_each.hpp"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <boost/preprocessor/cat.hpp>
|
||||||
|
|
||||||
|
class wxTextOutputStream;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : String type
|
||||||
|
|
||||||
|
/// The string type used throughout MSE
|
||||||
|
typedef wxString String;
|
||||||
|
|
||||||
|
DECLARE_TYPEOF_NO_REV(String); // iterating over characters in a string
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Unicode
|
||||||
|
|
||||||
|
/// u if UNICODE is defined, a otherwise
|
||||||
|
#ifdef UNICODE
|
||||||
|
# define IF_UNICODE(u,a) u
|
||||||
|
#else
|
||||||
|
# define IF_UNICODE(u,a) a
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef _
|
||||||
|
/// A string/character constant, correctly handled in unicode builds
|
||||||
|
#define _(S) IF_UNICODE(BOOST_PP_CAT(L,S), S)
|
||||||
|
|
||||||
|
/// The character type used
|
||||||
|
typedef IF_UNICODE(wchar_t, char) Char;
|
||||||
|
|
||||||
|
/// Decode a UTF8 string
|
||||||
|
/** In non-unicode builds the input is considered to be an incorrectly encoded utf8 string.
|
||||||
|
* In unicode builds it is a normal string, utf8 already decoded.
|
||||||
|
* Also removes a byte-order-mark from the start of the string if it is pressent
|
||||||
|
*/
|
||||||
|
String decodeUTF8BOM(const String& s);
|
||||||
|
|
||||||
|
/// UTF8 Byte order mark for writing at the start of files
|
||||||
|
/** In non-unicode builds it is UTF8 encoded \xFEFF
|
||||||
|
* In unicode builds it is a normal \xFEFF
|
||||||
|
*/
|
||||||
|
const Char BYTE_ORDER_MARK[] = IF_UNICODE(L"\xFEFF", "\xEF\xBB\xBF");
|
||||||
|
|
||||||
|
/// Writes a string to an output stream, encoded as UTF8
|
||||||
|
void writeUTF8(wxTextOutputStream& stream, const String& str);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Char functions
|
||||||
|
|
||||||
|
// Character set tests
|
||||||
|
inline bool isSpace(Char c) { return IF_UNICODE( iswspace(c) , isspace(c) ); }
|
||||||
|
inline bool isAlpha(Char c) { return IF_UNICODE( iswalpha(c) , isalpha(c) ); }
|
||||||
|
inline bool isDigit(Char c) { return IF_UNICODE( iswdigit(c) , isdigit(c) ); }
|
||||||
|
inline bool isAlnum(Char c) { return IF_UNICODE( iswalnum(c) , isalnum(c) ); }
|
||||||
|
inline bool isUpper(Char c) { return IF_UNICODE( iswupper(c) , isupper(c) ); }
|
||||||
|
inline bool isLower(Char c) { return IF_UNICODE( iswlower(c) , islower(c) ); }
|
||||||
|
// Character conversions
|
||||||
|
inline Char toUpper(Char c) { return IF_UNICODE( towupper(c) , toupper(c) ); }
|
||||||
|
inline Char toLower(Char c) { return IF_UNICODE( towlower(c) , tolower(c) ); }
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : String utilities
|
||||||
|
|
||||||
|
/// Remove whitespace from both ends of a string
|
||||||
|
String trim(const String&);
|
||||||
|
|
||||||
|
/// Remove whitespace from the start of a string
|
||||||
|
String trimLeft(const String&);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Words
|
||||||
|
|
||||||
|
/// Returns the last word in a string
|
||||||
|
String lastWord(const String&);
|
||||||
|
|
||||||
|
/// Remove the last word from a string, leaves whitespace before that word
|
||||||
|
String stripLastWord(const String&);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Caseing
|
||||||
|
|
||||||
|
/// Make each word in a string start with an upper case character.
|
||||||
|
/// for use in menus
|
||||||
|
String capitalize(const String&);
|
||||||
|
|
||||||
|
/// Make the first word in a string start with an upper case character.
|
||||||
|
/// for use in dialogs
|
||||||
|
String capitalizeSentence(const String&);
|
||||||
|
|
||||||
|
/// Convert a field name to cannocial form: lower case and ' ' instead of '_'
|
||||||
|
/// non alphanumeric characters are ignored
|
||||||
|
/// "camalCase" is converted to words "camel case"
|
||||||
|
String cannocialNameForm(const String&);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_VECTOR2D
|
||||||
|
#define HEADER_UTIL_VECTOR2D
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/prec.hpp>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Rounding
|
||||||
|
|
||||||
|
// Rounding function for converting doubles to integers
|
||||||
|
inline int realRound(double d) {
|
||||||
|
return d > 0 ? d + 0.5 : d - 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Vector2D
|
||||||
|
|
||||||
|
/// A simple 2d vector class
|
||||||
|
class Vector2D {
|
||||||
|
public:
|
||||||
|
/// Coordinates of this vector
|
||||||
|
double x, y;
|
||||||
|
|
||||||
|
/// Default contructor
|
||||||
|
inline Vector2D()
|
||||||
|
: x(0), y(0) {}
|
||||||
|
/// Contructor with given x and y values
|
||||||
|
inline Vector2D(double x, double y)
|
||||||
|
: x(x), y(y) {}
|
||||||
|
|
||||||
|
/// Addition of two vectors
|
||||||
|
inline Vector2D operator + (Vector2D p2) const {
|
||||||
|
return Vector2D(x + p2.x, y + p2.y);
|
||||||
|
}
|
||||||
|
/// Addition of two vectors
|
||||||
|
inline void operator += (Vector2D p2) {
|
||||||
|
x += p2.x; y += p2.y;
|
||||||
|
}
|
||||||
|
/// Subtract two vectors
|
||||||
|
inline Vector2D operator - (Vector2D p2) const {
|
||||||
|
return Vector2D(x - p2.x, y - p2.y);
|
||||||
|
}
|
||||||
|
/// Subtract two vectors
|
||||||
|
inline void operator -= (Vector2D p2) {
|
||||||
|
x -= p2.x; y -= p2.y;
|
||||||
|
}
|
||||||
|
/// Invert a vector
|
||||||
|
inline Vector2D operator - () const {
|
||||||
|
return Vector2D(-x, -y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply with a scalar
|
||||||
|
inline Vector2D operator * (double r) const {
|
||||||
|
return Vector2D(x * r, y * r);
|
||||||
|
}
|
||||||
|
/// Multiply with a scalar
|
||||||
|
inline void operator *= (double r) {
|
||||||
|
x *= r; y *= r;
|
||||||
|
}
|
||||||
|
/// Divide by a scalar
|
||||||
|
inline Vector2D operator / (double r) const {
|
||||||
|
return Vector2D(x / r, y / r);
|
||||||
|
}
|
||||||
|
/// Divide by a scalar
|
||||||
|
inline void operator /= (double r) {
|
||||||
|
x /= r; y /= r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inner product with another vector
|
||||||
|
inline double dot(Vector2D p2) const {
|
||||||
|
return (x * p2.x) + (y * p2.y);
|
||||||
|
}
|
||||||
|
/// Outer product with another vector
|
||||||
|
inline double cross(Vector2D p2) const {
|
||||||
|
return (x * p2.y) - (y * p2.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Piecewise multiplication
|
||||||
|
inline Vector2D mul(Vector2D p2) {
|
||||||
|
return Vector2D(x * p2.x, y * p2.y);
|
||||||
|
}
|
||||||
|
/// Piecewise division
|
||||||
|
inline Vector2D div(Vector2D p2) {
|
||||||
|
return Vector2D(x / p2.x, y / p2.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply a 'matrix' to this vector
|
||||||
|
inline Vector2D mul(Vector2D mx, Vector2D my) {
|
||||||
|
return Vector2D(dot(mx), dot(my));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the square of the length of this vector
|
||||||
|
inline double lengthSqr() const {
|
||||||
|
return x*x + y*y;
|
||||||
|
}
|
||||||
|
/// Returns the length of this vector
|
||||||
|
inline double length() const {
|
||||||
|
return sqrt(lengthSqr());
|
||||||
|
}
|
||||||
|
/// Returns a normalized version of this vector
|
||||||
|
/// i.e. length() == 1
|
||||||
|
inline Vector2D normalized() const {
|
||||||
|
return *this / length();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator wxPoint() const {
|
||||||
|
return wxPoint(realRound(x), realRound(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vector at infinity
|
||||||
|
static inline Vector2D infinity() {
|
||||||
|
double inf = numeric_limits<double>::infinity();
|
||||||
|
return Vector2D(inf, inf);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Piecewise minimum
|
||||||
|
inline Vector2D piecewise_min(Vector2D a, Vector2D b) {
|
||||||
|
return Vector2D(
|
||||||
|
a.x < b.x ? a.x : b.x,
|
||||||
|
a.y < b.y ? a.y : b.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Piecewise maximum
|
||||||
|
inline Vector2D piecewise_max(Vector2D a, Vector2D b) {
|
||||||
|
return Vector2D(
|
||||||
|
a.x < b.x ? b.x : a.x,
|
||||||
|
a.y < b.y ? b.y : a.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| 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_UTIL_WINDOW_ID
|
||||||
|
#define HEADER_UTIL_WINDOW_ID
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Menu ids
|
||||||
|
|
||||||
|
/// Window ids for menus and toolbars
|
||||||
|
enum MenuID {
|
||||||
|
|
||||||
|
ID_MENU_MIN = 0
|
||||||
|
, ID_MENU_MAX = 999
|
||||||
|
|
||||||
|
// File menu
|
||||||
|
, ID_FILE_NEW = wxID_NEW
|
||||||
|
, ID_FILE_OPEN = wxID_OPEN
|
||||||
|
, ID_FILE_SAVE = wxID_SAVE
|
||||||
|
, ID_FILE_SAVE_AS = wxID_SAVEAS
|
||||||
|
, ID_FILE_STORE = 1
|
||||||
|
, ID_FILE_EXIT = wxID_EXIT
|
||||||
|
, ID_FILE_EXPORT = 2
|
||||||
|
, ID_FILE_EXPORT_HTML = 3
|
||||||
|
, ID_FILE_EXPORT_IMAGE = 4
|
||||||
|
, ID_FILE_EXPORT_IMAGES = 5
|
||||||
|
, ID_FILE_EXPORT_APPR = 6
|
||||||
|
, ID_FILE_EXPORT_MWS = 7
|
||||||
|
, ID_FILE_PRINT = wxID_PRINT
|
||||||
|
, ID_FILE_PRINT_PREVIEW = wxID_PREVIEW
|
||||||
|
, ID_FILE_INSPECT = 8
|
||||||
|
, ID_FILE_RECENT = wxID_FILE1
|
||||||
|
, ID_FILE_RECENT_MAX = wxID_FILE9
|
||||||
|
|
||||||
|
// Edit menu
|
||||||
|
, ID_EDIT_UNDO = wxID_UNDO
|
||||||
|
, ID_EDIT_REDO = wxID_REDO
|
||||||
|
, ID_EDIT_CUT = wxID_CUT
|
||||||
|
, ID_EDIT_COPY = wxID_COPY
|
||||||
|
, ID_EDIT_PASTE = wxID_PASTE
|
||||||
|
, ID_EDIT_DELETE = 101
|
||||||
|
, ID_EDIT_DUPLICATE = 102
|
||||||
|
, ID_EDIT_FIND = wxID_FIND
|
||||||
|
, ID_EDIT_FIND_NEXT = 103
|
||||||
|
, ID_EDIT_REPLACE = wxID_REPLACE
|
||||||
|
, ID_EDIT_PREFERENCES = 104
|
||||||
|
|
||||||
|
// Window menu (MainWindow)
|
||||||
|
, ID_WINDOW_NEW = 201
|
||||||
|
, ID_WINDOW_MIN = 202
|
||||||
|
, ID_WINDOW_MAX = 220
|
||||||
|
|
||||||
|
// Help menu (MainWindow)
|
||||||
|
, ID_HELP_INDEX = 301
|
||||||
|
, ID_HELP_ABOUT
|
||||||
|
|
||||||
|
// Mode menu (SymbolWindow)
|
||||||
|
, ID_MODE_MIN = 401
|
||||||
|
, ID_MODE_SELECT = ID_MODE_MIN
|
||||||
|
, ID_MODE_ROTATE
|
||||||
|
, ID_MODE_POINTS
|
||||||
|
, ID_MODE_SHAPES
|
||||||
|
, ID_MODE_PAINT
|
||||||
|
, ID_MODE_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Child ids
|
||||||
|
|
||||||
|
/// Ids for menus on child panels (MainWindowPanel / SymbolEditorBase)
|
||||||
|
enum ChildMenuID {
|
||||||
|
|
||||||
|
ID_CHILD_MIN = 1000
|
||||||
|
, ID_CHILD_MAX = 2999
|
||||||
|
|
||||||
|
// Cards menu
|
||||||
|
, ID_CARD_ADD = 1001
|
||||||
|
, ID_CARD_ADD_MULT
|
||||||
|
, ID_CARD_REMOVE
|
||||||
|
, ID_CARD_PREV
|
||||||
|
, ID_CARD_NEXT
|
||||||
|
, ID_CARD_ROTATE
|
||||||
|
, ID_CARD_ROTATE_0
|
||||||
|
, ID_CARD_ROTATE_90
|
||||||
|
, ID_CARD_ROTATE_180
|
||||||
|
, ID_CARD_ROTATE_270
|
||||||
|
|
||||||
|
// Keyword menu
|
||||||
|
, ID_KEYWORD_ADD = 1101
|
||||||
|
, ID_KEYWORD_REMOVE
|
||||||
|
, ID_KEYWORD_PREV
|
||||||
|
, ID_KEYWORD_NEXT
|
||||||
|
|
||||||
|
|
||||||
|
// SymbolSelectEditor toolbar/menu
|
||||||
|
, ID_PART = 2001
|
||||||
|
, ID_PART_MERGE = ID_PART + PART_MERGE
|
||||||
|
, ID_PART_SUBTRACT = ID_PART + PART_SUBTRACT
|
||||||
|
, ID_PART_INTERSECTION = ID_PART + PART_INTERSECTION
|
||||||
|
, ID_PART_DIFFERENCE = ID_PART + PART_DIFFERENCE
|
||||||
|
, ID_PART_OVERLAP = ID_PART + PART_OVERLAP
|
||||||
|
, ID_PART_BORDER = ID_PART + PART_BORDER
|
||||||
|
, ID_PART_MAX
|
||||||
|
|
||||||
|
// SymbolPointEditor toolbar/menu
|
||||||
|
, ID_SEGMENT = 2101
|
||||||
|
, ID_SEGMENT_LINE = ID_SEGMENT + SEGMENT_LINE
|
||||||
|
, ID_SEGMENT_CURVE = ID_SEGMENT + SEGMENT_CURVE
|
||||||
|
, ID_SEGMENT_MAX
|
||||||
|
, ID_LOCK = 2151
|
||||||
|
, ID_LOCK_FREE = ID_LOCK + LOCK_FREE
|
||||||
|
, ID_LOCK_DIR = ID_LOCK + LOCK_DIR
|
||||||
|
, ID_LOCK_SIZE = ID_LOCK + LOCK_SIZE
|
||||||
|
, ID_LOCK_MAX
|
||||||
|
|
||||||
|
// SymbolBasicShapeEditor toolbar/menu
|
||||||
|
, ID_SHAPE = 2201
|
||||||
|
, ID_SHAPE_CIRCLE = ID_SHAPE
|
||||||
|
, ID_SHAPE_RECTANGLE
|
||||||
|
, ID_SHAPE_POLYGON
|
||||||
|
, ID_SHAPE_STAR
|
||||||
|
, ID_SHAPE_MAX
|
||||||
|
, ID_SIDES
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Control/window ids
|
||||||
|
|
||||||
|
/// Window ids for controls
|
||||||
|
enum ControlID {
|
||||||
|
|
||||||
|
ID_CONTROL_MIN = 6000
|
||||||
|
, ID_CONTROL_MAX = 6999
|
||||||
|
|
||||||
|
// Controls
|
||||||
|
, ID_VIEWER = 6001
|
||||||
|
, ID_EDITOR
|
||||||
|
, ID_CONTROL
|
||||||
|
, ID_CARD_LIST
|
||||||
|
, ID_PART_LIST
|
||||||
|
, ID_NOTES
|
||||||
|
, ID_KEYWORD
|
||||||
|
, ID_PARAMETER
|
||||||
|
, ID_SEPARATOR
|
||||||
|
, ID_REMINDER
|
||||||
|
, ID_RULES
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Generate a header and source file
|
||||||
|
|
||||||
|
$file = $ARGV[0];
|
||||||
|
$macro = uc $file;
|
||||||
|
$macro =~ s@/@_@g;
|
||||||
|
|
||||||
|
# read templates
|
||||||
|
|
||||||
|
open F, "../src/code_template.hpp";
|
||||||
|
$hpp = join('',<F>);
|
||||||
|
close F;
|
||||||
|
|
||||||
|
open F, "../src/code_template.cpp";
|
||||||
|
$cpp = join('',<F>);
|
||||||
|
close F;
|
||||||
|
|
||||||
|
# insert stuff
|
||||||
|
|
||||||
|
$hpp =~ s/_\n/_$macro\n/g;
|
||||||
|
|
||||||
|
$cpp =~ s@<util/prec.hpp>@<$file.hpp>@g;
|
||||||
|
|
||||||
|
# write files
|
||||||
|
|
||||||
|
open F, "> ../src/$file.hpp";
|
||||||
|
print F $hpp;
|
||||||
|
close F;
|
||||||
|
|
||||||
|
open F, "> ../src/$file.cpp";
|
||||||
|
print F $cpp;
|
||||||
|
close F;
|
||||||
Reference in New Issue
Block a user