summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMicksha <ms-shaman@gmx.de>2019-01-04 21:37:34 +0100
committerMicksha <ms-shaman@gmx.de>2019-04-13 19:53:49 +0000
commit168ad9d6c730e93d76c801da607df84355187a39 (patch)
treeb771ad105e5a73733ede7559e22467f13a2880a8
parent8c75e651f00048dda32db85b2bff34e131960864 (diff)
downloadevol-tools-168ad9d6c730e93d76c801da607df84355187a39.tar.gz
evol-tools-168ad9d6c730e93d76c801da607df84355187a39.tar.bz2
evol-tools-168ad9d6c730e93d76c801da607df84355187a39.tar.xz
evol-tools-168ad9d6c730e93d76c801da607df84355187a39.zip
update saedit to saedit2
-rw-r--r--saedit/.gitignore6
-rw-r--r--saedit/.gitlab-ci.yml6
-rw-r--r--saedit/COPYING340
-rw-r--r--saedit/LICENSE674
-rw-r--r--saedit/Makefile52
-rw-r--r--saedit/action.c176
-rw-r--r--saedit/action.h59
-rw-r--r--saedit/animation.c386
-rw-r--r--saedit/animation.h59
-rwxr-xr-xsaedit/bmake28
-rw-r--r--saedit/buffer.c87
-rw-r--r--saedit/buffer.h22
-rw-r--r--saedit/callbacks.c432
-rw-r--r--saedit/callbacks.h96
-rw-r--r--saedit/common.c22
-rw-r--r--saedit/common.h24
-rw-r--r--saedit/config.c229
-rw-r--r--saedit/config.h75
-rw-r--r--saedit/context.c227
-rw-r--r--saedit/context.h50
-rw-r--r--saedit/drawfuncs.c107
-rw-r--r--saedit/drawfuncs.h33
-rw-r--r--saedit/errors.c23
-rw-r--r--saedit/errors.h12
-rw-r--r--saedit/file.c103
-rw-r--r--saedit/file.h16
-rwxr-xr-xsaedit/glade.sh3
-rw-r--r--saedit/glade/saedit-catalog.xml26
-rw-r--r--saedit/grounds/earth.pngbin4193 -> 0 bytes
-rw-r--r--saedit/grounds/grass.pngbin5142 -> 0 bytes
-rw-r--r--saedit/grounds/sand.pngbin5205 -> 0 bytes
-rw-r--r--saedit/grounds/water.pngbin7945 -> 0 bytes
-rw-r--r--saedit/iface.ui577
-rw-r--r--saedit/imageset.c130
-rw-r--r--saedit/imageset.h50
-rw-r--r--saedit/interactor.c439
-rw-r--r--saedit/interactor.h110
-rw-r--r--saedit/interface.c124
-rw-r--r--saedit/interface.ui852
-rw-r--r--saedit/logo.svg (renamed from saedit/icon.svg)33
-rw-r--r--saedit/main.c802
-rw-r--r--saedit/main.h138
-rw-r--r--saedit/sae.c212
-rw-r--r--saedit/sae.h69
-rw-r--r--saedit/search.c74
-rw-r--r--saedit/search.h26
-rw-r--r--saedit/spritedrawingarea/sdalayer.c271
-rw-r--r--saedit/spritedrawingarea/sdalayer.h54
-rw-r--r--saedit/spritedrawingarea/sdalayerprivate.h19
-rw-r--r--saedit/spritedrawingarea/spritedrawingarea.c437
-rw-r--r--saedit/spritedrawingarea/spritedrawingarea.h71
-rw-r--r--saedit/template.xml6
-rw-r--r--saedit/treefolderview/treefolderview.c400
-rw-r--r--saedit/treefolderview/treefolderview.h64
-rw-r--r--saedit/treefolderview/treefolderviewprivate.h45
-rw-r--r--saedit/treefolderview/type.c250
-rwxr-xr-xsaedit/valg2
-rw-r--r--saedit/xml.c189
-rw-r--r--saedit/xml.h68
-rw-r--r--saedit/xmlsetup.c165
-rw-r--r--saedit/xmlsetup.h10
61 files changed, 6390 insertions, 2670 deletions
diff --git a/saedit/.gitignore b/saedit/.gitignore
new file mode 100644
index 0000000..e680e74
--- /dev/null
+++ b/saedit/.gitignore
@@ -0,0 +1,6 @@
+*~
+*.swp
+*.o
+*/*.o
+glade/libsaedit.so
+saedit
diff --git a/saedit/.gitlab-ci.yml b/saedit/.gitlab-ci.yml
new file mode 100644
index 0000000..0807941
--- /dev/null
+++ b/saedit/.gitlab-ci.yml
@@ -0,0 +1,6 @@
+before_script:
+ - apt-get update -qq && apt-get install -y -qq gtk+3.0 gtksourceview-3.0
+
+saedit:
+ script:
+ - make all
diff --git a/saedit/COPYING b/saedit/COPYING
deleted file mode 100644
index e8b00c2..0000000
--- a/saedit/COPYING
+++ /dev/null
@@ -1,340 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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.
diff --git a/saedit/LICENSE b/saedit/LICENSE
new file mode 100644
index 0000000..eb82ba2
--- /dev/null
+++ b/saedit/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. 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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. 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.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ 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.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ 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
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ saedit2
+ Copyright (C) 2018 Danil Sagunov
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ saedit2 Copyright (C) 2018 Danil Sagunov
+ This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU 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 Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/saedit/Makefile b/saedit/Makefile
index 8d033f3..207eeaf 100644
--- a/saedit/Makefile
+++ b/saedit/Makefile
@@ -1,33 +1,33 @@
-ifndef CFLAGS
- CFLAGS = -O2 -pipe
-endif
+CC ?= gcc
-ifndef LDFLAGS
- LDFLAGS =
-endif
+CFLAGS += `pkg-config --cflags gtk+-3.0 gtksourceview-3.0`
+CFLAGS += -fPIC -Itreefolderview -Ispritedrawingarea
+CFLAGS += -Wall -Wdeclaration-after-statement -ansi
+CFLAGS += -Werror -Wextra -Wstrict-prototypes
+CFLAGS += -Wno-unused-parameter
-ifndef OUTPUT
- OUTPUT = saedit
-endif
+LDFLAGS += `pkg-config --libs gtk+-3.0 gtksourceview-3.0`
+LDFLAGS += -rdynamic -Ltreefolderview -Lspritedrawingarea
-LDFLAGS += -export-dynamic
+FLAGS = ${CFLAGS} ${LDFLAGS}
-CFLAGS += `pkg-config --cflags gtk+-3.0 gtksourceview-3.0`
+all: saedit glade/libsaedit.so
-LDFLAGS += `pkg-config --libs gtk+-3.0 gtksourceview-3.0`
+saedit: main.o treefolderview/treefolderview.o xml.o \
+ spritedrawingarea/spritedrawingarea.o \
+ context.o imageset.o action.o animation.o common.o \
+ interactor.o callbacks.o errors.o config.o file.o \
+ buffer.o spritedrawingarea/sdalayer.o xmlsetup.o \
+ drawfuncs.o
+ ${CC} $^ -o saedit ${FLAGS}
+
+glade/libsaedit.so: treefolderview/treefolderview.o \
+ spritedrawingarea/spritedrawingarea.o \
+ spritedrawingarea/sdalayer.o
+ ${CC} $^ -o glade/libsaedit.so ${FLAGS} -shared
+
+%.o: %.c
+ ${CC} $^ -c -o $@ ${CFLAGS}
-all:main.o search.o config.o xml.o sae.o compile clean
-compile: main.o search.o config.o xml.o
- gcc ${CFLAGS} -o '${OUTPUT}' main.o search.o config.o xml.o sae.o ${LDFLAGS}
-main.o: main.c main.h search.h config.h common.h xml.h
- gcc ${CFLAGS}-c -o main.o main.c
-search.o: search.c search.h common.h
- gcc ${CFLAGS} -c -o search.o search.c
-xml.o: xml.c xml.h common.h
- gcc ${CFLAGS} -c -o xml.o xml.c
-config.o: config.c config.h common.h
- gcc ${CFLAGS} -c -o config.o config.c
-sae.o: sae.c sae.h common.h
- gcc ${CFLAGS} -c -o sae.o sae.c
clean:
- rm -rfv *.o *~
+ rm -f *.o */*.o *~ glade/libsaedit.so saedit
diff --git a/saedit/action.c b/saedit/action.c
new file mode 100644
index 0000000..a9b31e2
--- /dev/null
+++ b/saedit/action.c
@@ -0,0 +1,176 @@
+#include "action.h"
+#include "animation.h"
+#include "imageset.h"
+#include "context.h"
+
+Action *
+action_new (
+ const SpriteContext *context,
+ const XMLNode *node,
+ gint included_from
+) {
+ Action *action;
+ GList *list;
+ gchar *imgset_name;
+ gboolean fail = FALSE;
+ Imageset *imageset;
+
+ g_return_val_if_fail (g_strcmp0 (node->name, "action") == 0, NULL);
+
+ action = (Action *) g_new0 (Action, 1);
+
+ action->name = xml_node_get_attr_value (node, "name");
+ if (action->name == NULL) {
+ /* TODO: report error */
+ fail = TRUE;
+ }
+
+ imgset_name = xml_node_get_attr_value (node, "imageset");
+ if (imgset_name == NULL) {
+ /* TODO: report error */
+ fail = TRUE;
+ } else {
+ imageset = sprite_context_get_imageset (context, imgset_name);
+ if (imageset == NULL) {
+ /* TODO: report error */
+ fail = TRUE;
+ }
+ }
+
+ if (fail) {
+ g_free (action);
+ return NULL;
+ }
+
+ action->hp = xml_node_get_int_attr_value (node, "hp", 100);
+
+ list = node->sub_nodes;
+ while (list != NULL) {
+ XMLNode *current = (XMLNode *) list->data;
+
+ if (g_strcmp0 (current->name, "animation") == 0) {
+ action_add_animation (
+ action,
+ imageset,
+ current,
+ included_from
+ );
+ } else {
+ /* TODO: action contains something unknown */
+ }
+
+ list = g_list_next (list);
+ }
+
+ return action;
+}
+
+void
+action_add_animation (
+ Action *action,
+ const Imageset *imageset,
+ const XMLNode *node,
+ gint included_from
+) {
+ Animation *animation;
+
+ g_return_if_fail (g_strcmp0 (node->name, "animation") == 0);
+
+ animation = animation_new (imageset, node, included_from);
+ if (animation == NULL)
+ return;
+
+ if (g_list_find_custom (
+ action->animations, animation,
+ (GCompareFunc) animation_compare_by_direction) != NULL
+ ) {
+ animation_free (animation);
+ return;
+ }
+
+ action->animations = g_list_append (action->animations, animation);
+}
+
+void
+action_free (Action *action) {
+ g_list_free_full (
+ action->animations,
+ (GDestroyNotify) animation_free
+ );
+
+ g_free (action->name);
+ g_free (action);
+}
+
+gint
+action_compare_by_hp_and_name (
+ const Action *first,
+ const Action *second
+) {
+ if (first->hp != second->hp) {
+ return first->hp - second->hp;
+ }
+ return g_strcmp0 (first->name, second->name);
+}
+
+gboolean
+action_hp_and_name_equals (
+ const Action *action,
+ gint hp,
+ const gchar *name
+) {
+ return
+ action->hp == hp &&
+ g_strcmp0 (action->name, name) == 0;
+}
+
+const Animation *
+action_get_animation (
+ const Action *action,
+ const gchar *direction
+) {
+ GList *animation = action->animations;
+ g_return_val_if_fail (animation != NULL, NULL);
+
+ while (direction != NULL && animation != NULL) {
+ Animation *current = (Animation *) animation->data;
+ g_assert (current != NULL);
+ if (animation_direction_equals (current, direction))
+ return current;
+
+ animation = g_list_next (animation);
+ }
+
+ return (Animation *) action->animations->data;
+}
+
+void
+action_get_hp_and_name (
+ const Action *action,
+ gint *hp,
+ gchar **name
+) {
+ if (hp != NULL)
+ *hp = action->hp;
+ if (name != NULL)
+ *name = action->name;
+}
+
+GList *
+action_get_directions (const Action *action) {
+ GList *result = NULL;
+ GList *animations = action->animations;
+
+ while (animations != NULL) {
+ Animation *animation = (Animation *) animations->data;
+ result = g_list_append (result, animation->direction);
+ animations = g_list_next (animations);
+ }
+
+ return result;
+}
+
+gchar *
+get_action_id (gint hp, const gchar *name) {
+ return g_strdup_printf ("%d:%s", hp, name);
+}
diff --git a/saedit/action.h b/saedit/action.h
new file mode 100644
index 0000000..57da155
--- /dev/null
+++ b/saedit/action.h
@@ -0,0 +1,59 @@
+#ifndef ACTION_H
+#define ACTION_H
+
+#include <glib.h>
+#include "common.h"
+#include "imageset.h"
+#include "animation.h"
+#include "xml.h"
+
+typedef struct {
+ gchar *name;
+ gint hp;
+ GList *animations;
+} Action;
+
+void
+action_free (Action *action);
+
+gint
+action_compare_by_hp_and_name (
+ const Action *first,
+ const Action *second
+);
+
+gboolean
+action_hp_and_name_equals (
+ const Action *action,
+ gint hp,
+ const gchar *name
+);
+
+void
+action_add_animation (
+ Action *action,
+ const Imageset *imageset,
+ const XMLNode *node,
+ gint included_from
+);
+
+const Animation *
+action_get_animation (
+ const Action *action,
+ const gchar *direction
+);
+
+void
+action_get_hp_and_name (
+ const Action *action,
+ gint *hp,
+ gchar **name
+);
+
+GList *
+action_get_directions (const Action *action);
+
+gchar *
+get_action_id (gint hp, const gchar *name);
+
+#endif
diff --git a/saedit/animation.c b/saedit/animation.c
new file mode 100644
index 0000000..997b420
--- /dev/null
+++ b/saedit/animation.c
@@ -0,0 +1,386 @@
+#include "animation.h"
+#include <string.h>
+
+static void
+animation_add_element_pause (
+ Animation *animation,
+ gint delay,
+ gint rand,
+ gint line_no
+) {
+ AnimElement *elem = animation_element_new ();
+ elem->delay = delay;
+ elem->rand = rand;
+ elem->type = ELEMENT_PAUSE;
+ elem->line_no = line_no;
+
+ animation->elements = g_list_append (animation->elements, elem);
+}
+
+static void
+animation_add_element_end (
+ Animation *animation,
+ gint rand,
+ gint line_no
+) {
+ AnimElement *elem = animation_element_new ();
+ elem->type = ELEMENT_END;
+ elem->rand = rand;
+ elem->line_no = line_no;
+
+ animation->elements = g_list_append (animation->elements, elem);
+}
+
+static void
+animation_add_element_frame (
+ Animation *animation,
+ const Imageset *imageset,
+ gint index,
+ gint offsetX,
+ gint offsetY,
+ gint delay,
+ gint rand,
+ gint line_no
+) {
+ gint x, y;
+ AnimElement *elem = animation_element_new ();
+
+ elem->type = ELEMENT_FRAME;
+
+ imageset_get_offset (imageset, &x, &y);
+ offsetX += x;
+ offsetY += y;
+
+ imageset_get_size (imageset, &x, &y);
+ offsetX -= x / 2;
+ offsetY -= y;
+
+ elem->offsetX = offsetX;
+ elem->offsetY = offsetY;
+
+ elem->delay = delay;
+ elem->rand = rand;
+ elem->line_no = line_no;
+
+ elem->sprite = imageset_get_sprite_by_index (imageset, index);
+ if (elem->sprite == NULL) {
+ /* TODO: report this */
+ g_free (elem);
+ return;
+ }
+
+ animation->elements = g_list_append (animation->elements, elem);
+}
+
+static void
+animation_add_element_goto (
+ Animation *animation,
+ gchar *label,
+ gint rand,
+ gint line_no
+) {
+ AnimElement *elem = animation_element_new ();
+ elem->type = ELEMENT_GOTO;
+ elem->str = label;
+ elem->rand = rand;
+ elem->line_no = line_no;
+
+ animation->elements = g_list_append (animation->elements, elem);
+}
+
+static void
+animation_add_element_jump (
+ Animation *animation,
+ gchar *action,
+ gint rand,
+ gint line_no
+) {
+ AnimElement *elem = animation_element_new ();
+ elem->type = ELEMENT_JUMP;
+ elem->str = action;
+ elem->rand = rand;
+ elem->line_no = line_no;
+
+ animation->elements = g_list_append (animation->elements, elem);
+}
+
+static void
+animation_add_element_label (
+ Animation *animation,
+ gchar *name,
+ gint line_no
+) {
+ AnimElement *elem = animation_element_new ();
+ elem->type = ELEMENT_LABEL;
+ elem->str = name;
+ elem->line_no = line_no;
+
+ animation->elements = g_list_append (animation->elements, elem);
+}
+
+static void
+animation_add_sequence (
+ Animation *animation,
+ const Imageset *imageset,
+ const XMLNode *node,
+ gint offsetX,
+ gint offsetY,
+ gint delay,
+ gint rand,
+ gint line_no
+) {
+ gchar *value, **tokens;
+ gint start = xml_node_get_int_attr_value (node, "start", -1);
+ gint end = xml_node_get_int_attr_value (node, "end", -1);
+ gint repeat = xml_node_get_int_attr_value_limited (
+ node, "repeat", 1, 0, 100
+ );
+
+ g_return_if_fail (g_strcmp0 (node->name, "sequence") == 0);
+
+ if (repeat < 1) {
+ /* TODO: show error */
+ return;
+ }
+
+ value = xml_node_get_attr_value (node, "value");
+
+ if (value == NULL) {
+ if (start < 0 || end < 0) {
+ /* TODO: show error */
+ return;
+ }
+ value = g_strdup_printf ("%d-%d", start, end);
+ }
+
+ tokens = g_strsplit (value, ",", 0);
+ g_free (value);
+
+ while (repeat > 0) {
+ gchar **token = tokens;
+
+ while (*token != NULL) {
+ if (g_strcmp0 (*token, "p") == 0) {
+ animation_add_element_pause (
+ animation,
+ delay,
+ rand,
+ line_no
+ );
+ } else {
+ gint start = -1, end = -1, d;
+ gchar *delim = strchr (*token, '-');
+
+ if (delim == NULL) {
+ try_strtoint (*token, &start);
+ end = start;
+ } else {
+ *delim = 0;
+ try_strtoint ( *token, &start);
+ try_strtoint (delim + 1, &end);
+ *delim = '-';
+ }
+
+ if (start < 0 || end < 0) {
+ /* TODO: show error */
+ g_strfreev (tokens);
+ return;
+ }
+
+ d = start <= end ? +1 : -1;
+ end += d;
+
+ while (start != end) {
+ animation_add_element_frame (
+ animation,
+ imageset,
+ start,
+ offsetX,
+ offsetY,
+ delay,
+ rand,
+ line_no
+ );
+ start += d;
+ }
+ }
+ ++token;
+ }
+ --repeat;
+ }
+
+ g_strfreev (tokens);
+}
+
+static void
+animation_add_elements_from_node (
+ Animation *animation,
+ const Imageset *imageset,
+ const XMLNode *node,
+ gint included_from
+) {
+ gchar *name = node->name;
+
+ gint offsetX, offsetY;
+ gint delay, rand;
+ gchar *str;
+ gint line_no;
+
+ imageset_get_offset (imageset, &offsetX, &offsetY);
+
+ offsetX += xml_node_get_int_attr_value (node, "offsetX", 0);
+ offsetY += xml_node_get_int_attr_value (node, "offsetY", 0);
+
+ delay = xml_node_get_int_attr_value_limited (
+ node, "delay", 0,
+ 0, 100000
+ );
+
+ rand = xml_node_get_int_attr_value_limited (
+ node, "rand", 100,
+ 0, 100
+ );
+
+ line_no = included_from;
+ if (line_no == -1)
+ line_no = node->line_no;
+
+ if (g_strcmp0 (name, "frame") == 0) {
+ gint index = xml_node_get_int_attr_value (node, "index", -1);
+
+ if (index < 0) {
+ /* TODO: report error */
+ return;
+ }
+
+ animation_add_element_frame (
+ animation,
+ imageset,
+ index,
+ offsetX, offsetY,
+ delay, rand,
+ line_no
+ );
+ } else
+
+ if (g_strcmp0 (name, "pause") == 0) {
+ animation_add_element_pause (
+ animation, delay, rand, line_no
+ );
+ } else
+
+ if (g_strcmp0 (name, "end") == 0) {
+ animation_add_element_end (animation, rand, line_no);
+ } else
+
+ if (g_strcmp0 (name, "jump") == 0) {
+ str = xml_node_get_attr_value (node, "action");
+ animation_add_element_jump (
+ animation, str, rand, line_no
+ );
+ } else
+
+ if (g_strcmp0 (name, "label") == 0) {
+ str = xml_node_get_attr_value (node, "name");
+ if (str == NULL) {
+ /* TODO: report error */
+ return;
+ }
+
+ animation_add_element_label (animation, str, line_no);
+ } else
+
+ if (g_strcmp0 (name, "goto") == 0) {
+ str = xml_node_get_attr_value (node, "label");
+ if (str == NULL) {
+ /* TODO: report error */
+ return;
+ }
+
+ animation_add_element_goto (
+ animation, str, rand, line_no
+ );
+ } else
+
+ if (g_strcmp0 (name, "sequence") == 0) {
+ animation_add_sequence (
+ animation,
+ imageset,
+ node,
+ offsetX, offsetY,
+ delay, rand,
+ line_no
+ );
+ } else
+
+ {
+ /* TODO: unknown tag */
+ return;
+ }
+}
+
+Animation *
+animation_new (
+ const Imageset *imageset,
+ const XMLNode *node,
+ gint included_from
+) {
+ Animation *animation;
+ gchar *dir;
+ GList *sub_node;
+
+ g_return_val_if_fail (g_strcmp0 (node->name, "animation") == 0, NULL);
+
+ dir = xml_node_get_attr_value (node, "direction");
+
+ if (dir == NULL)
+ dir = "";
+
+ animation = (Animation *) g_new0 (Animation, 1);
+ animation->direction = dir;
+
+ sub_node = node->sub_nodes;
+ while (sub_node != NULL) {
+ animation_add_elements_from_node (
+ animation,
+ imageset,
+ (XMLNode *) sub_node->data,
+ included_from
+ );
+ sub_node = g_list_next (sub_node);
+ }
+
+ return animation;
+}
+
+gint
+animation_compare_by_direction (
+ const Animation *first,
+ const Animation *second
+) {
+ return g_strcmp0 (first->direction, second->direction);
+}
+
+gboolean
+animation_direction_equals (
+ const Animation *animation,
+ const gchar *direction
+) {
+ return g_strcmp0 (animation->direction, direction) == 0;
+}
+
+void
+animation_free (Animation *animation) {
+ g_list_free_full (
+ animation->elements,
+ (GDestroyNotify) g_free
+ );
+
+ g_free (animation->direction);
+ g_free (animation);
+}
+
+AnimElement *
+animation_element_new () {
+ return (AnimElement *) g_new0 (AnimElement, 1);
+}
diff --git a/saedit/animation.h b/saedit/animation.h
new file mode 100644
index 0000000..6c4d05f
--- /dev/null
+++ b/saedit/animation.h
@@ -0,0 +1,59 @@
+#ifndef ANIMATION_H
+#define ANIMATION_H
+
+#include <gdk/gdk.h>
+#include <glib.h>
+#include "common.h"
+#include "xml.h"
+#include "imageset.h"
+
+typedef struct {
+ gchar *direction;
+ GList *elements;
+} Animation;
+
+Animation *
+animation_new (
+ const Imageset *imageset,
+ const XMLNode *node,
+ gint included_from
+);
+
+void
+animation_free (Animation *animation);
+
+gint
+animation_compare_by_direction (
+ const Animation *first,
+ const Animation *second
+);
+
+gboolean
+animation_direction_equals (
+ const Animation *animation,
+ const gchar *direction
+);
+
+enum {
+ ELEMENT_END = 0,
+ ELEMENT_FRAME,
+ ELEMENT_JUMP,
+ ELEMENT_GOTO,
+ ELEMENT_PAUSE,
+ ELEMENT_LABEL,
+ ELEMENT_COUNT
+};
+
+typedef struct {
+ gint type;
+ gint delay;
+ gint offsetX, offsetY;
+ gint rand;
+ gchar *str;
+ gint line_no;
+ GdkPixbuf *sprite;
+} AnimElement;
+
+AnimElement *animation_element_new (void);
+
+#endif
diff --git a/saedit/bmake b/saedit/bmake
deleted file mode 100755
index a1ecd7f..0000000
--- a/saedit/bmake
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/sh
-
-dir=`pwd`
-
-export LANG=C
-
-make clean
-
-#export CFLAGS="-std=c99 -Wvariadic-macros -Wvla -Wredundant-decls
-
-export CFLAGS="-std=gnu99 -Wvariadic-macros -Wvla -Wredundant-decls \
--Wpacked-bitfield-compat -Wtrampolines \
--Wsuggest-attribute=noreturn -Wstrict-aliasing=2 \
--fstrict-aliasing -Wunreachable-code -Wabi -Wdisabled-optimization \
--Wvolatile-register-var -Winvalid-pch -Wredundant-decls \
--Wnormalized=nfkc -Wmissing-format-attribute -Wmissing-noreturn \
--Wswitch-default -Waddress \
--Wlogical-op -Wcast-align -Wpointer-arith -Wundef \
--Wmissing-include-dirs -Winit-self -pedantic -Wall -ggdb3 -O2 -pipe \
--Wpacked -Wstrict-overflow=1 -Wunknown-pragmas -Wwrite-strings \
--Wstack-protector -Wshadow -Wunused-macros \
--Wbuiltin-macro-redefined -Wdeprecated \
--Wendif-labels -Wformat=1 -Wimport -Wpsabi \
--Wmissing-field-initializers -Wuninitialized \
--Wignored-qualifiers -Winit-self -Wempty-body -Wclobbered -Wtype-limits \
--Wsign-compare -Wwrite-strings"
-
-make 2>make.log
diff --git a/saedit/buffer.c b/saedit/buffer.c
new file mode 100644
index 0000000..5c51bcc
--- /dev/null
+++ b/saedit/buffer.c
@@ -0,0 +1,87 @@
+#include <gtksourceview/gtksource.h>
+#include "main.h"
+#include "buffer.h"
+
+gchar *
+buffer_get_text () {
+ GtkTextIter start, end;
+ GtkTextBuffer *buffer =
+ GTK_TEXT_BUFFER (gtk_text_view_get_buffer (
+ GTK_TEXT_VIEW (source_view)
+ ));
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ return gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
+}
+
+void
+buffer_set_text (
+ const gchar *text,
+ const guint len
+) {
+ GtkTextBuffer *buffer =
+ GTK_TEXT_BUFFER (gtk_text_view_get_buffer (
+ GTK_TEXT_VIEW (source_view)
+ ));
+
+ gtk_text_buffer_set_text (buffer, text, len);
+}
+
+void
+buffer_mark_line (gint line_no) {
+ GtkTextIter iter, end;
+ GtkSourceBuffer *buffer = GTK_SOURCE_BUFFER (
+ gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view))
+ );
+
+ gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter);
+ gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end);
+
+ gtk_source_buffer_remove_source_marks (
+ buffer,
+ &iter,
+ &end,
+ "active-line"
+ );
+
+ if (line_no == -1)
+ return;
+
+ gtk_text_iter_set_line (&iter, line_no);
+ gtk_text_view_scroll_to_mark (
+ GTK_TEXT_VIEW (source_view),
+ (GtkTextMark *) gtk_source_buffer_create_source_mark (
+ buffer,
+ NULL,
+ "active-line",
+ &iter
+ ),
+ 0.0,
+ TRUE,
+ 0.0,
+ 0.5
+ );
+}
+
+
+void
+buffer_set_modified (gboolean modified) {
+ GtkTextBuffer *buffer =
+ GTK_TEXT_BUFFER (gtk_text_view_get_buffer (
+ GTK_TEXT_VIEW (source_view)
+ ));
+
+ gtk_text_buffer_set_modified (buffer, modified);
+}
+
+gboolean
+buffer_get_modified () {
+ GtkTextBuffer *buffer =
+ GTK_TEXT_BUFFER (gtk_text_view_get_buffer (
+ GTK_TEXT_VIEW (source_view)
+ ));
+
+ return gtk_text_buffer_get_modified (buffer);
+}
diff --git a/saedit/buffer.h b/saedit/buffer.h
new file mode 100644
index 0000000..2926687
--- /dev/null
+++ b/saedit/buffer.h
@@ -0,0 +1,22 @@
+#ifndef _BUFFER_H_
+#define _BUFFER_H_
+
+gchar *
+buffer_get_text (void);
+
+void
+buffer_set_text (
+ const gchar *text,
+ const guint len
+);
+
+void
+buffer_mark_line (gint line_no);
+
+void
+buffer_set_modified (gboolean modified);
+
+gboolean
+buffer_get_modified (void);
+
+#endif
diff --git a/saedit/callbacks.c b/saedit/callbacks.c
new file mode 100644
index 0000000..f27fc47
--- /dev/null
+++ b/saedit/callbacks.c
@@ -0,0 +1,432 @@
+#include "main.h"
+#include "errors.h"
+#include "config.h"
+#include "file.h"
+#include "buffer.h"
+#include <string.h>
+
+void
+store_append_action (const Action *action, gpointer user_data) {
+ int hp;
+ gchar *name;
+ GtkTreeIter iter;
+
+ action_get_hp_and_name (action, &hp, &name);
+ gtk_list_store_append (store_actions, &iter);
+
+ gtk_list_store_set (
+ store_actions,
+ &iter,
+ 0, name,
+ 1, hp,
+ 2, get_action_id (hp, name),
+ -1
+ );
+}
+
+void
+parse_buffer_clicked_cb (
+ GtkToolButton *button,
+ gpointer user_data
+) {
+ gchar *buffer_text;
+ GError *error = NULL;
+ GList *actions;
+
+ release_context ();
+
+ buffer_text = buffer_get_text ();
+
+ context = sprite_context_new (
+ config_keys_get_data_folder_path ()
+ );
+ interactor = interactor_new (context);
+
+ sprite_context_add_sprite (
+ context,
+ xml_parse_buffer (buffer_text, &error),
+ -1
+ );
+
+ if (error != NULL) {
+ post_error ("XML parser", error->message);
+ g_error_free (error);
+ }
+
+ g_free (buffer_text);
+
+ interactor_set_updated_callback (interactor, intr_updated);
+
+ actions = sprite_context_get_actions (context);
+ g_list_foreach (actions, (GFunc) store_append_action, NULL);
+ g_list_free (actions);
+
+ gtk_widget_queue_draw (d_area);
+}
+
+void
+append_direction (const gchar *direction, gpointer user_data) {
+ gchar *display;
+
+ g_return_if_fail (direction != NULL);
+
+ if (strlen (direction) == 0)
+ display = g_strdup ("<empty>");
+ else
+ display = g_strdup (direction);
+
+ gtk_combo_box_text_append (
+ cb_directions,
+ direction,
+ display
+ );
+
+ g_free (display);
+}
+
+void
+action_changed_cb (GtkComboBox *cbox, gpointer user_data) {
+ GList *directions;
+ GtkTreeIter iter;
+ gint hp;
+ gchar *name;
+ const Action *action;
+ gboolean w_run;
+
+ g_return_if_fail (cbox == cb_actions);
+
+ if (interactor == NULL)
+ return;
+
+ if (!gtk_combo_box_get_active_iter (cbox, &iter))
+ return;
+
+ gtk_tree_model_get (
+ GTK_TREE_MODEL (store_actions),
+ &iter,
+ 0, &name,
+ 1, &hp,
+ -1
+ );
+
+ w_run = interactor_loop_running (interactor);
+ if (w_run)
+ interactor_loop_stop (interactor);
+
+ gtk_combo_box_text_remove_all (cb_directions);
+
+ action = sprite_context_get_action (context, hp, name);
+ g_return_if_fail (action != NULL);
+
+ directions = action_get_directions (action);
+ g_list_foreach (directions, (GFunc) append_direction, NULL);
+ g_list_free (directions);
+
+ interactor_set_action (interactor, hp, name);
+
+ if (w_run)
+ interactor_loop_start (interactor, 10, 10);
+}
+
+void
+direction_changed_cb (GtkComboBox *cbox, gpointer user_data) {
+ gchar *active_text;
+
+ g_return_if_fail (cbox == GTK_COMBO_BOX (cb_directions));
+
+ if (interactor == NULL)
+ return;
+
+ active_text = gtk_combo_box_text_get_active_text (cb_directions);
+
+ if (active_text == NULL)
+ return;
+
+ interactor_set_direction (
+ interactor,
+ active_text
+ );
+}
+
+void
+play_pause_clicked_cb (
+ GtkToolButton *button,
+ gpointer user_data
+) {
+ if (interactor == NULL)
+ return;
+
+ if (interactor_loop_running (interactor))
+ interactor_loop_stop (interactor);
+ else
+ interactor_loop_start (interactor, 10, 10);
+}
+
+void
+next_frame_clicked_cb (
+ GtkToolButton *button,
+ gpointer user_data
+) {
+ if (interactor == NULL)
+ return;
+
+ interactor_skip_current_frame (interactor);
+}
+
+void
+first_frame_clicked_cb (
+ GtkToolButton *button,
+ gpointer user_data
+) {
+ if (interactor == NULL)
+ return;
+
+ interactor_loop_stop (interactor);
+ interactor_reset_animation (interactor);
+}
+
+void
+choose_df_activated_cb (
+ GtkMenuItem *item,
+ gpointer user_data
+) {
+ TreeFolderView *tfview;
+ GtkWidget *dialog;
+ gint res;
+ gchar *filename;
+
+ g_return_if_fail (IS_TREE_FOLDER_VIEW (user_data));
+
+ tfview = TREE_FOLDER_VIEW (user_data);
+ dialog = gtk_file_chooser_dialog_new (
+ "Choose data folder",
+ GTK_WINDOW (main_window),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ "Cancel", GTK_RESPONSE_CANCEL,
+ "Open", GTK_RESPONSE_ACCEPT,
+ NULL
+ );
+
+ gtk_file_chooser_set_filename (
+ GTK_FILE_CHOOSER (dialog),
+ tree_folder_view_get_filename (tfview)
+ );
+
+ res = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (res == GTK_RESPONSE_ACCEPT) {
+ filename = gtk_file_chooser_get_filename (
+ GTK_FILE_CHOOSER (dialog)
+ );
+
+ config_keys_set_data_folder_path (filename);
+ config_keys_save ();
+
+ tree_folder_view_set_filename (
+ tfview,
+ filename
+ );
+
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+void
+open_file_activated_cb (
+ GtkMenuItem *item,
+ gpointer user_data
+) {
+ GtkWidget *dialog;
+ GtkFileChooser *chooser;
+ gint res;
+ gchar *data_folder;
+
+ dialog = gtk_file_chooser_dialog_new (
+ NULL,
+ GTK_WINDOW (main_window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ "_Cancel", GTK_RESPONSE_CANCEL,
+ "_Open", GTK_RESPONSE_ACCEPT,
+ NULL
+ );
+
+ data_folder = config_keys_get_data_folder_path ();
+
+ chooser = GTK_FILE_CHOOSER (dialog);
+ gtk_file_chooser_set_current_folder (
+ chooser,
+ data_folder
+ );
+
+ res = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (res == GTK_RESPONSE_ACCEPT) {
+ gchar *filename = gtk_file_chooser_get_filename (chooser);
+ open_file (filename);
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+ g_free (data_folder);
+}
+
+void save_file_as_activated_cb (
+ GtkMenuItem *item,
+ gpointer user_data
+) {
+ GtkWidget *dialog;
+ GtkFileChooser *chooser;
+ gint res;
+ gchar *data_folder;
+
+ dialog = gtk_file_chooser_dialog_new (
+ NULL,
+ GTK_WINDOW (main_window),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ "_Cancel", GTK_RESPONSE_CANCEL,
+ "_Save", GTK_RESPONSE_ACCEPT,
+ NULL
+ );
+
+ data_folder = config_keys_get_data_folder_path ();
+
+ chooser = GTK_FILE_CHOOSER (dialog);
+ gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
+
+ if (get_opened_file_name () != NULL) {
+ gtk_file_chooser_set_current_name (
+ chooser,
+ get_opened_file_name ()
+ );
+ } else {
+ gtk_file_chooser_set_current_folder (
+ chooser,
+ data_folder
+ );
+ }
+
+ res = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (res == GTK_RESPONSE_ACCEPT) {
+ gchar *filename = gtk_file_chooser_get_filename (chooser);
+ save_file (filename);
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+ g_free (data_folder);
+}
+
+void
+save_file_activated_cb (
+ GtkMenuItem *item,
+ gpointer user_data
+) {
+ const gchar *filename;
+
+ filename = get_opened_file_name ();
+
+ if (filename == NULL) {
+ save_file_as_activated_cb (item, user_data);
+ return;
+ }
+
+ save_file (NULL);
+}
+
+void
+zoom_adjustment_value_changed_cb (
+ GtkAdjustment *adjustment,
+ gpointer user_data
+) {
+ sprite_drawing_area_set_scale_factor (
+ SPRITE_DRAWING_AREA (user_data),
+ gtk_adjustment_get_value (adjustment)
+ );
+ gtk_widget_queue_draw (GTK_WIDGET (user_data));
+}
+
+void
+tfview_file_activated_cb (
+ TreeFolderView *tfview,
+ gchar *filename,
+ gpointer user_data
+) {
+ open_file (filename);
+}
+
+void
+sourceview_buffer_modified_changed_cb (
+ GtkTextBuffer *textbuffer,
+ gpointer user_data
+) {
+ update_window_title ();
+}
+
+void
+new_file_activated_cb (
+ GtkMenuItem *item,
+ gpointer user_data
+) {
+ open_file (NULL);
+}
+
+void
+view_reset_to_center_activate_cb (
+ GtkMenuItem *item,
+ gpointer user_data
+) {
+ sprite_drawing_area_set_center (
+ SPRITE_DRAWING_AREA (d_area),
+ 0, 0
+ );
+
+ gtk_widget_queue_draw (d_area);
+}
+
+void
+show_tile_grid_toggled_cb (
+ GtkCheckMenuItem *item,
+ gpointer user_data
+) {
+ sda_layer_set_visible (
+ tile_grid_layer,
+ gtk_check_menu_item_get_active (item)
+ );
+
+ gtk_widget_queue_draw (d_area);
+}
+
+void
+show_pixel_grid_toggled_cb (
+ GtkCheckMenuItem *item,
+ gpointer user_data
+) {
+ sda_layer_set_visible (
+ pixel_grid_layer,
+ gtk_check_menu_item_get_active (item)
+ );
+
+ gtk_widget_queue_draw (d_area);
+}
+
+void
+menuitem_about_activate_cb (
+ GtkMenuItem *item,
+ gpointer user_data
+) {
+ gtk_dialog_run (GTK_DIALOG (user_data));
+}
+
+void
+about_dialog_response_cb (
+ GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data
+) {
+ if (response_id == GTK_RESPONSE_DELETE_EVENT) {
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ }
+}
diff --git a/saedit/callbacks.h b/saedit/callbacks.h
new file mode 100644
index 0000000..7107353
--- /dev/null
+++ b/saedit/callbacks.h
@@ -0,0 +1,96 @@
+#ifndef _CALLBACKS_H_
+#define _CALLBACKS_H_
+
+void
+parse_buffer_clicked_cb (
+ GtkToolButton *button,
+ gpointer user_data
+);
+
+void
+action_changed_cb (GtkComboBox *cbox, gpointer user_data);
+
+void
+direction_changed_cb (GtkComboBox *cbox, gpointer user_data) {
+
+void
+play_pause_clicked_cb (
+ GtkToolButton *button,
+ gpointer user_data
+);
+
+void
+first_frame_clicked_cb (
+ GtkToolButton *button,
+ gpointer user_data
+);
+
+void
+choose_df_activated_cb (
+ GtkMenuItem *item,
+ gpointer user_data
+);
+
+void
+open_file_activated_cb (
+ GtkMenuIterm *item,
+ gpointer user_data
+);
+
+void
+save_file_activated_cb (
+ GtkMenuIterm *item,
+ gpointer user_data
+);
+
+void
+zoom_adjustment_value_changed_cb (
+ GtkAdjustment *adjustment,
+ gpointer user_data
+);
+
+void
+tfview_file_activated_cb (
+ TreeFolderView *tfview,
+ gchar *filename,
+ gpointer user_data
+);
+
+void
+new_file_activated_cb (
+ GtkMenuItem *item,
+ gpointer user_data
+);
+
+void
+save_file_as_activated_cb (
+ GtkMenuItem *item,
+ gpointer user_data
+);
+
+void
+show_tile_grid_toggled_cb (
+ GtkCheckMenuItem *item,
+ gpointer user_data
+);
+
+void
+show_pixel_grid_toggled_cb (
+ GtkCheckMenuItem *item,
+ gpointer user_data
+);
+
+void
+menuitem_about_activate_cb (
+ GtkMenuItem *item,
+ gpointer user_data
+);
+
+void
+about_dialog_response_cb (
+ GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data
+);
+
+#endif
diff --git a/saedit/common.c b/saedit/common.c
new file mode 100644
index 0000000..9bb8a30
--- /dev/null
+++ b/saedit/common.c
@@ -0,0 +1,22 @@
+#include "common.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+gboolean
+try_strtoint (
+ const gchar *str,
+ gint *result
+) {
+ gchar *endptr;
+ gint retval;
+
+ errno = 0;
+ retval = strtol (str, &endptr, 10);
+
+ if (*endptr != 0 || errno != 0)
+ return FALSE;
+
+ *result = retval;
+ return TRUE;
+}
diff --git a/saedit/common.h b/saedit/common.h
index 21e6526..1715e41 100644
--- a/saedit/common.h
+++ b/saedit/common.h
@@ -1,24 +1,12 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
-
#ifndef COMMON_H
#define COMMON_H
-#include <glib/gi18n.h>
+#include <glib.h>
-#define POSTFIX_FOLDER "..."
-#define SEPARATOR_SLASH "/"
-#define GRID_SIZE 32
-#define SPRITE_WIDTH_DEFAULT 64
-#define SPRITE_HEIGHT_DEFAULT 64
-#define FILE_TEMPLATE "template.xml"
+gboolean
+try_strtoint (
+ const gchar *str,
+ gint *result
+);
#endif
diff --git a/saedit/config.c b/saedit/config.c
index 67bb63d..1e20411 100644
--- a/saedit/config.c
+++ b/saedit/config.c
@@ -1,82 +1,191 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
-
#include "config.h"
+#include "xml.h"
+#include <unistd.h>
+#include <sys/stat.h>
+
+static GKeyFile *_config_keyfile = NULL;
+
+gchar *
+_config_get_config_folder (void) {
+ return g_strjoin (
+ "/",
+ g_get_user_config_dir (),
+ "saedit2",
+ NULL
+ );
+}
+
+gchar *
+_config_get_key_file_path (void) {
+ gchar *dir = _config_get_config_folder ();
+ gchar *result;
+
+ result = g_strjoin (
+ "/",
+ dir,
+ "config.ini",
+ NULL
+ );
-Options *config_options_new() {
- return g_new0(Options, 1);
+ g_free (dir);
+ return result;
}
-void config_options_load_from_file(Options *options,
- gchar *file,
- gchar *data_folder) {
- options->sprites = NULL;
+void
+config_keys_load () {
+ gchar *filename;
+
+ _config_keyfile = g_key_file_new ();
+
+ filename = _config_get_key_file_path ();
+
+ g_key_file_load_from_file (
+ _config_keyfile,
+ filename,
+ 0,
+ NULL
+ );
+
+ g_free (filename);
+}
+
+gchar *
+config_keys_get_data_folder_path () {
+ gchar *result;
+
+ if (_config_keyfile == NULL)
+ config_keys_load ();
+
+ result = g_key_file_get_value (
+ _config_keyfile,
+ "Default",
+ "Data Folder",
+ NULL
+ );
+
+ if (result == NULL)
+ result = g_strdup ("");
+
+ return result;
+}
+
+void
+config_keys_set_data_folder_path (const gchar *filename) {
+ if (_config_keyfile == NULL)
+ config_keys_load ();
+
+ g_key_file_set_value (
+ _config_keyfile,
+ "Default",
+ "Data Folder",
+ filename
+ );
+}
+
+gint
+config_keys_get_tile_size () {
+ /* TODO */
+ return 32;
+}
- XMLNode *node = xml_parse_file(file);
+void
+config_keys_save () {
+ gchar *filename;
+ if (_config_keyfile == NULL)
+ return;
+
+ filename = _config_get_config_folder ();
+ mkdir (filename, S_IRWXU);
+ g_free (filename);
+
+ filename = _config_get_key_file_path ();
+
+ g_key_file_save_to_file (
+ _config_keyfile,
+ filename,
+ NULL
+ );
+
+ g_free (filename);
+}
+
+static XMLNode *_config_paths_xml_root = NULL;
+
+void
+config_data_paths_load () {
+ gchar *filename;
+ gchar *df_path;
+
+ df_path = config_keys_get_data_folder_path ();
+
+ filename = g_strjoin (
+ "/",
+ df_path,
+ "paths.xml",
+ NULL
+ );
+
+ _config_paths_xml_root = xml_parse_file (filename);
+
+ g_free (df_path);
+ g_free (filename);
+}
+
+gchar *
+config_data_paths_get_sprites_path () {
+ XMLNode *node;
+ GList *list;
+ gchar *name;
+
+ if (_config_paths_xml_root == NULL)
+ config_data_paths_load ();
+
+ node = _config_paths_xml_root;
+
if (node != NULL) {
- GList *list = node->sub_nodes;
+ list = node->sub_nodes;
while (TRUE) {
- list = g_list_find_custom(list, "option", xml_node_compare_with_name_func);
+ list = g_list_find_custom (
+ list,
+ "option",
+ xml_node_compare_with_name_func
+ );
+
if (list == NULL)
break;
- gchar *name_attr = xml_node_get_attr_value(list->data, "name");
- if (name_attr != NULL) {
- if (g_strcmp0(name_attr, "sprites") == 0)
- options->sprites = xml_node_get_attr_value(list->data, "value");
+
+ name = xml_node_get_attr_value (list->data, "name");
+ if (name != NULL && g_strcmp0 (name, "sprites") == 0) {
+ g_free (name);
+ return xml_node_get_attr_value (list->data, "value");
}
+
+ g_free(name);
list = list->next;
}
}
- if (options->sprites == NULL) options->sprites = (gchar *)OPTION_SPRITES_DEFAULT;
- options->sprites = g_strjoin(SEPARATOR_SLASH, data_folder, options->sprites, NULL);
+ return g_strdup ("");
}
-Keys *config_keys_new() {
- Keys *keys = g_new0(Keys, 1);
- keys->clientdata_folder = (gchar *)KEY_CLIENTDATA_FOLDER_DEFAULT;
- keys->show_grid = KEY_SHOW_GRID_DEFAULT;
- return keys;
-}
+gchar *
+config_data_path_get_full_sprite_path (const gchar *rel_path) {
+ gchar *data_folder, *sprites_path, *filename;
-void config_keys_save(Keys *keys) {
- GKeyFile *key_file = g_key_file_new();
- g_key_file_set_value(key_file, "General", "ClientdataFolder",
- g_strjoin(SEPARATOR_SLASH,
- keys->clientdata_folder,
- POSTFIX_FOLDER,
- NULL));
- g_key_file_set_boolean(key_file, "General", "ShowGrid", keys->show_grid);
-
- mkdir(KEYS_CONFIG_DIR, S_IRWXU);
- int fd = g_creat(KEYS_CONFIG_FILE, S_IREAD | S_IWRITE);
- gchar *buf = g_key_file_to_data(key_file, NULL, NULL);
- write(fd, buf, strlen(buf));
- close(fd);
-
- g_key_file_free(key_file);
-}
+ data_folder = config_keys_get_data_folder_path ();
+ sprites_path = config_data_paths_get_sprites_path ();
-void config_keys_load(Keys *keys) {
- GKeyFile *key_file = g_key_file_new();
+ filename = g_strjoin (
+ "/",
+ data_folder,
+ sprites_path,
+ rel_path,
+ NULL
+ );
- g_key_file_load_from_file(key_file,
- KEYS_CONFIG_FILE,
- 0,
- NULL);
- if (g_key_file_has_key(key_file, "General", "ClientdataFolder", NULL))
- keys->clientdata_folder = g_key_file_get_value(key_file, "General", "ClientdataFolder", NULL);
- if (g_key_file_has_key(key_file, "General", "ShowGrid", NULL))
- keys->show_grid = g_key_file_get_boolean(key_file, "General", "ShowGrid", NULL);
+ g_free (sprites_path);
+ g_free (data_folder);
- g_key_file_free(key_file);
+ return filename;
}
diff --git a/saedit/config.h b/saedit/config.h
index ca43eed..cae98bd 100644
--- a/saedit/config.h
+++ b/saedit/config.h
@@ -1,51 +1,32 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
-
-#ifndef CONFIG_H
-#define CONFIG_H
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
#include <glib.h>
-#include <glib/gstdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <string.h>
-#include <unistd.h>
-#include "common.h"
-#include "xml.h"
-
-#define OPTION_SPRITES_DEFAULT "graphics/sprites/"
-
-#define KEY_SHOW_GRID_DEFAULT TRUE
-#define KEY_CLIENTDATA_FOLDER_DEFAULT ""
-#define KEYS_CONFIG_DIR g_strjoin(SEPARATOR_SLASH, g_get_user_config_dir(), "saedit", NULL)
-#define KEYS_CONFIG_FILE g_strjoin(SEPARATOR_SLASH, KEYS_CONFIG_DIR, "config.ini", NULL)
-
-typedef struct {
- gchar *sprites;
-} Options;
-
-Options *config_options_new();
-void config_options_load_from_file(Options *options,
- gchar *file,
- gchar *data_folder);
-
-typedef struct {
- gchar *clientdata_folder;
- gboolean show_grid;
-} Keys;
-
-Keys *config_keys_new();
-void config_keys_load(Keys *keys);
-void config_keys_save(Keys *keys);
+
+void
+config_keys_load (void);
+
+void
+config_keys_save (void);
+
+gchar *
+config_keys_get_data_folder_path (void);
+
+void
+config_keys_set_data_folder_path (
+ const gchar *filename
+);
+
+gint
+config_keys_get_tile_size (void);
+
+void
+config_data_paths_load (void);
+
+gchar *
+config_data_paths_get_sprites_path (void);
+
+gchar *
+config_data_path_get_full_sprite_path (const gchar *rel_path);
#endif
diff --git a/saedit/context.c b/saedit/context.c
new file mode 100644
index 0000000..2a14de5
--- /dev/null
+++ b/saedit/context.c
@@ -0,0 +1,227 @@
+#include <gtk/gtk.h>
+#include "context.h"
+#include "imageset.h"
+#include "action.h"
+#include "animation.h"
+#include "xml.h"
+#include "config.h"
+#include "xmlsetup.h"
+
+struct _SpriteContext {
+ gchar *cdf_filename;
+ GList *imagesets;
+ GList *actions;
+ GList *includes;
+};
+
+SpriteContext *
+sprite_context_new (
+ const gchar *cdf_filename
+) {
+ SpriteContext *context = (SpriteContext *) g_new0 (SpriteContext, 1);
+ context->cdf_filename = g_strdup (cdf_filename);
+ return context;
+}
+
+void
+sprite_context_add_imageset (
+ SpriteContext *context,
+ XMLNode *node
+) {
+ Imageset *imgset;
+
+ g_return_if_fail (g_strcmp0 (node->name, "imageset") == 0);
+
+ imgset = imageset_new (node, context->cdf_filename);
+ if (imgset == NULL)
+ return;
+
+ if (g_list_find_custom (
+ context->imagesets, imgset,
+ (GCompareFunc) imageset_compare_by_name) != NULL
+ ) {
+ imageset_free (imgset);
+ return;
+ }
+
+ context->imagesets = g_list_append (context->imagesets, imgset);
+}
+
+void
+sprite_context_add_action (
+ SpriteContext *context,
+ XMLNode *node,
+ gint included_from
+) {
+ Action *action;
+
+ g_return_if_fail (g_strcmp0 (node->name, "action") == 0);
+
+ action = action_new (context, node, included_from);
+ if (action == NULL)
+ return;
+
+ if (g_list_find_custom (
+ context->actions, action,
+ (GCompareFunc) action_compare_by_hp_and_name) != NULL
+ ) {
+ action_free (action);
+ return;
+ }
+
+ context->actions = g_list_append (context->actions, action);
+}
+
+void
+sprite_context_add_sprite (
+ SpriteContext *context,
+ XMLNode *node,
+ gint included_from
+) {
+ GList *list;
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (g_strcmp0 (node->name, "sprite") == 0);
+
+ list = node->sub_nodes;
+ for (; list != NULL; list = g_list_next (list)) {
+ XMLNode *current = (XMLNode *) list->data;
+ gchar *name = current->name;
+ if (g_strcmp0 (name, "include") == 0) {
+ XMLNode *sprite;
+ gchar *filename, *file;
+
+ file = xml_node_get_attr_value (current, "file");
+ if (file == NULL) {
+ /* TODO: report error */
+ continue;
+ }
+
+ if (g_list_find_custom (
+ context->includes, file,
+ (GCompareFunc) g_strcmp0) != NULL
+ ) {
+ /* TODO: such file was already included */
+ g_free (file);
+ continue;
+ }
+
+ context->includes = g_list_append (
+ context->includes, file
+ );
+
+ filename = config_data_path_get_full_sprite_path (file);
+
+ sprite = xml_parse_file (filename);
+ g_free (filename);
+
+ if (sprite == NULL) {
+ /* TODO: report error */
+ continue;
+ }
+
+ if (g_strcmp0 (sprite->name, "sprite") != 0) {
+ /* TODO: report error */
+ continue;
+ }
+
+ if (included_from == -1)
+ included_from = current->line_no;
+
+ sprite_context_add_sprite (
+ context,
+ sprite,
+ included_from
+ );
+
+ xml_free (sprite);
+ } else
+ if (g_strcmp0 (name, "imageset") == 0) {
+ sprite_context_add_imageset (context, current);
+ } else
+ if (g_strcmp0 (name, "action") == 0) {
+ sprite_context_add_action (context, current, included_from);
+ } else
+ if (g_strcmp0 (name, "saedit") == 0) {
+ if (included_from == -1) { /* we are in the main context */
+ xml_setup_setup (current);
+ }
+ } else {
+ /* TODO: sprite contains something unknown */
+ }
+ }
+}
+
+Action *
+sprite_context_get_action (
+ const SpriteContext *context,
+ gint hp,
+ const gchar *name
+) {
+ GList *action = context->actions;
+
+ while (action != NULL) {
+ if (action_hp_and_name_equals (
+ (Action *) action->data,
+ hp,
+ name
+ )) {
+ return (Action *) action->data;
+ }
+
+ action = g_list_next (action);
+ }
+
+ return NULL;
+}
+
+Imageset *
+sprite_context_get_imageset (
+ const SpriteContext *context,
+ const gchar *name
+) {
+ GList *imageset = context->imagesets;
+
+ while (imageset != NULL) {
+ if (imageset_name_equals (
+ (Imageset *) imageset->data,
+ name
+ )) {
+ return (Imageset *) imageset->data;
+ }
+
+ imageset = g_list_next (imageset);
+ }
+
+ return NULL;
+}
+
+GList *
+sprite_context_get_actions (
+ const SpriteContext *context
+) {
+ return g_list_copy (context->actions);
+}
+
+void
+sprite_context_free (
+ SpriteContext *context
+) {
+ g_list_free_full (
+ context->imagesets,
+ (GDestroyNotify) imageset_free
+ );
+
+ g_list_free_full (
+ context->actions,
+ (GDestroyNotify) action_free
+ );
+
+ g_list_free_full (
+ context->includes,
+ (GDestroyNotify) g_free
+ );
+
+ g_free (context->cdf_filename);
+
+ g_free (context);
+}
diff --git a/saedit/context.h b/saedit/context.h
new file mode 100644
index 0000000..9d41fde
--- /dev/null
+++ b/saedit/context.h
@@ -0,0 +1,50 @@
+#ifndef CONTEXT_H
+#define CONTEXT_H
+
+#include "xml.h"
+#include "imageset.h"
+#include "action.h"
+
+typedef struct _SpriteContext SpriteContext;
+
+SpriteContext *
+sprite_context_new (
+ const gchar *client_data_folder
+);
+
+void
+sprite_context_add_sprite (
+ SpriteContext *context,
+ XMLNode *node,
+ gboolean is_include
+);
+
+Action *
+action_new (
+ const SpriteContext *context,
+ const XMLNode *node,
+ gint included_from
+);
+
+Action *
+sprite_context_get_action (
+ const SpriteContext *context,
+ gint hp,
+ const gchar *name
+);
+
+Imageset *
+sprite_context_get_imageset (
+ const SpriteContext *context,
+ const gchar *name
+);
+
+GList *
+sprite_context_get_actions (
+ const SpriteContext *context
+);
+
+void
+sprite_context_free (SpriteContext *context);
+
+#endif
diff --git a/saedit/drawfuncs.c b/saedit/drawfuncs.c
new file mode 100644
index 0000000..97a769e
--- /dev/null
+++ b/saedit/drawfuncs.c
@@ -0,0 +1,107 @@
+#include "interactor.h"
+#include "drawfuncs.h"
+#include "spritedrawingarea.h"
+#include "config.h"
+
+void
+interactor_sprite_layer_draw_func (
+ SDALayer *layer,
+ cairo_t *cr,
+ gpointer user_data
+) {
+ const GdkPixbuf *sprite;
+ gint offsetX, offsetY;
+ Interactor *interactor = *((Interactor **) user_data);
+
+ if (interactor == NULL)
+ return;
+
+ sprite = interactor_get_sprite (interactor);
+
+ if (sprite == NULL)
+ return;
+
+ interactor_get_offset (interactor, &offsetX, &offsetY);
+
+ gdk_cairo_set_source_pixbuf (
+ cr, sprite,
+ offsetX,
+ offsetY + config_keys_get_tile_size () / 2
+ );
+ cairo_pattern_set_filter (
+ cairo_get_source (cr),
+ CAIRO_FILTER_NEAREST
+ );
+ cairo_paint (cr);
+}
+
+void
+background_layer_draw_func (
+ SDALayer *layer,
+ cairo_t *cr,
+ gpointer user_data
+) {
+ GdkRGBA *rgba = (GdkRGBA *) user_data;
+
+ gdk_cairo_set_source_rgba (cr, rgba);
+
+ cairo_rectangle (
+ cr,
+ -SPRITE_DRAWING_AREA_FIELD_SIZE,
+ -SPRITE_DRAWING_AREA_FIELD_SIZE,
+ 2 * SPRITE_DRAWING_AREA_FIELD_SIZE,
+ 2 * SPRITE_DRAWING_AREA_FIELD_SIZE
+ );
+ cairo_fill (cr);
+}
+
+void
+tile_grid_layer_draw_func (
+ SDALayer *layer,
+ cairo_t *cr,
+ gpointer user_data
+) {
+ gint x;
+ gint field_size = SPRITE_DRAWING_AREA_FIELD_SIZE;
+ gint tile_size = config_keys_get_tile_size ();
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_XOR);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
+ cairo_translate (cr, 0.5, 0.5);
+
+ x = -tile_size / 2;
+ while (x >= -field_size)
+ x -= tile_size;
+
+ for (; x <= field_size; x += tile_size) {
+ cairo_move_to (cr, x, -field_size);
+ cairo_line_to (cr, x, field_size);
+ cairo_move_to (cr, -field_size, x);
+ cairo_line_to (cr, field_size, x);
+ }
+
+ cairo_stroke (cr);
+}
+
+void
+pixel_grid_layer_draw_func (
+ SDALayer *layer,
+ cairo_t *cr,
+ gpointer user_data
+) {
+ gint x;
+ gint field_size = SPRITE_DRAWING_AREA_FIELD_SIZE;
+
+ cairo_set_line_width (cr, 0.1);
+ cairo_set_source_rgb (cr, 0, 0, 0);
+
+ for (x = -field_size; x <= field_size; ++x) {
+ cairo_move_to (cr, x, -field_size);
+ cairo_line_to (cr, x, field_size);
+ cairo_move_to (cr, -field_size, x);
+ cairo_line_to (cr, field_size, x);
+ }
+
+ cairo_stroke (cr);
+}
diff --git a/saedit/drawfuncs.h b/saedit/drawfuncs.h
new file mode 100644
index 0000000..1c1154e
--- /dev/null
+++ b/saedit/drawfuncs.h
@@ -0,0 +1,33 @@
+#ifndef _DRAW_FUNCS_H_
+#define _DRAW_FUNCS_H_
+
+#include "spritedrawingarea.h"
+
+void
+interactor_sprite_layer_draw_func (
+ SDALayer *layer,
+ cairo_t *cr,
+ gpointer user_data
+);
+
+void
+background_layer_draw_func (
+ SDALayer *layer,
+ cairo_t *cr,
+ gpointer user_data
+);
+
+void
+tile_grid_layer_draw_func (
+ SDALayer *layer,
+ cairo_t *cr,
+ gpointer user_data
+);
+
+void
+pixel_grid_layer_draw_func (
+ SDALayer *layer,
+ cairo_t *cr,
+ gpointer user_data
+);
+#endif
diff --git a/saedit/errors.c b/saedit/errors.c
new file mode 100644
index 0000000..ff3a710
--- /dev/null
+++ b/saedit/errors.c
@@ -0,0 +1,23 @@
+#include "errors.h"
+#include "main.h"
+
+void
+post_error (
+ const gchar *error_context,
+ const gchar *error_message
+) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new_with_markup (
+ GTK_WINDOW (main_window),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "<b>%s error:</b> %s",
+ error_context,
+ error_message
+ );
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
diff --git a/saedit/errors.h b/saedit/errors.h
new file mode 100644
index 0000000..58b12e6
--- /dev/null
+++ b/saedit/errors.h
@@ -0,0 +1,12 @@
+#ifndef _ERRORS_H_
+#define _ERRORS_H_
+
+#include <glib.h>
+
+void
+post_error (
+ const gchar *error_context,
+ const gchar *error_message
+);
+
+#endif
diff --git a/saedit/file.c b/saedit/file.c
new file mode 100644
index 0000000..5496c86
--- /dev/null
+++ b/saedit/file.c
@@ -0,0 +1,103 @@
+#include <gtksourceview/gtksource.h>
+#include "errors.h"
+#include "buffer.h"
+#include "main.h"
+#include "file.h"
+
+#include <string.h>
+
+
+gchar *opened_file_name = NULL;
+
+gboolean
+show_unsaved_changes_dialog () {
+ gint result;
+ GtkWidget *dialog;
+
+ if (!buffer_get_modified ()) {
+ return FALSE;
+ }
+
+ dialog = gtk_message_dialog_new (
+ GTK_WINDOW (main_window),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_YES_NO,
+ "There are unsaved changes in the current file. "
+ "Do you wish to proceed?"
+ );
+
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ return result == GTK_RESPONSE_NO;
+}
+
+void
+open_file (
+ const gchar *filename
+) {
+ gsize len;
+ gchar *text;
+
+ if (show_unsaved_changes_dialog ())
+ return;
+
+ release_context ();
+
+ if (filename != NULL) {
+ g_file_get_contents (
+ filename,
+ &text,
+ &len,
+ NULL
+ );
+ } else {
+ text = g_strdup ("");
+ len = 0;
+ }
+
+ g_free (opened_file_name);
+ opened_file_name = g_strdup (filename);
+
+ buffer_set_text (text, len);
+ buffer_set_modified (FALSE);
+
+ update_window_title ();
+
+ g_free (text);
+}
+
+void
+save_file (const gchar *filename) {
+ GError *error = NULL;
+ gboolean success;
+ gchar *text = buffer_get_text ();
+
+ success = g_file_set_contents (
+ filename == NULL ? opened_file_name : filename,
+ text,
+ strlen (text),
+ &error
+ );
+
+ if (!success) {
+ post_error ("Saving", error->message);
+ } else {
+ if (filename != NULL) {
+ g_free (opened_file_name);
+ opened_file_name = g_strdup (filename);
+ }
+
+ buffer_set_modified (FALSE);
+ update_window_title ();
+ }
+
+ g_free (text);
+}
+
+const gchar *
+get_opened_file_name () {
+ return opened_file_name;
+}
+
diff --git a/saedit/file.h b/saedit/file.h
new file mode 100644
index 0000000..9354879
--- /dev/null
+++ b/saedit/file.h
@@ -0,0 +1,16 @@
+#ifndef _FILE_H_
+#define _FILE_H_
+
+gboolean
+show_unsaved_changes_dialog (void);
+
+const gchar *
+get_opened_file_name (void);
+
+void
+open_file (const gchar *filename);
+
+void
+save_file (const gchar *filename);
+
+#endif
diff --git a/saedit/glade.sh b/saedit/glade.sh
new file mode 100755
index 0000000..77f5b18
--- /dev/null
+++ b/saedit/glade.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+GLADE_CATALOG_SEARCH_PATH=glade GLADE_MODULE_SEARCH_PATH=glade glade $*
diff --git a/saedit/glade/saedit-catalog.xml b/saedit/glade/saedit-catalog.xml
new file mode 100644
index 0000000..4f304a7
--- /dev/null
+++ b/saedit/glade/saedit-catalog.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glade-catalog name="saedit" library="saedit" depends="gtk+">
+ <glade-widget-classes>
+ <glade-widget-class
+ name="TreeFolderView"
+ generic-name="treefolderview"
+ title="TreeFolderView"
+ />
+ <glade-widget-class
+ name="SpriteDrawingArea"
+ generic-name="spritedrawingarea"
+ title="SpriteDrawingArea"
+ />
+ <glade-widget-class
+ name="GtkSourceBuffer"
+ generic-name="sourcebuffer"
+ title="GtkSourceBuffer"
+ />
+ </glade-widget-classes>
+
+ <glade-widget-group name="saedit" title="SAEdit">
+ <glade-widget-class-ref name="TreeFolderView"/>
+ <glade-widget-class-ref name="SpriteDrawingArea"/>
+ <glade-widget-class-ref name="GtkSourceBuffer"/>
+ </glade-widget-group>
+</glade-catalog>
diff --git a/saedit/grounds/earth.png b/saedit/grounds/earth.png
deleted file mode 100644
index 9bdb25a..0000000
--- a/saedit/grounds/earth.png
+++ /dev/null
Binary files differ
diff --git a/saedit/grounds/grass.png b/saedit/grounds/grass.png
deleted file mode 100644
index a0f3ab5..0000000
--- a/saedit/grounds/grass.png
+++ /dev/null
Binary files differ
diff --git a/saedit/grounds/sand.png b/saedit/grounds/sand.png
deleted file mode 100644
index 31be5db..0000000
--- a/saedit/grounds/sand.png
+++ /dev/null
Binary files differ
diff --git a/saedit/grounds/water.png b/saedit/grounds/water.png
deleted file mode 100644
index 20da6b4..0000000
--- a/saedit/grounds/water.png
+++ /dev/null
Binary files differ
diff --git a/saedit/iface.ui b/saedit/iface.ui
new file mode 100644
index 0000000..dd1cab9
--- /dev/null
+++ b/saedit/iface.ui
@@ -0,0 +1,577 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+ <requires lib="gtk+" version="3.12"/>
+ <requires lib="gtksourceview" version="3.0"/>
+ <requires lib="saedit" version="0.0"/>
+ <object class="GtkAccelGroup" id="accelgroup1"/>
+ <object class="GtkFileFilter" id="filefilter-xml">
+ <patterns>
+ <pattern>*.xml</pattern>
+ </patterns>
+ </object>
+ <object class="GtkListStore" id="liststore-actions">
+ <columns>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ <!-- column-name hp -->
+ <column type="gint"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkSourceBuffer" id="sourceview-buffer">
+ <signal name="modified-changed" handler="sourceview_buffer_modified_changed_cb" swapped="no"/>
+ </object>
+ <object class="GtkAdjustment" id="zoom-adjustment">
+ <property name="lower">1</property>
+ <property name="upper">10</property>
+ <property name="value">1</property>
+ <property name="step_increment">0.10000000000000001</property>
+ <property name="page_increment">0.5</property>
+ <signal name="value-changed" handler="zoom_adjustment_value_changed_cb" object="drawingarea-main" swapped="no"/>
+ </object>
+ <object class="GtkWindow" id="window-main">
+ <property name="can_focus">False</property>
+ <property name="icon">logo.svg</property>
+ <signal name="delete-event" handler="window_main_delete_event_cb" swapped="no"/>
+ <signal name="destroy" handler="gtk_main_quit" swapped="no"/>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="accel_group">accelgroup1</property>
+ <child>
+ <object class="GtkImageMenuItem" id="menuitem-file-new">
+ <property name="label">gtk-new</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="new_file_activated_cb" swapped="no"/>
+ <accelerator key="n" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menuitem-file-open">
+ <property name="label">gtk-open</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="open_file_activated_cb" swapped="no"/>
+ <accelerator key="o" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menuitem-file-save">
+ <property name="label">gtk-save</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="save_file_activated_cb" swapped="no"/>
+ <accelerator key="s" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menuitem-file-save-as">
+ <property name="label">gtk-save-as</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="save_file_as_activated_cb" swapped="no"/>
+ <accelerator key="s" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="&lt;separator&gt;">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem-choose-df">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Choose data folder...</property>
+ <signal name="activate" handler="choose_df_activated_cb" object="treefolderview-main" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separatormenuitem3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="imagemenuitem25">
+ <property name="label">gtk-quit</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem-view-reset-to-center">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Reset to center</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="view_reset_to_center_activate_cb" swapped="no"/>
+ <accelerator key="r" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="menuitem-show-tile-grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Tile grid</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <signal name="toggled" handler="show_tile_grid_toggled_cb" swapped="no"/>
+ <accelerator key="g" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="menuitem-show-pixel-grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Pixel grid</property>
+ <property name="use_underline">True</property>
+ <signal name="toggled" handler="show_pixel_grid_toggled_cb" swapped="no"/>
+ <accelerator key="g" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="menuitem-about">
+ <property name="label">gtk-about</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="menuitem_about_activate_cb" object="about-dialog" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkPaned" id="paned2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">1</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow">
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="TreeFolderView" id="treefolderview-main">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="filter">filefilter-xml</property>
+ <property name="filename">/home/</property>
+ <signal name="file-activated" handler="tfview_file_activated_cb" object="sourceview-main" swapped="no"/>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkPaned" id="paned3">
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="orientation">vertical</property>
+ <property name="position">100</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="height_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="SpriteDrawingArea" id="drawingarea-main">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="zoom-scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">zoom-adjustment</property>
+ <property name="inverted">True</property>
+ <property name="restrict_to_fill_level">False</property>
+ <property name="round_digits">1</property>
+ <property name="has_origin">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Action:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="cbox-actions">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore-actions</property>
+ <property name="id_column">2</property>
+ <signal name="changed" handler="action_changed_cb" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2">
+ <property name="xalign">1</property>
+ <property name="alignment">right</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Direction: </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="cboxtext-directions">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="button_sensitivity">on</property>
+ <signal name="changed" handler="direction_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="toolbar-animation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkToolButton" id="button-parse-buffer">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Parse buffer</property>
+ <property name="stock_id">gtk-execute</property>
+ <signal name="clicked" handler="parse_buffer_clicked_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="&lt;separator&gt;1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="button-anim-first">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">First frame</property>
+ <property name="stock_id">gtk-goto-first</property>
+ <signal name="clicked" handler="first_frame_clicked_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="button-anim-prev">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Previous frame</property>
+ <property name="stock_id">gtk-go-back</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="button-anim-play">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Play/Pause</property>
+ <property name="use_underline">True</property>
+ <property name="icon_name">media-playback-start</property>
+ <signal name="clicked" handler="play_pause_clicked_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="button-anim-next">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Next frame</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-go-forward</property>
+ <signal name="clicked" handler="next_frame_clicked_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="button-anim-last">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Last frame</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-goto-last</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="width_request">100</property>
+ <property name="height_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkSourceView" id="sourceview-main">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="left_margin">2</property>
+ <property name="right_margin">2</property>
+ <property name="buffer">sourceview-buffer</property>
+ <property name="monospace">True</property>
+ <property name="show_line_numbers">True</property>
+ <property name="show_line_marks">True</property>
+ <property name="tab_width">4</property>
+ <property name="indent_width">4</property>
+ <property name="auto_indent">True</property>
+ <property name="insert_spaces_instead_of_tabs">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAboutDialog" id="about-dialog">
+ <property name="can_focus">False</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="transient_for">window-main</property>
+ <property name="program_name">Sprite Animation Editor</property>
+ <property name="version">alpha2</property>
+ <property name="copyright" translatable="yes">Copyleft (ɔ) Vasily_Makarov</property>
+ <property name="comments" translatable="yes">Editor, parser and player for the TMW/Evol project animations. Greatly inspired by the project developers team.</property>
+ <property name="authors">Danil "Vasily Makarov" Sagunov &lt;vasily@evolonline.org&gt;</property>
+ <property name="logo">logo.svg</property>
+ <property name="license_type">gpl-3-0</property>
+ <signal name="response" handler="about_dialog_response_cb" swapped="no"/>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/saedit/imageset.c b/saedit/imageset.c
new file mode 100644
index 0000000..9ae2ea0
--- /dev/null
+++ b/saedit/imageset.c
@@ -0,0 +1,130 @@
+#include "imageset.h"
+#include <string.h>
+
+struct _Imageset {
+ gchar *name;
+ gint width, height;
+ gint offsetX, offsetY;
+
+ GdkPixbuf *pixbuf;
+};
+
+Imageset *
+imageset_new (
+ const XMLNode *node,
+ const gchar *cdf_filename
+) {
+ Imageset *imgset;
+ gchar *src, *filename;
+ GError *err = NULL;
+ gboolean fail = FALSE;
+
+ g_return_val_if_fail (g_strcmp0 (node->name, "imageset") == 0, NULL);
+
+ imgset = (Imageset *) g_new0 (Imageset, 1);
+
+ imgset->name = xml_node_get_attr_value (node, "name");
+ if (imgset->name == NULL) {
+ /* TODO: report error */
+ fail = TRUE;
+ }
+
+ src = xml_node_get_attr_value (node, "src");
+ if (src == NULL) {
+ /* TODO: report error */
+ fail = TRUE;
+ } else {
+ gchar *delim = strchr (src, '|');
+ if (delim != NULL)
+ *delim = 0;
+ /* TODO: process palettes? */
+ }
+
+ imgset->width = xml_node_get_int_attr_value (node, "width", -1);
+ imgset->height = xml_node_get_int_attr_value (node, "height", -1);
+ if (imgset->width <= 0 || imgset->height <= 0) {
+ /* TODO: report error */
+ fail = TRUE;
+ }
+
+ imgset->offsetX = xml_node_get_int_attr_value (node, "offsetX", 0);
+ imgset->offsetY = xml_node_get_int_attr_value (node, "offsetY", 0);
+
+ filename = g_strconcat (cdf_filename, "/", src, NULL);
+ imgset->pixbuf = gdk_pixbuf_new_from_file (filename, &err);
+
+ if (imgset->pixbuf == NULL) {
+ /* TODO handle err and report error */
+ fail = TRUE;
+ }
+
+ if (fail) {
+ imageset_free (imgset);
+ return NULL;
+ }
+
+ return imgset;
+}
+
+gint
+imageset_compare_by_name (
+ const Imageset *first,
+ const Imageset *second
+) {
+ return g_strcmp0 (first->name, second->name);
+}
+
+GdkPixbuf *
+imageset_get_sprite_by_index (
+ const Imageset *imageset,
+ gint index
+) {
+ gint w = gdk_pixbuf_get_width (imageset->pixbuf);
+ w /= imageset->width;
+
+ g_return_val_if_fail (index >= 0, NULL);
+
+ return gdk_pixbuf_new_subpixbuf (
+ imageset->pixbuf,
+ (index % w) * imageset->width,
+ (index / w) * imageset->height,
+ imageset->width,
+ imageset->height
+ );
+}
+
+void
+imageset_free (Imageset *imgset) {
+ g_free (imgset->name);
+ if (imgset->pixbuf != NULL)
+ g_object_unref (imgset->pixbuf);
+ g_free (imgset);
+}
+
+gboolean
+imageset_name_equals (
+ const Imageset *imageset,
+ const gchar *name
+) {
+ return g_strcmp0 (imageset->name, name) == 0;
+}
+
+void
+imageset_get_offset (
+ const Imageset *imageset,
+ gint *offsetX,
+ gint *offsetY
+) {
+ *offsetX = imageset->offsetX;
+ *offsetY = imageset->offsetY;
+}
+
+void
+imageset_get_size (
+ const Imageset *imageset,
+ gint *width,
+ gint *height
+) {
+ *width = imageset->width;
+ *height = imageset->height;
+}
diff --git a/saedit/imageset.h b/saedit/imageset.h
new file mode 100644
index 0000000..3496d65
--- /dev/null
+++ b/saedit/imageset.h
@@ -0,0 +1,50 @@
+#ifndef IMAGESET_H
+#define IMAGESET_H
+
+#include <gdk/gdk.h>
+#include "common.h"
+#include "xml.h"
+
+typedef struct _Imageset Imageset;
+
+Imageset *
+imageset_new (
+ const XMLNode *node,
+ const gchar *cdf_filename
+);
+
+void
+imageset_free (Imageset *imageset);
+
+gint
+imageset_compare_by_name (
+ const Imageset *first,
+ const Imageset *second
+);
+
+GdkPixbuf *
+imageset_get_sprite_by_index (
+ const Imageset *imageset,
+ gint index
+);
+
+gboolean
+imageset_name_equals (
+ const Imageset *imageset,
+ const gchar *name
+);
+
+void
+imageset_get_offset (
+ const Imageset *imageset,
+ gint *offsetX,
+ gint *offsetY
+);
+
+void
+imageset_get_size (
+ const Imageset *imageset,
+ gint *width,
+ gint *height
+);
+#endif
diff --git a/saedit/interactor.c b/saedit/interactor.c
new file mode 100644
index 0000000..85e55fa
--- /dev/null
+++ b/saedit/interactor.c
@@ -0,0 +1,439 @@
+#include <glib.h>
+#include <stdlib.h>
+
+#include "interactor.h"
+#include "imageset.h"
+#include "action.h"
+#include "animation.h"
+#include "errors.h"
+
+struct _Interactor {
+ const SpriteContext *context;
+ const Action *action;
+ const Animation *animation;
+ gchar *direction;
+ gboolean rand_checked;
+ GList *element;
+ gint delay;
+
+ guint loop_tag;
+ guint tick_length;
+
+ InteractionUpdatedFunc updated_cb;
+
+ GList *repeaters;
+};
+
+static gboolean
+interactor_updated_func (Interactor *interactor) {
+ g_return_val_if_fail (interactor->updated_cb != NULL, FALSE);
+
+ interactor->updated_cb (interactor);
+ return FALSE;
+}
+
+static void
+interactor_updated (Interactor *interactor) {
+ if (interactor->updated_cb == NULL)
+ return;
+
+ g_main_context_invoke (
+ NULL,
+ (GSourceFunc) interactor_updated_func,
+ interactor
+ );
+}
+
+static AnimElement *
+interactor_get_element (const Interactor *interactor) {
+ if (interactor->element == NULL)
+ return NULL;
+ return (AnimElement *) interactor->element->data;
+}
+
+Interactor *
+interactor_new (
+ const SpriteContext *context
+) {
+ Interactor *interactor = g_new0 (Interactor, 1);
+
+ interactor->context = context;
+ interactor->repeaters = NULL;
+ return interactor;
+}
+
+gboolean
+interactor_reset_animation (
+ Interactor *interactor
+) {
+ GList *l;
+
+ for (l = interactor->repeaters; l != NULL; l = l->next)
+ interactor_reset_animation ((Interactor *) l->data);
+
+ if (interactor->action == NULL)
+ return FALSE;
+
+ interactor->animation = action_get_animation (
+ interactor->action,
+ interactor->direction
+ );
+
+ g_return_val_if_fail (interactor->animation != NULL, FALSE);
+
+ interactor->element = interactor->animation->elements;
+ interactor->delay = 0;
+ interactor->rand_checked = TRUE;
+ interactor_updated (interactor);
+ interactor_play (interactor, 0);
+
+ return TRUE;
+}
+
+gboolean
+interactor_set_action (
+ Interactor *interactor,
+ gint hp,
+ const gchar *name
+) {
+ Action *action;
+ GList *l;
+
+ for (l = interactor->repeaters; l != NULL; l = l->next)
+ interactor_set_action ((Interactor *) l->data, hp, name);
+
+ if (
+ interactor->action != NULL &&
+ action_hp_and_name_equals (interactor->action, hp, name)
+ )
+ return FALSE;
+
+ action = sprite_context_get_action (
+ interactor->context, hp, name
+ );
+
+ if (action == NULL)
+ return FALSE;
+
+ interactor->action = action;
+ return interactor_reset_animation (interactor);
+}
+
+gboolean
+interactor_set_direction (
+ Interactor *interactor,
+ const gchar *direction
+) {
+ GList *l;
+
+ for (l = interactor->repeaters; l != NULL; l = l->next)
+ interactor_set_direction ((Interactor *) l->data, direction);
+
+ if (g_strcmp0 (interactor->direction, direction) != 0) {
+ if (interactor->direction != NULL)
+ g_free (interactor->direction);
+
+ interactor->direction = g_strdup (direction);
+ return interactor_reset_animation (interactor);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+_animation_element_rand_check (
+ const AnimElement *element
+) {
+ if (element->rand == 100) return TRUE;
+ if (element->rand == 0) return FALSE;
+ return rand() % 100 < element->rand;
+}
+
+gboolean
+interactor_play (
+ Interactor *interactor,
+ gint time
+) {
+ gboolean updated = FALSE;
+ GList *l;
+
+ for (l = interactor->repeaters; l != NULL; l = l->next)
+ interactor_play ((Interactor *) l->data, time);
+
+ g_return_val_if_fail (time >= 0, FALSE);
+
+ if (interactor->action == NULL)
+ return FALSE;
+ if (interactor->animation == NULL)
+ return FALSE;
+ g_return_val_if_fail (interactor->element != NULL, FALSE);
+
+ interactor->delay += time;
+
+ while (TRUE) {
+ AnimElement *element = interactor_get_element (interactor);
+ gint e_delay = element->delay;
+
+ if ( interactor->rand_checked ||
+ _animation_element_rand_check (element)
+ ) {
+ interactor->rand_checked = TRUE;
+
+ if (interactor->delay < e_delay)
+ break;
+
+ interactor->delay -= e_delay;
+ interactor->rand_checked = FALSE;
+ updated = TRUE;
+
+ if (element->type == ELEMENT_END) {
+ interactor_reset_animation (interactor);
+ return FALSE;
+ } else
+
+ if (element->type == ELEMENT_FRAME) {
+ if (e_delay == 0)
+ break;
+ } else
+
+ if (element->type == ELEMENT_PAUSE) {
+ if (e_delay == 0)
+ break;
+ } else
+
+ if (element->type == ELEMENT_JUMP) {
+ gint delay = interactor->delay;
+
+ gboolean found = interactor_set_action (
+ interactor,
+ interactor->action->hp,
+ element->str
+ );
+
+ if (!found) {
+ /* TODO: report about this */
+ return FALSE;
+ }
+
+ return interactor_play (
+ interactor,
+ delay
+ );
+ } else
+
+ if (element->type == ELEMENT_LABEL) {
+
+ } else
+
+ if (element->type == ELEMENT_GOTO) {
+ GList *nelem =
+ interactor->animation->elements;
+
+ while (nelem != NULL) {
+ AnimElement *current =
+ (AnimElement *) nelem->data;
+ if (current->type == ELEMENT_LABEL) {
+ if (g_strcmp0 (
+ current->str,
+ element->str) == 0
+ )
+ break;
+ }
+
+ nelem = g_list_next (nelem);
+ }
+
+ if (nelem != NULL) {
+ interactor->element = nelem;
+ continue;
+ } else {
+ post_error ("Playback", "Specified goto label not found");
+ return FALSE;
+ }
+ }
+ }
+
+ interactor->element = g_list_next (interactor->element);
+ if (interactor->element == NULL)
+ interactor->element = interactor->animation->elements;
+ }
+
+ if (updated)
+ interactor_updated (interactor);
+
+ return TRUE;
+}
+
+const GdkPixbuf *
+interactor_get_sprite (const Interactor *interactor) {
+ if (interactor->element == NULL)
+ return NULL;
+ return interactor_get_element (interactor)->sprite;
+}
+
+void
+interactor_get_offset (
+ const Interactor *interactor,
+ gint *offsetX,
+ gint *offsetY
+) {
+ AnimElement *element = interactor_get_element (interactor);
+ if (element == NULL)
+ return;
+
+ *offsetX = element->offsetX;
+ *offsetY = element->offsetY;
+}
+
+gboolean
+interactor_loop_tick (Interactor *interactor) {
+ gboolean result = interactor_play (
+ interactor,
+ interactor->tick_length
+ );
+
+ if (result == FALSE) {
+ interactor->loop_tag = 0;
+ interactor_updated (interactor);
+ }
+
+ return result;
+}
+
+void
+interactor_loop_start (
+ Interactor *interactor,
+ const guint interval,
+ const guint tick_length
+) {
+ if (interactor->loop_tag != 0)
+ return;
+
+ interactor->tick_length = tick_length;
+ interactor->loop_tag = g_timeout_add (
+ interval,
+ (GSourceFunc) interactor_loop_tick,
+ interactor
+ );
+
+ interactor_updated (interactor);
+}
+
+gboolean
+interactor_loop_stop (Interactor *interactor) {
+ if (interactor->loop_tag == 0)
+ return FALSE;
+
+ g_source_remove (interactor->loop_tag);
+ interactor->loop_tag = 0;
+
+ interactor_updated (interactor);
+
+ return TRUE;
+}
+
+gboolean
+interactor_loop_running (
+ const Interactor *interactor
+) {
+ if (interactor == NULL)
+ return FALSE;
+ return interactor->loop_tag != 0;
+}
+
+void
+interactor_free (Interactor *interactor) {
+ interactor_loop_stop (interactor);
+ g_list_free (interactor->repeaters);
+ g_free (interactor);
+}
+
+void
+interactor_free_with_repeaters (Interactor *interactor) {
+ interactor_loop_stop (interactor);
+
+ g_list_free_full (
+ interactor->repeaters,
+ (GDestroyNotify) interactor_free_with_repeaters
+ );
+
+ g_free (interactor);
+}
+
+void
+interactor_set_updated_callback (
+ Interactor *interactor,
+ InteractionUpdatedFunc callback
+) {
+ interactor->updated_cb = callback;
+}
+
+gint
+interactor_get_line_no (
+ Interactor *interactor
+) {
+ AnimElement *element = interactor_get_element (interactor);
+
+ if (element == NULL)
+ return -1;
+
+ return element->line_no;
+}
+
+const gchar *
+interactor_get_animation_direction (
+ const Interactor *interactor
+) {
+ if (interactor->animation == NULL)
+ return NULL;
+ return interactor->animation->direction;
+}
+
+const gchar *
+interactor_get_direction (
+ const Interactor *interactor
+) {
+ return interactor->direction;
+}
+
+gboolean
+interactor_get_action_hp_and_name (
+ const Interactor *interactor,
+ gint *hp,
+ gchar **name
+) {
+ if (interactor->action == NULL)
+ return FALSE;
+
+ action_get_hp_and_name (interactor->action, hp, name);
+ return TRUE;
+}
+
+void
+interactor_skip_current_frame (
+ Interactor *interactor
+) {
+ AnimElement *element;
+
+ interactor_loop_stop (interactor);
+ element = interactor_get_element (interactor);
+
+ if (element != NULL) {
+ g_return_if_fail (interactor->delay <= element->delay);
+ interactor_play (
+ interactor,
+ element->delay - interactor->delay
+ );
+ }
+}
+
+void
+interactor_add_repeater (
+ Interactor *interactor,
+ Interactor *repeater
+) {
+ interactor->repeaters = g_list_append (
+ interactor->repeaters,
+ repeater
+ );
+}
diff --git a/saedit/interactor.h b/saedit/interactor.h
new file mode 100644
index 0000000..5d0ddfc
--- /dev/null
+++ b/saedit/interactor.h
@@ -0,0 +1,110 @@
+#ifndef _INTERACTOR_H_
+#define _INTERACTOR_H_
+
+#include "context.h"
+
+typedef struct _Interactor Interactor;
+
+Interactor *
+interactor_new (
+ const SpriteContext *context
+);
+
+gboolean
+interactor_set_action (
+ Interactor *interactor,
+ gint hp,
+ const gchar *name
+);
+
+gboolean
+interactor_reset_animation (
+ Interactor *interactor
+);
+
+gboolean
+interactor_set_direction (
+ Interactor *interactor,
+ const gchar *direction
+);
+
+gboolean
+interactor_play (
+ Interactor *interactor,
+ gint time
+);
+
+const GdkPixbuf *
+interactor_get_sprite (const Interactor *interactor);
+
+void
+interactor_loop_start (
+ Interactor *interactor,
+ const guint interval,
+ const guint tick_length
+);
+
+gboolean
+interactor_loop_stop (Interactor *interactor);
+
+void
+interactor_free (Interactor *interactor);
+
+void
+interactor_free_with_repeaters (Interactor *interactor);
+
+typedef void
+(*InteractionUpdatedFunc) (Interactor *interactor);
+
+void
+interactor_set_updated_callback (
+ Interactor *interactor,
+ InteractionUpdatedFunc callback
+);
+
+void
+interactor_get_offset (
+ const Interactor *interactor,
+ gint *offsetX,
+ gint *offsetY
+);
+
+gint
+interactor_get_line_no (
+ Interactor *interactor
+);
+
+const gchar *
+interactor_get_animation_direction (
+ const Interactor *interactor
+);
+
+const gchar *
+interactor_get_direction (
+ const Interactor *interactor
+);
+
+gboolean
+interactor_get_action_hp_and_name (
+ const Interactor *interactor,
+ gint *hp,
+ gchar **name
+);
+
+gboolean
+interactor_loop_running (
+ const Interactor *interactor
+);
+
+void
+interactor_skip_current_frame (
+ Interactor *interactor
+);
+
+void
+interactor_add_repeater (
+ Interactor *interactor,
+ Interactor *repeater
+);
+
+#endif
diff --git a/saedit/interface.c b/saedit/interface.c
deleted file mode 100644
index 0984cbd..0000000
--- a/saedit/interface.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
-
-GtkWidget *reload_menu_item = NULL;
-GtkWidget *find_dialog = NULL;
-GtkWidget *toolbar = NULL;
-
-void find_menu_item_activate_callback(GtkWidget *menuitem, gpointer user_data) {
- gtk_dialog_run(GTK_DIALOG(find_dialog));
-}
-
-void save_dialog_response_callback(GtkWidget *dialog, gint response_id, gpointer user_data) {
- if (response_id == GTK_RESPONSE_ACCEPT) {
- gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
- if (filename != NULL) {
- save_to_xml_file(filename);
- gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(xml_file_chooser_button), filename);
- }
- }
- gtk_widget_destroy(dialog);
-}
-
-void save_dialog_show() {
- GtkDialog *dialog = GTK_DIALOG(gtk_file_chooser_dialog_new(_("Save file as..."), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL));
- g_signal_connect(dialog, "response", G_CALLBACK(save_dialog_response_callback), NULL);
- gtk_dialog_run(dialog);
-}
-
-void save_menu_item_activate_callback(GtkWidget *menuitem, GtkWidget *fsdialog) {
- gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(xml_file_chooser_button));
- if (filename != NULL)
- save_to_xml_file(filename);
- else
- save_dialog_show();
-}
-
-void xml_file_save_button_callback(GtkWidget *button, gpointer user_data) {
- gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(xml_file_chooser_button));
- if (filename != NULL)
- save_to_xml_file(filename);
-}
-
-void file_new() {
- GtkTextIter start, end;
- gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(source_buffer), &start, &end);
- gtk_text_buffer_delete(GTK_TEXT_BUFFER(source_buffer), &start, &end);
- gchar *temp;
- if (g_file_get_contents(FILE_TEMPLATE, &temp, NULL, NULL))
- gtk_text_buffer_set_text(GTK_TEXT_BUFFER(source_buffer), temp, -1);
- gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(xml_file_chooser_button), "");
- gtk_widget_set_sensitive(reload_menu_item, FALSE);
-
- free_current_info();
-}
-
-void set_up_interface() {
-
- GtkBuilder *builder = gtk_builder_new();
- gtk_builder_add_from_file(builder, "interface.ui", NULL);
- gtk_builder_connect_signals(builder, NULL);
-
- //Setup main window
- win = GTK_WIDGET(gtk_builder_get_object(builder, "win_main"));
-
- //Setup GtkSourceView
- GtkSourceLanguageManager *langman = gtk_source_language_manager_get_default();
- source_buffer = gtk_source_buffer_new_with_language(gtk_source_language_manager_get_language(langman, "xml"));
-
- source_view = GTK_WIDGET(gtk_builder_get_object(builder, "source_view"));
- gtk_text_view_set_buffer(GTK_TEXT_VIEW(source_view), GTK_TEXT_BUFFER(source_buffer));
- search_init(source_view);
-
- //Setup GtkScrolledWindow
- GtkWidget *scrolled_window = NULL;
- scrolled_window = GTK_WIDGET(gtk_builder_get_object(builder, "scrolledwindow1"));
- gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolled_window),
- gtk_text_view_get_hadjustment(GTK_TEXT_VIEW(source_view)));
- gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolled_window),
- gtk_text_view_get_vadjustment(GTK_TEXT_VIEW(source_view)));
-
- //Setup GtkDrawingArea
- darea = GTK_WIDGET(gtk_builder_get_object(builder, "darea1"));
-
- //Setup GtkToolbar
- toolbar = GTK_WIDGET(gtk_builder_get_object(builder, "toolbar"));
-
- reload_menu_item = GTK_WIDGET(gtk_builder_get_object(builder, "menuitem6"));
- show_grid_menu_item = GTK_WIDGET(gtk_builder_get_object(builder, "menuitem11"));
- imageset_preview_menu_item = GTK_WIDGET(gtk_builder_get_object(builder, "menuitem12"));
-
- data_folder_chooser_button = GTK_WIDGET(gtk_builder_get_object(builder, "datafcbutton"));
- xml_file_chooser_button = GTK_WIDGET(gtk_builder_get_object(builder, "xmlfcbutton"));
- xml_file_open_button = GTK_WIDGET(gtk_builder_get_object(builder, "xmlfobutton"));
- xml_file_save_button = GTK_WIDGET(gtk_builder_get_object(builder, "xmlfsbutton"));
-
- gen_sae_info->imagesets_combo_box = GTK_WIDGET(gtk_builder_get_object(builder, "imagesetscbox"));
- gen_sae_info->actions_combo_box = GTK_WIDGET(gtk_builder_get_object(builder, "actionscbox"));
- gen_sae_info->animations_combo_box = GTK_WIDGET(gtk_builder_get_object(builder, "animationscbox"));
-
- //Setup GtkAboutDialog
- about_dialog = GTK_WIDGET(gtk_builder_get_object(builder, "about_dialog"));
-
- //Setup GtkMessageDialog
- parsing_error_dialog = GTK_WIDGET(gtk_builder_get_object(builder, "parsing-error-dialog"));
-
- //Setup Find dialog
- find_dialog = GTK_WIDGET(gtk_builder_get_object(builder, "find_dialog"));
- gtk_widget_hide(find_dialog);
-
- file_new();
-
- gtk_widget_show_all(win);
- gtk_widget_show_all(source_view);
-
- g_object_unref(builder);
-}
diff --git a/saedit/interface.ui b/saedit/interface.ui
deleted file mode 100644
index dc83601..0000000
--- a/saedit/interface.ui
+++ /dev/null
@@ -1,852 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.18.3 -->
-<interface>
- <requires lib="gtk+" version="3.2"/>
- <requires lib="gtksourceview" version="0.0"/>
- <object class="GtkAccelGroup" id="accelgroup"/>
- <object class="GtkFileChooserDialog" id="fcdialog1">
- <property name="can_focus">False</property>
- <property name="border_width">5</property>
- <property name="title" translatable="yes">Open file</property>
- <property name="type_hint">dialog</property>
- <child internal-child="vbox">
- <object class="GtkBox" id="dialog-vbox1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkButtonBox" id="dialog-action_area1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="button1">
- <property name="label">gtk-open</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="button2">
- <property name="label">gtk-cancel</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-3">button1</action-widget>
- <action-widget response="0">button2</action-widget>
- </action-widgets>
- </object>
- <object class="GtkWindow" id="win_main">
- <property name="width_request">600</property>
- <property name="height_request">600</property>
- <property name="can_focus">False</property>
- <property name="title" translatable="yes">Sprite Animation Editor</property>
- <property name="window_position">center</property>
- <property name="default_width">600</property>
- <property name="default_height">600</property>
- <property name="icon">icon.svg</property>
- <signal name="destroy" handler="save_config_and_quit" swapped="no"/>
- <child>
- <object class="GtkVBox" id="vbox1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkMenuBar" id="menubar1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkMenuItem" id="menuitem1">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_File</property>
- <property name="use_underline">True</property>
- <child type="submenu">
- <object class="GtkMenu" id="menu1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="accel_group">accelgroup</property>
- <child>
- <object class="GtkMenuItem" id="menuitem4">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_New</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="file_new" swapped="no"/>
- <accelerator key="n" signal="activate" modifiers="GDK_CONTROL_MASK"/>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menuitem5">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Open...</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="open_menu_item_activate_callback" object="fcdialog1" swapped="no"/>
- <accelerator key="o" signal="activate" modifiers="GDK_CONTROL_MASK"/>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menuitem6">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Reload</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="open_xml_file" swapped="no"/>
- <accelerator key="r" signal="activate" modifiers="GDK_CONTROL_MASK"/>
- </object>
- </child>
- <child>
- <object class="GtkSeparatorMenuItem" id="menuitem15">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menuitem7">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Save</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="save_menu_item_activate_callback" swapped="no"/>
- <accelerator key="s" signal="activate" modifiers="GDK_CONTROL_MASK"/>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menuitem8">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Save _as...</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="save_dialog_show" swapped="no"/>
- <accelerator key="s" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menuitem2">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Search</property>
- <property name="use_underline">True</property>
- <child type="submenu">
- <object class="GtkMenu" id="menu2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkMenuItem" id="menuitem9">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Find...</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="find_menu_item_activate_callback" swapped="no"/>
- <accelerator key="f" signal="activate" modifiers="GDK_CONTROL_MASK"/>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menuitem10">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Find _Next</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="search_find_next" swapped="no"/>
- <accelerator key="f" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menuitem3">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_View</property>
- <property name="use_underline">True</property>
- <child type="submenu">
- <object class="GtkMenu" id="menu3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkCheckMenuItem" id="menuitem11">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Show _Grid</property>
- <property name="use_underline">True</property>
- <signal name="toggled" handler="show_grid_menu_item_toggled_callback" swapped="no"/>
- <accelerator key="g" signal="activate" modifiers="GDK_CONTROL_MASK"/>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menuitem12">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Imageset view</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="show_imageset_dialog" swapped="no"/>
- <accelerator key="i" signal="activate" modifiers="GDK_CONTROL_MASK"/>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menuitem13">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Help</property>
- <property name="use_underline">True</property>
- <child type="submenu">
- <object class="GtkMenu" id="menu4">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkMenuItem" id="menuitem14">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_About</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="show_about_dialog" swapped="no"/>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hbox1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkVBox" id="vbox4">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel" id="label2">
- <property name="height_request">24</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Clientdata folder</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkFileChooserButton" id="datafcbutton">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="action">select-folder</property>
- <property name="title" translatable="yes">Clientdata folder</property>
- <signal name="selection-changed" handler="data_folder_set_callback" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label1">
- <property name="height_request">24</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">XML source file</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkFileChooserButton" id="xmlfcbutton">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="local_only">False</property>
- <property name="dialog">fcdialog1</property>
- <property name="title" translatable="yes">Select XML file</property>
- <signal name="file-set" handler="open_xml_file" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="xmlfobutton">
- <property name="label">gtk-open</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="open_xml_file" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">4</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="xmlfsbutton">
- <property name="label">gtk-save</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="xml_file_save_button_callback" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">5</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="button3">
- <property name="label" translatable="yes">Parse XML Buffer</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <signal name="clicked" handler="parse_xml_buffer" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">6</property>
- </packing>
- </child>
- <child>
- <object class="GtkSeparator" id="separator1">
- <property name="height_request">30</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">7</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label3">
- <property name="height_request">24</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Imagesets</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">8</property>
- </packing>
- </child>
- <child>
- <object class="GtkComboBoxText" id="imagesetscbox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="active">0</property>
- <signal name="changed" handler="imagesets_combo_box_changed_callback" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">9</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label4">
- <property name="height_request">24</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Actions</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">10</property>
- </packing>
- </child>
- <child>
- <object class="GtkComboBoxText" id="actionscbox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="active">0</property>
- <signal name="changed" handler="actions_combo_box_changed_callback" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">11</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label5">
- <property name="height_request">24</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Directions</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">12</property>
- </packing>
- </child>
- <child>
- <object class="GtkComboBoxText" id="animationscbox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="active">0</property>
- <signal name="changed" handler="animations_combo_box_changed_callback" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">13</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkSeparator" id="separator2">
- <property name="width_request">1</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkVPaned" id="vpaned1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkVBox" id="vbox2">
- <property name="can_focus">False</property>
- <child>
- <object class="GtkDrawingArea" id="darea1">
- <property name="height_request">120</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <signal name="draw" handler="darea_draw_event" swapped="no"/>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolbar" id="toolbar">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkToolButton" id="toolbar-to-first">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">First frame</property>
- <property name="use_underline">True</property>
- <property name="stock_id">gtk-goto-first</property>
- <signal name="clicked" handler="toolbar_to_first_clicked_callback" swapped="no"/>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="toolbar-prev-frame">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Previous frame</property>
- <property name="use_underline">True</property>
- <property name="stock_id">gtk-go-back</property>
- <signal name="clicked" handler="toolbar_prev_frame_clicked_callback" swapped="no"/>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="toolbar-play">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Continue sequencing</property>
- <property name="use_underline">True</property>
- <property name="stock_id">gtk-media-play</property>
- <signal name="clicked" handler="toolbar_play_clicked_callback" swapped="no"/>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="toolbar-pause">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Pause sequencing</property>
- <property name="use_underline">True</property>
- <property name="stock_id">gtk-media-pause</property>
- <signal name="clicked" handler="toolbar_pause_clicked_callback" swapped="no"/>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="toolbar-next-frame">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Next frame</property>
- <property name="use_underline">True</property>
- <property name="stock_id">gtk-go-forward</property>
- <signal name="clicked" handler="toolbar_next_frame_clicked_callback" swapped="no"/>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="toolbar-to-last">
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Last frame</property>
- <property name="use_underline">True</property>
- <property name="stock_id">gtk-goto-last</property>
- <signal name="clicked" handler="toolbar_to_last_clicked_callback" swapped="no"/>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="resize">False</property>
- <property name="shrink">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkSourceView" id="source_view">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="left_margin">2</property>
- <property name="right_margin">2</property>
- <property name="show_line_numbers">True</property>
- <property name="tab_width">4</property>
- <property name="auto_indent">True</property>
- <property name="highlight_current_line">True</property>
- <property name="indent_on_tab">False</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="resize">True</property>
- <property name="shrink">True</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- <object class="GtkAboutDialog" id="about_dialog">
- <property name="can_focus">False</property>
- <property name="border_width">5</property>
- <property name="window_position">center</property>
- <property name="icon">icon.svg</property>
- <property name="type_hint">dialog</property>
- <property name="transient_for">win_main</property>
- <property name="program_name">Sprite Animation Editor</property>
- <property name="version">⍺0.1.1</property>
- <property name="copyright" translatable="yes">Copyleft ↄ Vasily_Makarov 2011-2015</property>
- <property name="comments" translatable="yes">A simple tool to edit eAthena-based XML animation files</property>
- <property name="authors">Vasily_Makarov &lt;danilka.pro@gmail.com&gt;
-Reid Yaro &lt;reidyaro@gmail.com&gt;</property>
- <property name="logo">icon.svg</property>
- <property name="license_type">gpl-2-0</property>
- <child internal-child="vbox">
- <object class="GtkBox" id="dialog-vbox3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkButtonBox" id="dialog-action_area3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="layout_style">end</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- </child>
- </object>
- <object class="GtkDialog" id="find_dialog">
- <property name="width_request">240</property>
- <property name="height_request">80</property>
- <property name="can_focus">False</property>
- <property name="border_width">5</property>
- <property name="title" translatable="yes">Find</property>
- <property name="resizable">False</property>
- <property name="type_hint">normal</property>
- <property name="skip_taskbar_hint">True</property>
- <property name="skip_pager_hint">True</property>
- <property name="transient_for">win_main</property>
- <signal name="response" handler="search_find_dialog_response_callback" object="find_dialog_entry" swapped="no"/>
- <child internal-child="vbox">
- <object class="GtkBox" id="dialog-vbox4">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkButtonBox" id="dialog-action_area4">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="button4">
- <property name="label">gtk-find</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="button5">
- <property name="label">gtk-cancel</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="find_dialog_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">•</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-3">button4</action-widget>
- <action-widget response="-6">button5</action-widget>
- </action-widgets>
- </object>
- <object class="GtkListStore" id="liststore1">
- <columns>
- <!-- column-name text -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkListStore" id="liststore2">
- <columns>
- <!-- column-name text -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkListStore" id="liststore3">
- <columns>
- <!-- column-name text -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkMessageDialog" id="parsing-error-dialog">
- <property name="can_focus">False</property>
- <property name="border_width">5</property>
- <property name="title" translatable="yes">Parsing error</property>
- <property name="type_hint">dialog</property>
- <property name="skip_taskbar_hint">True</property>
- <property name="skip_pager_hint">True</property>
- <property name="message_type">error</property>
- <property name="buttons">close</property>
- <property name="text" translatable="yes">&lt;b&gt;Bad source buffer!&lt;/b&gt;</property>
- <property name="use_markup">True</property>
- <signal name="response" handler="gtk_widget_hide" swapped="no"/>
- <child internal-child="vbox">
- <object class="GtkBox" id="messagedialog-vbox">
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">14</property>
- <child internal-child="action_area">
- <object class="GtkButtonBox" id="messagedialog-action_area">
- <property name="can_focus">False</property>
- <property name="layout_style">end</property>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
-</interface>
diff --git a/saedit/icon.svg b/saedit/logo.svg
index 6a3bf35..a68864a 100644
--- a/saedit/icon.svg
+++ b/saedit/logo.svg
@@ -14,8 +14,8 @@
height="256"
id="svg2"
version="1.1"
- inkscape:version="0.48.2 r9819"
- sodipodi:docname="icon.svg">
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="logo.svg">
<defs
id="defs4">
<linearGradient
@@ -585,16 +585,16 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="1"
- inkscape:cx="-79.999984"
- inkscape:cy="276.83172"
+ inkscape:zoom="1.4142136"
+ inkscape:cx="33.799066"
+ inkscape:cy="114.6248"
inkscape:document-units="px"
- inkscape:current-layer="layer1"
+ inkscape:current-layer="g3107"
showgrid="false"
- inkscape:window-width="1680"
- inkscape:window-height="1001"
+ inkscape:window-width="1366"
+ inkscape:window-height="704"
inkscape:window-x="0"
- inkscape:window-y="20"
+ inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
@@ -604,6 +604,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@@ -627,17 +628,19 @@
<path
inkscape:connector-curvature="0"
id="path3105"
- style="fill:#800000;font-family:Sans;-inkscape-font-specification:Sans"
- d="m 110.87616,841.71545 0,3.39844 c -1.01564,-0.52081 -2.07033,-0.91144 -3.16406,-1.17187 -1.09377,-0.2604 -2.22658,-0.39061 -3.39844,-0.39063 -1.78386,2e-5 -3.12501,0.27346 -4.02344,0.82031 -0.885421,0.5469 -1.328129,1.36721 -1.328123,2.46094 -6e-6,0.83335 0.319004,1.4909 0.957031,1.97266 0.638012,0.46876 1.920562,0.91798 3.847652,1.34765 l 1.23047,0.27344 c 2.55207,0.54689 4.36197,1.32163 5.42969,2.32422 1.08071,0.98959 1.62108,2.37631 1.62109,4.16016 -1e-5,2.03125 -0.80731,3.63932 -2.42187,4.82422 -1.60158,1.18489 -3.80861,1.77734 -6.62109,1.77734 -1.17189,0 -2.39584,-0.11719 -3.671879,-0.35156 -1.263026,-0.22136 -2.59766,-0.5599 -4.003906,-1.01563 l 0,-3.71094 c 1.328121,0.69011 2.636714,1.21094 3.925781,1.5625 1.289054,0.33855 2.565094,0.50782 3.828124,0.50782 1.6927,0 2.99478,-0.28646 3.90625,-0.85938 0.91144,-0.58593 1.36717,-1.40624 1.36719,-2.46094 -2e-5,-0.97655 -0.33205,-1.72525 -0.9961,-2.24609 -0.65105,-0.52082 -2.08985,-1.02213 -4.3164,-1.50391 l -1.25,-0.29296 c -2.22657,-0.46874 -3.834642,-1.18489 -4.82422,-2.14844 -0.989586,-0.97655 -1.484378,-2.31119 -1.484375,-4.00391 -3e-6,-2.05727 0.729163,-3.64581 2.1875,-4.76562 1.458327,-1.11977 3.528635,-1.67967 6.210935,-1.67969 1.32812,2e-5 2.57811,0.0977 3.75,0.29297 1.17186,0.19533 2.25259,0.4883 3.24219,0.8789" />
+ style="font-family:Sans;-inkscape-font-specification:Sans;fill:#800000"
+ d="m 110.87616,841.71545 v 3.39844 c -1.01564,-0.52081 -2.07033,-0.91144 -3.16406,-1.17187 -1.09377,-0.2604 -2.22658,-0.39061 -3.39844,-0.39063 -1.78386,2e-5 -3.12501,0.27346 -4.02344,0.82031 -0.885421,0.5469 -1.328129,1.36721 -1.328123,2.46094 -6e-6,0.83335 0.319004,1.4909 0.957031,1.97266 0.638012,0.46876 1.920562,0.91798 3.847652,1.34765 l 1.23047,0.27344 c 2.55207,0.54689 4.36197,1.32163 5.42969,2.32422 1.08071,0.98959 1.62108,2.37631 1.62109,4.16016 -1e-5,2.03125 -0.80731,3.63932 -2.42187,4.82422 -1.60158,1.18489 -3.80861,1.77734 -6.62109,1.77734 -1.17189,0 -2.39584,-0.11719 -3.671879,-0.35156 -1.263026,-0.22136 -2.59766,-0.5599 -4.003906,-1.01563 l -1.611767,-1.9132 c 1.328121,0.69011 1.846328,-1.14472 5.537548,-0.23524 1.289054,0.33855 2.565094,0.50782 3.828124,0.50782 1.6927,0 2.99478,-0.28646 3.90625,-0.85938 0.91144,-0.58593 1.36717,-1.40624 1.36719,-2.46094 -2e-5,-0.97655 -0.33205,-1.72525 -0.9961,-2.24609 -0.65105,-0.52082 -2.08985,-1.02213 -4.3164,-1.50391 l -1.25,-0.29296 c -2.22657,-0.46874 -3.834642,-1.18489 -4.82422,-2.14844 -0.989586,-0.97655 -1.484378,-2.31119 -1.484375,-4.00391 -3e-6,-2.05727 0.729163,-3.64581 2.1875,-4.76562 1.458327,-1.11977 3.528635,-1.67967 6.210935,-1.67969 1.32812,2e-5 2.57811,0.0977 3.75,0.29297 1.17186,0.19533 2.25259,0.4883 3.24219,0.8789"
+ sodipodi:nodetypes="ccccccccccccsccccsccccccccccc" />
</g>
<g
id="text3090"
- style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#800000;fill-opacity:1;stroke:none;font-family:Cantarell;-inkscape-font-specification:Sans">
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:125%;font-family:Cantarell;-inkscape-font-specification:Sans;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#800000;fill-opacity:1;stroke:none">
<path
id="path3102"
- style="fill:#800000;font-family:Sans;-inkscape-font-specification:Sans"
- d="m 97.149544,871.06958 c -2.903657,10e-6 -4.915374,0.33204 -6.035156,0.99609 -1.119799,0.66408 -1.679694,1.79689 -1.679688,3.39844 -6e-6,1.27605 0.41666,2.29167 1.25,3.04688 0.846346,0.74219 1.992178,1.11328 3.4375,1.11328 1.992175,0 3.587225,-0.70312 4.785156,-2.10938 1.210924,-1.41926 1.816394,-3.30077 1.816404,-5.64453 l 0,-0.80078 -3.574216,0 m 7.167966,-1.48437 0,12.48046 -3.59375,0 0,-3.32031 c -0.820326,1.32813 -1.842461,2.3112 -3.066404,2.94922 -1.223971,0.625 -2.721365,0.9375 -4.492187,0.9375 -2.239591,0 -4.023443,-0.625 -5.351563,-1.875 -1.315107,-1.26302 -1.972658,-2.94921 -1.972656,-5.05859 -2e-6,-2.46093 0.820309,-4.3164 2.460938,-5.56641 1.653639,-1.24999 4.114574,-1.87499 7.382812,-1.875 l 5.03906,0 0,-0.35156 c -1e-5,-1.65363 -0.54689,-2.92967 -1.640622,-3.82813 -1.080744,-0.91144 -2.60418,-1.36717 -4.570313,-1.36718 -1.25001,1e-5 -2.467457,0.14975 -3.652344,0.44921 -1.184902,0.2995 -2.324224,0.74872 -3.417968,1.34766 l 0,-3.32031 c 1.315098,-0.50779 2.591139,-0.8854 3.828125,-1.13281 1.23697,-0.2604 2.441395,-0.39061 3.613281,-0.39063 3.164048,2e-5 5.527331,0.82033 7.089841,2.46094 1.56248,1.64064 2.34373,4.12762 2.34375,7.46094"
- inkscape:connector-curvature="0" />
+ style="font-family:Sans;-inkscape-font-specification:Sans;fill:#800000"
+ d="m 97.149544,871.06958 c -2.903657,10e-6 -4.915374,0.33204 -6.035156,0.99609 -1.119799,0.66408 -1.679694,1.79689 -1.679688,3.39844 -6e-6,1.27605 0.41666,2.29167 1.25,3.04688 0.846346,0.74219 1.992178,1.11328 3.4375,1.11328 1.992175,0 3.587225,-0.70312 4.785156,-2.10938 1.210924,-1.41926 1.816394,-3.30077 1.816404,-5.64453 v -0.80078 h -3.574216 m 7.167966,-1.48437 v 12.48046 h -3.59375 v -3.32031 c -0.820326,1.32813 -1.842461,2.3112 -3.066404,2.94922 -1.223971,0.625 -2.721365,0.9375 -4.492187,0.9375 -2.239591,0 -4.023443,-0.625 -5.351563,-1.875 -1.315107,-1.26302 -1.972658,-2.94921 -1.972656,-5.05859 -2e-6,-2.46093 0.820309,-4.3164 2.460938,-5.56641 1.653639,-1.24999 4.114574,-1.87499 7.382812,-1.875 h 5.03906 v -0.35156 c -1e-5,-1.65363 -0.54689,-2.92967 -1.640622,-3.82813 -1.080744,-0.91144 -2.60418,-1.36717 -4.570313,-1.36718 -1.25001,1e-5 -2.467457,0.14975 -3.652344,0.44921 -1.184902,0.2995 -2.324224,0.74872 -3.417968,1.34766 v -3.32031 c 1.315098,-0.50779 2.591139,-0.8854 3.828125,-1.13281 1.23697,-0.2604 2.441395,-0.39061 3.613281,-0.39063 3.180647,-0.20787 5.527331,0.82033 7.089841,2.46094 1.56248,1.64064 2.34373,4.12762 2.34375,7.46094"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccscccccccccsccccccccccccccc" />
</g>
<g
id="text3094"
diff --git a/saedit/main.c b/saedit/main.c
index d6c147a..103e0c4 100644
--- a/saedit/main.c
+++ b/saedit/main.c
@@ -1,609 +1,235 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
+#include <gtksourceview/gtksource.h>
+#include "xml.h"
+#include "config.h"
+#include "spritedrawingarea.h"
+#include "action.h"
+#include "errors.h"
+#include "file.h"
+#include "buffer.h"
+#include "xmlsetup.h"
+#include "drawfuncs.h"
#include "main.h"
-#include "interface.c"
-
-//Cairo functions
-
-cairo_surface_t *get_grid_surface(int w, int h) {
- cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, (w + 2) * GRID_SIZE, (h + 2) * GRID_SIZE);
- cairo_t *cr = cairo_create(surface);
- int x, y;
-
- for (x = 0; x < w; x++)
- for (y = 0; y < h; y++) {
- gdk_cairo_set_source_pixbuf(cr, gen_sae_info->ground, x * GRID_SIZE, y * GRID_SIZE);
- cairo_paint(cr);
- }
-
- if (config->show_grid) {
- cairo_surface_t *gridsurf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w * GRID_SIZE + 1, h * GRID_SIZE + 1);
- cairo_t *scr = cairo_create(gridsurf);
- cairo_set_line_width(scr, 1);
- cairo_set_source_rgba(scr, 0.5, 0.5, 0.5, 1);
- for (x = 0; x < w; x++)
- for (y = 0; y < h; y++) {
- cairo_rectangle(scr, x*GRID_SIZE+0.5, y*GRID_SIZE+0.5, GRID_SIZE, GRID_SIZE);
- cairo_stroke(scr);
- }
- cairo_set_source_surface(cr, gridsurf, GRID_SIZE, GRID_SIZE);
- cairo_paint(cr);
- cairo_destroy(scr);
- cairo_surface_destroy(gridsurf);
+#include <string.h>
+
+void
+intr_updated (Interactor *interactor) {
+ gtk_widget_queue_draw (d_area);
+
+ gtk_tool_button_set_icon_name (
+ GTK_TOOL_BUTTON (tbtn_play),
+ interactor_loop_running (interactor) ?
+ "media-playback-pause" :
+ "media-playback-start"
+ );
+
+ if (interactor != NULL) {
+ gboolean result;
+ gint hp;
+ gchar *id, *name;
+
+ buffer_mark_line (
+ interactor_get_line_no (interactor) - 1
+ );
+
+ result = interactor_get_action_hp_and_name (
+ interactor,
+ &hp, &name
+ );
+
+ if (!result)
+ return;
+
+ id = get_action_id (hp, name);
+ gtk_combo_box_set_active_id (cb_actions, id);
+ g_free (id);
+
+ gtk_combo_box_set_active_id (
+ GTK_COMBO_BOX (cb_directions),
+ interactor_get_animation_direction (interactor)
+ );
+ } else
+ buffer_mark_line (-1);
+}
+
+void
+release_context () {
+ xml_setup_clear ();
+
+ if (interactor != NULL) {
+ interactor_free (interactor);
+ interactor = NULL;
+ intr_updated (interactor);
}
- cairo_destroy(cr);
- return surface;
-}
-
-gboolean darea_draw_event(GtkWidget *widget, cairo_t *cr, SAEInfo *sae_info) {
-
- if (sae_info == NULL)
- sae_info = gen_sae_info;
-
- int width = gtk_widget_get_allocated_width(widget),
- height = gtk_widget_get_allocated_height(widget);
+ gtk_combo_box_text_remove_all (cb_directions);
+ gtk_list_store_clear (store_actions);
- int w = 3, h = 3;
-
- //cairo_t *cr = gdk_cairo_create(gtk_widget_get_parent_window(widget));
-
- cairo_surface_t *surface = get_grid_surface(w, h);
- cairo_set_source_surface(cr, surface, width/2 - GRID_SIZE * (w + 2) * 0.5, height/2 - GRID_SIZE * (h + 2) * 0.5);
- cairo_paint(cr);
-
- if (player != NULL) {
- GdkPixbuf *pbuf = player->sprite->pixbuf;
- if (pbuf == NULL) return FALSE;
- gdk_cairo_set_source_pixbuf(cr, pbuf,
- width/2 - player->imageset->width/2 + player->offsetX + player->sprite->offsetX + player->imageset->offsetX,
- height/2 + GRID_SIZE/2 - player->imageset->height + player->offsetY + player->sprite->offsetY + player->imageset->offsetY);
- cairo_paint(cr);
+ if (context != NULL) {
+ sprite_context_free (context);
+ context = NULL;
}
-
- GdkPixbuf *pbuf = sae_info->sprite->pixbuf;
- if (pbuf == NULL) return FALSE;
- gdk_cairo_set_source_pixbuf(cr, pbuf,
- width/2 - sae_info->imageset->width/2 + sae_info->offsetX + sae_info->sprite->offsetX + sae_info->imageset->offsetX,
- height/2 +GRID_SIZE/2 - sae_info->imageset->height + sae_info->offsetY + sae_info->sprite->offsetY + sae_info->imageset->offsetY);
- cairo_paint(cr);
-
- cairo_surface_destroy(surface);
- return FALSE;
-}
-
-//Common functions
-
-gchar *markup_bold(gchar *str) {
- return g_strconcat("<b>", str, "</b>", NULL);
-}
-
-void format_src_string(gchar *src) {
- gchar *str = g_strrstr(src, "|");
- if (str == NULL) return;
- strncpy(str, "\0", 1);
-}
-
-GtkTextIter *gtk_source_buffer_highlight_line(GtkSourceBuffer *buffer, int line_number) {
- GtkTextIter start;
- gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(buffer), &start);
- gtk_text_iter_set_line(&start, line_number);
- gtk_text_buffer_place_cursor(GTK_TEXT_BUFFER(buffer), &start);
- return gtk_text_iter_copy(&start);
}
-//File working
-
-void open_xml_file(GtkButton *button) {
- gtk_widget_set_sensitive(xml_file_open_button, TRUE);
- gtk_widget_set_sensitive(reload_menu_item, TRUE);
- gchar *buf;
- size_t len;
- g_file_get_contents(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(xml_file_chooser_button)), &buf, &len, NULL);
- if (g_utf8_validate(buf, len, NULL)) {
- gtk_text_buffer_set_text(GTK_TEXT_BUFFER(source_buffer), buf, len);
- gtk_widget_set_sensitive(xml_file_save_button, TRUE);
+void
+update_window_title () {
+ gchar *title, *data_folder, *file_name;
+ const gchar *opened_file_name;
+
+ gboolean modified = buffer_get_modified ();
+ data_folder = config_keys_get_data_folder_path ();
+ opened_file_name = get_opened_file_name ();
+
+ if (
+ opened_file_name != NULL &&
+ g_str_has_prefix (opened_file_name, data_folder)
+ ) {
+ file_name = g_strconcat (
+ "<Data Folder>",
+ opened_file_name + strlen (data_folder),
+ NULL
+ );
} else {
- gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(xml_file_chooser_button));
- }
-
- free_current_info();
-}
-
-void save_to_xml_file(gchar *filename) {
- GtkTextIter start, end;
- gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(source_buffer), &start);
- gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(source_buffer), &end);
- g_file_set_contents(filename, gtk_text_buffer_get_text(GTK_TEXT_BUFFER(source_buffer), &start, &end, TRUE), -1, NULL);
-}
-
-//SAEInfo functions (must be ported to sae.c)
-
-void free_imagesets(SAEInfo *sae_info) {
- free_imageset(sae_info);
- sae_info->imagesets = NULL;
- gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(sae_info->imagesets_combo_box))));
-}
-
-void free_imageset(SAEInfo *sae_info) {
- sae_info->imageset = imageset_new();
- sae_info->ground = sae_info_ground_new();
- gtk_widget_set_sensitive(imageset_preview_menu_item, FALSE);
- gtk_widget_set_sensitive(toolbar, FALSE);
-}
-
-void free_actions(SAEInfo *sae_info) {
- sae_info->actions = NULL;
- if (sae_info->actions_combo_box != NULL)
- gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(sae_info->actions_combo_box))));
-}
-
-void free_animations(SAEInfo *sae_info) {
- sae_info->animations = NULL;
-
- if (sae_info->animations_combo_box != NULL)
- gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(sae_info->animations_combo_box))));
-
- kill_timeout(sae_info->anim_tag);
- sae_info->anim_tag = 0;
- sae_info->sprite = frame_new(-1, 0, 0, 0);
- set_sprite_by_index(0, sae_info);
-}
-
-void free_lists(SAEInfo *sae_info) {
- free_imagesets(sae_info);
- free_actions(sae_info);
- free_animations(sae_info);
-}
-
-void free_current_info() {
- free_lists(gen_sae_info);
- player = NULL;
-}
-
-//Callbacks
-
-void data_folder_set_callback(GtkFileChooserButton *widget, gpointer data) {
- config->clientdata_folder = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(data_folder_chooser_button));
- gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(xml_file_chooser_button), gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)));
-}
-
-void show_grid_menu_item_toggled_callback(GtkCheckMenuItem *checkmenuitem, gpointer user_data) {
- config->show_grid = gtk_check_menu_item_get_active(checkmenuitem);
- gtk_widget_queue_draw(darea);
-}
-
-void parsing_error_warning(SAEInfo *sae_info, const gchar *message) {
- free_lists(sae_info);
- gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(parsing_error_dialog), "%s", message);
- gtk_dialog_run(GTK_DIALOG(parsing_error_dialog));
-}
-
-void actions_combo_box_changed_callback(GtkComboBoxText *widget, gpointer user_data) {
- if (player != NULL)
- set_up_action_by_name(gtk_combo_box_text_get_active_text(widget), player);
- set_up_action_by_name(gtk_combo_box_text_get_active_text(widget), gen_sae_info);
-}
-
-void animations_combo_box_changed_callback(GtkComboBoxText *widget, gpointer user_data) {
- set_up_animation_by_direction(gen_sae_info, gtk_combo_box_text_get_active_text(widget));
- if (player != NULL) {
- set_up_animation_by_direction(player, gtk_combo_box_text_get_active_text(widget));
- show_animation(player);
- }
- show_animation(gen_sae_info);
-}
-
-void imagesets_combo_box_changed_callback(GtkComboBoxText *widget, gpointer user_data) {
- if (gtk_combo_box_text_get_active_text(widget) != NULL)
- set_up_imageset_by_name(gtk_combo_box_text_get_active_text(widget), gen_sae_info);
-}
-
-gboolean frame_image_button_press_event_callback(GtkWidget *widget, GdkEventButton *button, int idx) {
- gchar buf[10];
- gint len = g_sprintf(buf, "%d", idx);
- gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(source_buffer), buf, len);
- return FALSE;
-}
-
-void open_menu_item_activate_callback(GtkMenuItem *menuitem, GtkFileChooserDialog *fcdialog) {
- gtk_dialog_run(GTK_DIALOG(fcdialog));
-}
-
-//>Toolbar callbacks
-void toolbar_to_first_clicked_callback(GtkToolButton *toolbutton, gpointer user_data) {
- toolbar_pause_clicked_callback(toolbutton, user_data);
- gen_sae_info->animation = g_list_first(gen_sae_info->animation);
- show_sprite(gen_sae_info);
-}
-
-void toolbar_prev_frame_clicked_callback(GtkToolButton *toolbutton, gpointer user_data) {
- toolbar_pause_clicked_callback(toolbutton, user_data);
- GList *prev = g_list_previous(gen_sae_info->animation);
- if (prev == NULL)
- return;
- gen_sae_info->animation = prev;
- show_sprite(gen_sae_info);
-}
-
-void toolbar_play_clicked_callback(GtkToolButton *toolbutton, gpointer user_data) {
- show_animation(gen_sae_info);
-}
-
-void toolbar_pause_clicked_callback(GtkToolButton *toolbutton, gpointer user_data) {
- int tag = gen_sae_info->anim_tag;
- if (tag > 0) {
- kill_timeout(tag);
- GList *prev = g_list_previous(gen_sae_info->animation);
- if (prev == NULL)
- prev = g_list_last(gen_sae_info->animation);
- gen_sae_info->animation = prev;
- }
- gen_sae_info->anim_tag = 0;
-}
-
-void toolbar_next_frame_clicked_callback(GtkToolButton *toolbutton, gpointer user_data) {
- toolbar_pause_clicked_callback(toolbutton, user_data);
- GList *next = g_list_next(gen_sae_info->animation);
- if (next == NULL)
- return;
- gen_sae_info->animation = next;
- show_sprite(gen_sae_info);
-}
-
-void toolbar_to_last_clicked_callback(GtkToolButton *toolbutton, gpointer user_data) {
- toolbar_pause_clicked_callback(toolbutton, user_data);
- gen_sae_info->animation = g_list_last(gen_sae_info->animation);
- show_sprite(gen_sae_info);
-}
-
-//Dialogs
-
-void show_about_dialog() {
- gtk_dialog_run(GTK_DIALOG(about_dialog));
- gtk_widget_hide(about_dialog);
-}
-
-void show_imageset_dialog() {
- if (gen_sae_info->imageset->spriteset == NULL) return;
- GtkWidget *dialog = gtk_dialog_new();
- GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
- gtk_window_set_title(GTK_WINDOW(dialog), _("Imageset preview"));
- gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(win));
-
- int w = gen_sae_info->imageset->spriteset_width / gen_sae_info->imageset->width;
- int h = gen_sae_info->imageset->spriteset_height / gen_sae_info->imageset->height;
-
- GtkWidget *hbox = NULL;
- GtkWidget *image = NULL;
- GtkWidget *event_box = NULL;
-
- int x, y;
- for (y = 0; y < h; y++) {
- hbox = gtk_hbox_new(TRUE, 2);
- gtk_box_pack_start(GTK_BOX(content_area), hbox, TRUE, TRUE, 2);
- for (x = 0; x < w; x++) {
- unsigned long int id = w * y + x;
- event_box = gtk_event_box_new();
- g_signal_connect(G_OBJECT(event_box), "button-press-event", G_CALLBACK(frame_image_button_press_event_callback), (gpointer)id);
- gtk_box_pack_start(GTK_BOX(hbox), event_box, TRUE, TRUE, 0);
-
- image = gtk_image_new_from_pixbuf(get_sprite_by_index(w * y + x, gen_sae_info));
- gtk_widget_add_events(image, GDK_BUTTON_PRESS_MASK);
- gtk_container_add(GTK_CONTAINER(event_box), image);
- }
- }
- gtk_widget_show_all(dialog);
-}
-
-//Main functions
-
-void set_sprite_by_index(size_t idx, SAEInfo *sae_info) {
- sae_info->sprite->pixbuf = get_sprite_by_index(idx, sae_info);
- gtk_widget_queue_draw(darea);
-}
-
-void set_up_actions_by_imageset_name(gchar *imageset_name, SAEInfo *sae_info) {
- free_actions(sae_info);
- free_animations(sae_info);
- GList *_actions_list = NULL;
- GList *list = sae_info->root->sub_nodes;
- XMLNode *node = NULL;
- while (TRUE) {
- list = g_list_find_custom(list, imageset_name, xml_node_compare_with_action_node_by_imageset_name_func);
- if (list == NULL)
- break;
- if (_actions_list == NULL) {
- _actions_list = g_list_alloc();
- sae_info->actions = _actions_list;
- _actions_list->data = list->data;
- } else
- _actions_list = g_list_append(_actions_list, list->data);
- node = list->data;
- if (sae_info->actions_combo_box != NULL)
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(sae_info->actions_combo_box), xml_node_get_attr_value(node, "name"));
- list = g_list_next(list);
- }
- if (sae_info->actions_combo_box != NULL)
- gtk_combo_box_set_active(GTK_COMBO_BOX(sae_info->actions_combo_box), 0);
-}
-
-gboolean set_up_imagesets(SAEInfo *sae_info) {
- GList *_imagesets_list = NULL;
- free_lists(sae_info);
- GList *list = sae_info->root->sub_nodes;
- XMLNode *node = NULL;
- while (TRUE) {
- list = g_list_find_custom(list, "imageset", xml_node_compare_with_name_func);
- if (list == NULL)
- break;
- if (_imagesets_list == NULL) {
- _imagesets_list = g_list_alloc();
- _imagesets_list->data = list->data;
- sae_info->imagesets = _imagesets_list;
- } else
- _imagesets_list = g_list_append(_imagesets_list, list->data);
- node = list->data;
- if (sae_info->imagesets_combo_box != NULL)
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(sae_info->imagesets_combo_box), xml_node_get_attr_value(node, "name"));
- list = g_list_next(list);
- }
- if (_imagesets_list == NULL)
- return FALSE;
- if (sae_info->imagesets_combo_box != NULL)
- gtk_combo_box_set_active(GTK_COMBO_BOX(sae_info->imagesets_combo_box), 0);
- return TRUE;
-}
-
-void show_sprite(SAEInfo *sae_info) {
- Frame *sprite = sae_info->animation->data;
- gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(source_view),
- gtk_source_buffer_highlight_line(source_buffer, sprite->line_number-1),
- 0.25,
- FALSE,
- 0.0,
- 0.0);
- sae_info->sprite = sprite;
- gtk_widget_queue_draw(darea);
-}
-
-void show_animation(SAEInfo *sae_info) {
- kill_timeout(sae_info->anim_tag);
- if (sae_info->animation == NULL)
- return;
-
- show_sprite(sae_info);
- Frame *sprite = sae_info->animation->data;
-
- GList *next = g_list_next(sae_info->animation);
- if (next == NULL)
- next = g_list_first(sae_info->animation);
-
- if (!sprite->delay)
- {
- free_animations(sae_info);
- return;
- }
- sae_info->animation = next;
- sae_info->anim_tag = g_timeout_add(sprite->delay, (GSourceFunc)show_animation, sae_info);
-}
-
-gboolean show_general_animation(SAEInfo *sae_info) {
- XMLNode *node = sae_info->animations->data;
- if (node == NULL)
- return FALSE;
- animations_combo_box_changed_callback(NULL, NULL);
- return TRUE;
-}
-
-gboolean set_up_action_by_name(const gchar *name, SAEInfo *sae_info) {
- free_animations(sae_info);
- GList *list = g_list_find_custom(sae_info->actions,
- xml_attr_new("name", name),
- (GCompareFunc)xml_node_compare_with_attr_func);
- if (list == NULL) return FALSE;
- list = ((XMLNode *)list->data)->sub_nodes;
- gboolean was_direction = FALSE;
- while (TRUE) {
- list = g_list_find_custom(list, "animation", xml_node_compare_with_name_func);
- if (list == NULL)
- break;
- if (sae_info->animations == NULL) {
- sae_info->animations = g_list_alloc();
- sae_info->animations->data = list->data;
- } else
- sae_info->animations = g_list_append(sae_info->animations, list->data);
- XMLNode *node = list->data;
- gchar *direction = xml_node_get_attr_value(node, "direction");
- if (direction != NULL) {
- if (sae_info->animations_combo_box != NULL)
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(sae_info->animations_combo_box), direction);
- was_direction = TRUE;
- }
- list = g_list_next(list);
- }
- if (sae_info->animations == NULL)
- return FALSE;
- if (!was_direction)
- show_general_animation(sae_info);
- else if (sae_info->animations_combo_box != NULL)
- gtk_combo_box_set_active(GTK_COMBO_BOX(sae_info->animations_combo_box), 0);
- return TRUE;
-}
-
-void set_up_imageset_by_name(const gchar *name, SAEInfo *sae_info) {
- free_imageset(sae_info);
- free_actions(sae_info);
- free_animations(sae_info);
-
- GList *list = g_list_find_custom(sae_info->imagesets,
- xml_attr_new("name", name),
- (GCompareFunc)xml_node_compare_with_attr_func);
-
- if (list == NULL)
- return;
-
- XMLNode *node = list->data;
- if (node == NULL)
- return;
-
- sae_info->imageset->node = node;
- sae_info->imageset->offsetX = 0;
- sae_info->imageset->offsetY = 0;
-
- gchar *offset_attr = xml_node_get_attr_value(sae_info->imageset->node, "offsetX");
- if (offset_attr != NULL)
- sscanf(offset_attr, "%d", &sae_info->imageset->offsetX);
-
- offset_attr = xml_node_get_attr_value(sae_info->imageset->node, "offsetY");
- if (offset_attr != NULL)
- sscanf(offset_attr, "%d", &sae_info->imageset->offsetY);
-
- gchar *imageset_name = xml_node_get_attr_value(sae_info->imageset->node, "name");
-
- gchar *src = xml_node_get_attr_value(sae_info->imageset->node, "src");
- format_src_string(src);
- gchar *datapath = config->clientdata_folder;
- gchar *path = g_strjoin(SEPARATOR_SLASH, datapath, src, NULL);
-
- sae_info->imageset->spriteset = gdk_pixbuf_new_from_file(path, NULL);
- if (sae_info->imageset->spriteset == NULL) {
- parsing_error_warning(sae_info, "Wrong spriteset path!");
- return;
+ file_name = g_strdup (opened_file_name);
}
- gtk_widget_set_sensitive(imageset_preview_menu_item, TRUE);
- gtk_widget_set_sensitive(toolbar, TRUE);
- sae_info->imageset->spriteset_width = gdk_pixbuf_get_width(sae_info->imageset->spriteset);
- sae_info->imageset->spriteset_height = gdk_pixbuf_get_height(sae_info->imageset->spriteset);
-
- gchar *width = xml_node_get_attr_value(sae_info->imageset->node, "width");
- sscanf(width, "%d", &sae_info->imageset->width);
- gchar *height = xml_node_get_attr_value(sae_info->imageset->node, "height");
- sscanf(height, "%d", &sae_info->imageset->height);
-
- list = g_list_find_custom(sae_info->root->sub_nodes, "sae", xml_node_compare_with_name_func);
- if (list != NULL) {
- gchar *ground_attr = xml_node_get_attr_value((XMLNode *)list->data, "ground");
- if (ground_attr != NULL) {
- ground_attr = g_strjoin(NULL, DIR_GROUNDS, SEPARATOR_SLASH, ground_attr, ".png", NULL);
- GdkPixbuf *pbuf = gdk_pixbuf_new_from_file(ground_attr, NULL);
- if(pbuf != NULL)
- sae_info->ground = pbuf;
- }
- gchar *player_attr = xml_node_get_attr_value((XMLNode *)list->data, "player");
- if (player_attr != NULL && player == NULL) {
- gchar *text;
- gchar *sprites_path = paths->sprites;
- gchar *player_file = g_strjoin(NULL, sprites_path, DIR_PLAYERS, player_attr, ".xml", NULL);
- if (g_file_get_contents(player_file, &text, NULL, NULL)) {
- player = sae_info_new();
- parse_xml_text(text, player);
- set_up_imageset_by_name("base", player);
- }
- }
+ if (file_name == NULL) {
+ file_name = g_strdup ("New animation");
}
- set_up_actions_by_imageset_name(imageset_name, sae_info);
- if (sae_info->actions == NULL)
- return;
- set_sprite_by_index(0, sae_info);
-}
-
-void load_options() {
- gchar *datapath = config->clientdata_folder;
- gchar *path = g_strjoin(SEPARATOR_SLASH, datapath, "paths.xml", NULL);
- config_options_load_from_file(paths, path, datapath);
-}
-
-void parse_xml_text(gchar *text, SAEInfo *sae_info) {
- GError *error = NULL;
- free_lists(sae_info);
+ title = g_strconcat (
+ modified ? "*" : "",
+ file_name,
+ NULL
+ );
+
+ gtk_window_set_title (GTK_WINDOW (main_window), title);
+ g_free (title);
+ g_free (file_name);
+}
+
+void
+setup_source_view (GtkSourceView *source_view) {
+ GtkSourceLanguageManager *langman;
+ GtkSourceMarkAttributes *attrs;
+
+ langman = gtk_source_language_manager_get_default();
+
+ gtk_source_buffer_set_language (
+ GTK_SOURCE_BUFFER (
+ gtk_text_view_get_buffer (
+ GTK_TEXT_VIEW (source_view)
+ )
+ ),
+ gtk_source_language_manager_get_language (langman, "xml")
+ );
+
+ attrs = gtk_source_mark_attributes_new ();
+ gtk_source_mark_attributes_set_icon_name (attrs, "media-record");
+ gtk_source_view_set_mark_attributes (
+ source_view, "active-line", attrs, 0
+ );
+
+ gtk_widget_show_all (GTK_WIDGET (source_view));
+}
+
+GtkWidget *
+gtk_builder_get_widget (
+ GtkBuilder *builder,
+ const gchar *name
+) {
+ return GTK_WIDGET (
+ gtk_builder_get_object (builder, name)
+ );
+}
+
+gboolean
+window_main_delete_event_cb (
+ GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data
+) {
+ return show_unsaved_changes_dialog ();
+}
+
+int
+main (int argc, char *argv[]) {
+ GtkBuilder *builder;
+ gchar *path;
+
+ gtk_init (&argc, &argv);
+
+ builder = gtk_builder_new ();
+ gtk_builder_add_from_file (builder, "iface.ui", NULL);
+ gtk_builder_connect_signals (builder, NULL);
+
+ main_window = gtk_builder_get_widget (builder, "window-main");
+ tf_view = gtk_builder_get_widget (builder, "treefolderview-main");
+ d_area = gtk_builder_get_widget (builder, "drawingarea-main");
+ source_view = gtk_builder_get_widget (builder, "sourceview-main");
+ tbtn_play = gtk_builder_get_widget (builder, "button-anim-play");
+
+ cb_actions = GTK_COMBO_BOX (
+ gtk_builder_get_widget (builder, "cbox-actions")
+ );
+ cb_directions = GTK_COMBO_BOX_TEXT (
+ gtk_builder_get_widget (builder, "cboxtext-directions")
+ );
+
+ store_actions = GTK_LIST_STORE (
+ gtk_builder_get_object (builder, "liststore-actions")
+ );
+
+ zoom_adj = GTK_ADJUSTMENT (
+ gtk_builder_get_object (builder, "zoom-adjustment")
+ );
+
+ g_object_unref (builder);
+
+ setup_source_view (GTK_SOURCE_VIEW (source_view));
+
+ update_window_title ();
+
+ path = config_keys_get_data_folder_path ();
+ tree_folder_view_set_filename (
+ TREE_FOLDER_VIEW (tf_view),
+ path
+ );
+ g_free (path);
+
+ sprite_drawing_area_add_layer (
+ SPRITE_DRAWING_AREA (d_area),
+ sda_layer_new (interactor_sprite_layer_draw_func, &interactor)
+ );
+
+ tile_grid_layer = sda_layer_new (
+ tile_grid_layer_draw_func,
+ NULL
+ );
- XMLNode *_root_node = xml_parse_buffer(text, &error);
- sae_info->root = _root_node;
- if (_root_node == NULL) {
- parsing_error_warning(sae_info, error->message);
- g_error_free(error);
- return;
- }
+ sda_layer_set_z_index (tile_grid_layer, 512);
+
+ sprite_drawing_area_add_layer (
+ SPRITE_DRAWING_AREA (d_area),
+ tile_grid_layer
+ );
+
+ pixel_grid_layer = sda_layer_new (
+ pixel_grid_layer_draw_func,
+ NULL
+ );
- GList *list = g_list_find_custom(_root_node->sub_nodes, "include", xml_node_compare_with_name_func);
- while (list != NULL) {
- XMLNode *node = list->data;
- gchar *file_attr = xml_node_get_attr_value(node, "file");
- if (file_attr != NULL) {
- file_attr = g_strjoin(NULL, paths->sprites, file_attr, NULL);
- gchar *buf;
- if (g_file_get_contents(file_attr, &buf, NULL, NULL))
- _root_node->sub_nodes = g_list_concat(_root_node->sub_nodes, xml_parse_buffer(buf, &error)->sub_nodes);
- }
- GList *next = g_list_next(list);
- if (next != NULL)
- list = g_list_find_custom(next, "include", xml_node_compare_with_name_func);
- else
- list = NULL;
- }
+ sda_layer_set_z_index (pixel_grid_layer, 512 + 1);
+ sda_layer_set_visible (pixel_grid_layer, FALSE);
- sae_info->offsetX = 0;
- sae_info->offsetY = 0;
+ sprite_drawing_area_add_layer (
+ SPRITE_DRAWING_AREA (d_area),
+ pixel_grid_layer
+ );
- if (error != NULL)
- g_error_free(error);
-
- if (!set_up_imagesets(sae_info)) {
- parsing_error_warning(sae_info, "Bad data!");
- return;
- }
-}
-
-void parse_xml_buffer(GtkWidget *button, GtkSourceBuffer *buffer) {
- if (buffer == NULL)
- buffer = source_buffer;
-
- player = NULL;
- load_options();
-
- GtkTextIter beg, end;
- gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(buffer), &beg);
- gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(buffer), &end);
- parse_xml_text(gtk_text_iter_get_text(&beg, &end), gen_sae_info);
-}
-
-void load_config() {
- config_keys_load(config);
- gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(data_folder_chooser_button), config->clientdata_folder);
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(show_grid_menu_item), config->show_grid);
-}
-
-void save_config_and_quit() {
- config_keys_save(config);
- gtk_main_quit();
-}
-
-int main(int argc, char *argv[]) {
-
- gtk_init(&argc, &argv);
-
- icon = gdk_pixbuf_new_from_file(FILE_ICON, NULL);
-
- gen_sae_info = sae_info_new();
- config = config_keys_new();
- paths = config_options_new();
-
- set_up_interface();
- load_config();
+ gtk_widget_show_all (main_window);
gtk_main();
-
+
return 0;
}
diff --git a/saedit/main.h b/saedit/main.h
index 27367bd..1e482f3 100644
--- a/saedit/main.h
+++ b/saedit/main.h
@@ -1,119 +1,51 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
+#ifndef _MAIN_H_
+#define _MAIN_H_
-#ifndef MAIN_H
-#define MAIN_H
-
-#include <stdlib.h>
#include <gtk/gtk.h>
-#include <gtksourceview/gtksourceview.h>
-#include <gtksourceview/gtksourcelanguagemanager.h>
-#include <cairo.h>
-#include <glib/gi18n.h>
-
-#include "common.h"
-#include "xml.h"
-#include "config.h"
-#include "sae.h"
-#include "search.h"
-
-const int MIN_WIDTH = 600;
-const int MIN_HEIGHT = 600;
-
-const gchar *DIR_GROUNDS = "grounds";
-const gchar *DIR_PLAYERS = "races/";
-
-const gchar *FILE_ICON = "icon.svg";
-
-const int IMAGESET_PREVIEW_WINDOW_WIDTH = 200;
-const int IMAGESET_PREVIEW_WINDOW_HEIGHT = 300;
-
-GtkWidget *win = NULL;
-GtkWidget *darea = NULL;
-GtkWidget *data_folder_chooser_button = NULL;
-GtkWidget *xml_file_chooser_button = NULL;
-GtkWidget *xml_file_open_button = NULL;
-GtkWidget *xml_file_save_button = NULL;
-GtkWidget *imageset_preview_menu_item = NULL;
-GtkWidget *show_grid_menu_item = NULL;
-GtkWidget *source_view = NULL;
-GtkWidget *about_dialog = NULL;
-GtkWidget *parsing_error_dialog = NULL;
+#include "treefolderview.h"
+#include "context.h"
+#include "interactor.h"
+#include "spritedrawingarea.h"
-GtkSourceBuffer *source_buffer = NULL;
+GtkWidget *main_window;
+GtkWidget *d_area;
+GtkWidget *source_view;
+GtkWidget *tf_view;
+GtkWidget *tbtn_play;
-SAEInfo *gen_sae_info = NULL;
-SAEInfo *player = NULL;
+GtkComboBox *cb_actions;
+GtkComboBoxText *cb_directions;
+GtkListStore *store_actions;
+GtkAdjustment *zoom_adj;
-GdkPixbuf *icon = NULL;
+SpriteContext *context;
+Interactor *interactor;
-Options *paths;
-Keys *config;
+SDALayer *tile_grid_layer;
+SDALayer *pixel_grid_layer;
-//Cairo functions
-cairo_surface_t *get_grid_surface(int w, int h);
-gboolean darea_draw_event(GtkWidget *widget, cairo_t *cr, SAEInfo *sae_info);
+void
+buffer_set_modified (gboolean modified);
-//Common functions
-gchar *markup_bold(gchar *str);
-void format_src_string(gchar *src);
-GtkTextIter *gtk_source_buffer_highlight_line(GtkSourceBuffer *buffer, int line_number);
+gboolean
+buffer_get_modified (void);
-//File working
-void open_xml_file(GtkButton *button);
-void save_to_xml_file(gchar *filename);
+void
+release_context (void);
-//SAEInfo functions
-void free_imagesets(SAEInfo *sae_info);
-void free_actions(SAEInfo *sae_info);
-void free_animations(SAEInfo *sae_info);
-void free_imageset(SAEInfo *sae_info);
-void free_lists(SAEInfo *sae_info);
+void
+intr_updated (Interactor *interactor);
-void free_current_info();
+void
+update_window_title (void);
-//Callbacks
-void data_folder_set_callback(GtkFileChooserButton *widget, gpointer data);
-void parsing_error_warning(SAEInfo *sae_info, const gchar *message);
-void show_grid_menu_item_toggled_callback(GtkCheckMenuItem *checkmenuitem, gpointer user_data);
-void actions_combo_box_changed_callback(GtkComboBoxText *widget, gpointer user_data);
-void imagesets_combo_box_changed_callback(GtkComboBoxText *widget, gpointer user_data);
-void animations_combo_box_changed_callback(GtkComboBoxText *widget, gpointer user_data);
-void open_menu_item_activate_callback(GtkMenuItem *menuitem, GtkFileChooserDialog *fcdialog);
-gboolean frame_image_button_press_event_callback(GtkWidget *widget, GdkEventButton *button, int index);
-//>Toolbar callbacks
-void toolbar_to_first_clicked_callback(GtkToolButton *toolbutton, gpointer user_data);
-void toolbar_prev_frame_clicked_callback(GtkToolButton *toolbutton, gpointer user_data);
-void toolbar_play_clicked_callback(GtkToolButton *toolbutton, gpointer user_data);
-void toolbar_pause_clicked_callback(GtkToolButton *toolbutton, gpointer user_data);
-void toolbar_next_frame_clicked_callback(GtkToolButton *toolbutton, gpointer user_data);
-void toolbar_to_last_clicked_callback(GtkToolButton *toolbutton, gpointer user_data);
+GtkWidget *
+window_main_get_source_view (void);
-//Dialogs
-void show_imageset_dialog();
-void show_about_dialog();
+void
+update_window_title (void);
-void set_sprite_by_index(size_t index, SAEInfo *sae_info);
-void set_up_actions_by_imageset_name(gchar *imageset_name, SAEInfo *sae_info);
-gboolean set_up_imagesets(SAEInfo *sae_info);
-gboolean show_general_animation(SAEInfo *sae_info);
-gboolean set_up_action_by_name(const gchar *name, SAEInfo *sae_info);
-void set_up_imageset_by_name(const gchar* name, SAEInfo *sae_info);
-void parse_xml_buffer(GtkWidget *button, GtkSourceBuffer *buffer);
-void set_up_interface();
-void load_config();
-void save_config_and_quit();
-void load_options();
-void parse_xml_text(gchar *text, SAEInfo *sae_info);
-void show_sprite(SAEInfo *sae_info);
-void show_animation(SAEInfo *sae_info);
+void
+release_context (void);
#endif
diff --git a/saedit/sae.c b/saedit/sae.c
deleted file mode 100644
index 70e7c7d..0000000
--- a/saedit/sae.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
-
-#include "sae.h"
-
-void kill_timeout(guint tag) {
- if (tag > 0)
- g_source_remove(tag);
-}
-
-Frame *frame_new(int index1, int offsetX, int offsetY, int delay) {
- Frame *res = g_new0(Frame, 1);
- res->index = index1;
- res->offsetX = offsetX;
- res->offsetY = offsetY;
- res->delay = delay;
- return res;
-}
-
-Imageset *imageset_new() {
- Imageset *res = g_new0(Imageset, 1);
- res->width = SPRITE_WIDTH_DEFAULT;
- res->height = SPRITE_HEIGHT_DEFAULT;
- return res;
-}
-
-SAEInfo *sae_info_new() {
- SAEInfo *res = g_new0(SAEInfo, 1);
- res->ground = sae_info_ground_new();
- res->sprite = frame_new(-1, 0, 0, 0);
- res->imageset = imageset_new();
- return res;
-}
-
-GdkPixbuf *sae_info_ground_new() {
- GdkPixbuf *ground = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, GRID_SIZE * 3, GRID_SIZE * 3);
- gdk_pixbuf_fill(ground, 0x00000000);
- return ground;
-}
-
-GdkPixbuf* get_sprite_by_index(int index1, SAEInfo *sae_info) {
- if (index1 == -1) {
- GdkPixbuf *res = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
- sae_info->imageset->width,
- sae_info->imageset->height);
- gdk_pixbuf_fill(res, 0x00000000);
- return res;
- }
-
- if (index1 >= 0) {
- size_t w = sae_info->imageset->spriteset_width/sae_info->imageset->width;
- if (sae_info->imageset->spriteset == NULL) return NULL;
-
- return gdk_pixbuf_new_subpixbuf(sae_info->imageset->spriteset,
- index1%w*sae_info->imageset->width,
- index1/w*sae_info->imageset->height,
- sae_info->imageset->width,
- sae_info->imageset->height);
- }
-
- return NULL;
-}
-
-inline void _add_frame(SAEInfo *sae_info, int index1, int offsetX, int offsetY, int delay, int line) {
-
- Frame *sprite = frame_new(index1, offsetX, offsetY, delay);
- sprite->line_number = line;
- sprite->pixbuf = get_sprite_by_index(index1, sae_info);
-
- if (sae_info->animation != NULL)
- sae_info->animation = g_list_append(sae_info->animation, sprite);
- else {
- sae_info->animation = g_list_alloc();
- sae_info->animation->data = sprite;
- }
-}
-
-gboolean set_up_animation_by_direction(SAEInfo *sae_info, const gchar *direction) {
- if (sae_info->imageset->spriteset == NULL)
- return FALSE;
- sae_info->animation = NULL;
-
- GList *list = g_list_find_custom(sae_info->animations,
- xml_attr_new("direction", direction),
- (GCompareFunc)xml_node_compare_with_attr_func);
- if (list == NULL)
- return FALSE;
-
- XMLNode *anode = list->data;
- list = anode->sub_nodes;
-
- while (list != NULL) {
- XMLNode *node = list->data;
-
- //sequence tag attributes
- int offsetX = 0, offsetY = 0,
- delay = 0,
- start = -1, end = -1,
- repeat = 1;
- gchar *value = NULL;
-
- gchar *ofX_attr, *ofY_attr;
- ofX_attr = xml_node_get_attr_value(node, "offsetX");
- if (ofX_attr != NULL)
- sscanf(ofX_attr, "%d", &offsetX);
-
- ofY_attr = xml_node_get_attr_value(node, "offsetY");
- if (ofY_attr != NULL)
- sscanf(ofY_attr, "%d", &offsetY);
-
- gchar *delay_attr = xml_node_get_attr_value(node, "delay");
- if (delay_attr != NULL)
- sscanf(delay_attr, "%d", &delay);
-
- if (g_str_equal(node->name, "frame")) {
-
- gchar *index_attr = xml_node_get_attr_value(node, "index");
- if (index_attr != NULL) {
- sscanf(index_attr, "%d", &start);
- end = start;
- }
-
- } else if (g_str_equal(node->name, "sequence")) {
-
- gchar *start_attr = xml_node_get_attr_value(node, "start");
- if (start_attr != NULL)
- sscanf(start_attr, "%d", &start);
-
- gchar *end_attr = xml_node_get_attr_value(node, "end");
- if (end_attr != NULL)
- sscanf(end_attr, "%d", &end);
-
- value = xml_node_get_attr_value(node, "value");
-
- gchar *repeat_attr = xml_node_get_attr_value(node, "repeat");
- if (repeat_attr != NULL)
- sscanf(repeat_attr, "%d", &repeat);
-
- } else if (g_str_equal(node->name, "pause")) {
-
- value = (gchar *)"p";
-
- gchar *repeat_attr = xml_node_get_attr_value(node, "repeat");
- if (repeat_attr != NULL)
- sscanf(repeat_attr, "%d", &repeat);
-
- }
-
- if (start >= 0) { //start-end case
-
- int r = 0, i = 0;
- for (r = 1; r <= repeat; r++) {
- for (i = start; i <= end; i++) {
- _add_frame(sae_info, i, offsetX, offsetY, delay, node->line_number);
- }
- }
-
- } else
- if (value != NULL) { //value case
-
- gchar **values = g_strsplit(value, ",", 0);
-
- int r;
- for (r = 0; r < repeat; r++) { //TODO: remove multiparsing
- gchar **iter = values;
- while (*iter != NULL) {
-
- if (g_str_equal(*iter, "p")) { //pause
-
- _add_frame(sae_info, -1, 0, 0, delay, node->line_number);
-
- } else {
-
- unsigned f, s;
-
- if (sscanf(*iter, "%u-%u", &f, &s) == 2) {
-
- int i;
- for (i = f; i <= s; i++) {
- _add_frame(sae_info, i, offsetX, offsetY, delay, node->line_number);
- }
-
- } else if (sscanf(*iter, "%u", &f) == 1) {
-
- _add_frame(sae_info, f, offsetX, offsetY, delay, node->line_number);
-
- }
-
- }
- iter++;
- }
- }
-
- g_strfreev(values);
-
- }
-
- list = g_list_next(list);
- }
- if (sae_info->animation == NULL)
- return FALSE;
-
- return TRUE;
-}
diff --git a/saedit/sae.h b/saedit/sae.h
deleted file mode 100644
index 51f45c1..0000000
--- a/saedit/sae.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
-
-#ifndef SAE_H
-#define SAE_H
-
-#include <gtk/gtk.h>
-#include "common.h"
-#include "xml.h"
-
-void kill_timeout(guint tag);
-
-typedef struct {
- int index;
- int offsetX;
- int offsetY;
- int delay;
- int line_number;
- GdkPixbuf *pixbuf;
- cairo_surface_t *surface;
-} Frame;
-
-Frame *frame_new(int index, int offsetX, int offsetY, int delay);
-
-typedef struct {
- XMLNode *node;
- int offsetX;
- int offsetY;
- int width;
- int height;
- int spriteset_width;
- int spriteset_height;
- GdkPixbuf *spriteset;
-} Imageset;
-
-Imageset *imageset_new();
-
-typedef struct {
- GList *imagesets;
- GList *actions;
- GList *animations;
- GList *animation;
- Imageset *imageset;
- Frame *sprite;
- guint anim_tag;
- XMLNode *root;
- GtkWidget *imagesets_combo_box;
- GtkWidget *actions_combo_box;
- GtkWidget *animations_combo_box;
- GdkPixbuf *ground;
- int offsetX;
- int offsetY;
-} SAEInfo;
-
-SAEInfo *sae_info_new();
-GdkPixbuf *sae_info_ground_new();
-
-GdkPixbuf* get_sprite_by_index(int index, SAEInfo *sae_info);
-gboolean set_up_animation_by_direction(SAEInfo *sae_info, const gchar *direction);
-
-#endif
diff --git a/saedit/search.c b/saedit/search.c
deleted file mode 100644
index df2970e..0000000
--- a/saedit/search.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
-
-#include "search.h"
-
-GtkWidget *search_text_view = NULL;
-gchar *search_last_text = NULL;
-
-gboolean search_find_text(gchar *text) {
- search_last_text = text;
-
- GtkTextBuffer *text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(search_text_view));
- if (!GTK_IS_TEXT_BUFFER(text_buffer))
- return FALSE;
-
- gboolean found;
-
- GtkTextIter m_start, m_end, start;
- gtk_text_buffer_get_selection_bounds(text_buffer, NULL, &start);
- found = gtk_text_iter_forward_search(&start, text, 4, &m_start, &m_end, NULL);
-
- if (!found) {
- gtk_text_buffer_get_start_iter(text_buffer, &start);
- found = gtk_text_iter_forward_search(&start, text, 4, &m_start, &m_end, NULL);
- }
-
- if (found) {
- gtk_text_buffer_place_cursor(text_buffer, &m_start);
- gtk_text_buffer_move_mark_by_name(text_buffer, "selection_bound", &m_end);
- gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW(search_text_view),
- gtk_text_buffer_get_insert(text_buffer),
- 0.25,
- FALSE,
- 0.0,
- 0.0);
- return TRUE;
- } else
- return FALSE;
-}
-
-gboolean search_find_next() {
- if (search_last_text != NULL)
- search_find_text(search_last_text);
- // here must return some thing
-}
-
-void search_find_dialog_response_callback(GtkWidget *dialog,
- gint response_id,
- gpointer entry) {
-
- if (response_id == GTK_RESPONSE_CANCEL || response_id == GTK_RESPONSE_DELETE_EVENT)
- gtk_widget_hide(dialog);
- else {
-
- g_return_if_fail(response_id == GTK_RESPONSE_ACCEPT);
- g_return_if_fail(GTK_IS_TEXT_VIEW(search_text_view));
- g_return_if_fail(GTK_IS_ENTRY(GTK_ENTRY(entry)));
-
- search_find_text(g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))));
-
- }
-}
-
-void search_init(GtkWidget *text_view) {
- search_text_view = text_view;
-}
diff --git a/saedit/search.h b/saedit/search.h
deleted file mode 100644
index faf412e..0000000
--- a/saedit/search.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
-
-#ifndef SEARCH_H
-#define SEARCH_H
-
-#include <gtk/gtk.h>
-#include <gtksourceview/gtksourceview.h>
-#include "common.h"
-
-void search_init(GtkWidget *text_view);
-gboolean search_find_text(gchar *text);
-gboolean search_find_next();
-void search_find_dialog_response_callback(GtkWidget *dialog,
- gint response_id,
- gpointer entry);
-
-#endif
diff --git a/saedit/spritedrawingarea/sdalayer.c b/saedit/spritedrawingarea/sdalayer.c
new file mode 100644
index 0000000..9794b80
--- /dev/null
+++ b/saedit/spritedrawingarea/sdalayer.c
@@ -0,0 +1,271 @@
+#include "spritedrawingarea.h"
+#include "sdalayer.h"
+#include "sdalayerprivate.h"
+
+enum {
+ PROP_0,
+ PROP_VISIBLE,
+ PROP_OFFSET_X,
+ PROP_OFFSET_Y,
+ PROP_DRAW_FUNC,
+ PROP_USER_DATA,
+ PROP_Z_INDEX,
+ PROP_COUNT
+};
+
+static void
+sda_layer_get_property (
+ GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec
+) {
+ SDALayer *layer = SDA_LAYER (object);
+ SDALayerPrivate *priv = layer->priv;
+
+ switch (prop_id) {
+ case PROP_VISIBLE:
+ g_value_set_boolean (
+ value, priv->visible
+ );
+ break;
+
+ case PROP_OFFSET_X:
+ g_value_set_int (
+ value, priv->offset_x
+ );
+ break;
+
+ case PROP_OFFSET_Y:
+ g_value_set_int (
+ value, priv->offset_y
+ );
+ break;
+
+ case PROP_Z_INDEX:
+ g_value_set_int (
+ value, priv->z_index
+ );
+ break;
+
+ case PROP_DRAW_FUNC:
+ g_value_set_pointer (
+ value, (gpointer) priv->draw_func
+ );
+ break;
+
+ case PROP_USER_DATA:
+ g_value_set_pointer (
+ value, priv->user_data
+ );
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+sda_layer_set_property (
+ GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec
+) {
+ SDALayer *layer = SDA_LAYER (object);
+ SDALayerPrivate *priv = layer->priv;
+
+ switch (prop_id) {
+ case PROP_VISIBLE:
+ priv->visible = g_value_get_boolean (value);
+ break;
+
+ case PROP_OFFSET_X:
+ priv->offset_x = g_value_get_int (value);
+ break;
+
+ case PROP_OFFSET_Y:
+ priv->offset_y = g_value_get_int (value);
+ break;
+
+ case PROP_Z_INDEX:
+ priv->z_index = g_value_get_int (value);
+ break;
+
+ case PROP_DRAW_FUNC:
+ priv->draw_func = (SDALayerDrawFunc) g_value_get_pointer (value);
+ break;
+
+ case PROP_USER_DATA:
+ priv->user_data = g_value_get_pointer (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+void
+sda_layer_init (
+ SDALayer *layer,
+ SDALayerClass *class
+) {
+ layer->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ layer,
+ SDA_TYPE_LAYER,
+ SDALayerPrivate
+ );
+
+ layer->priv->draw_func = NULL;
+ layer->priv->user_data = NULL;
+}
+
+void
+sda_layer_class_init (
+ SDALayerClass *klass,
+ gpointer class_data
+) {
+ GObjectClass *object_class;
+
+ g_type_class_add_private (klass, sizeof (SDALayerPrivate));
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = sda_layer_set_property;
+ object_class->get_property = sda_layer_get_property;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_VISIBLE,
+ g_param_spec_boolean (
+ "visible",
+ "Visible",
+ "Whether this layer is visible",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ )
+ );
+
+ g_object_class_install_property (
+ object_class,
+ PROP_OFFSET_X,
+ g_param_spec_int (
+ "offset-x",
+ "Offset X",
+ "The X coordinate of the layer offset",
+ -SPRITE_DRAWING_AREA_FIELD_SIZE,
+ SPRITE_DRAWING_AREA_FIELD_SIZE,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ )
+ );
+
+ g_object_class_install_property (
+ object_class,
+ PROP_OFFSET_Y,
+ g_param_spec_int (
+ "offset-y",
+ "Offset y",
+ "The Y coordinate of the layer offset",
+ -SPRITE_DRAWING_AREA_FIELD_SIZE,
+ SPRITE_DRAWING_AREA_FIELD_SIZE,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ )
+ );
+
+ g_object_class_install_property (
+ object_class,
+ PROP_DRAW_FUNC,
+ g_param_spec_pointer (
+ "draw-func",
+ "Draw function",
+ "A function invoked to draw the layer",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
+ )
+ );
+
+ g_object_class_install_property (
+ object_class,
+ PROP_USER_DATA,
+ g_param_spec_pointer (
+ "user-data",
+ "User data",
+ "Data that will be propagated to the draw"
+ " function upon call",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ )
+ );
+
+ g_object_class_install_property (
+ object_class,
+ PROP_Z_INDEX,
+ g_param_spec_int (
+ "z-index",
+ "Z-index",
+ "Z-index of the layer",
+ -1024, 1024, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ )
+ );
+}
+
+GType
+sda_layer_get_type (void) {
+ static GType sda_layer_type = 0;
+
+ if (sda_layer_type == 0) {
+ const GTypeInfo sda_layer_info = {
+ sizeof (SDALayerClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) sda_layer_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (SDALayer),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) sda_layer_init,
+ NULL, /* value_table */
+ };
+
+ sda_layer_type = g_type_register_static (
+ G_TYPE_OBJECT,
+ "SDALayer",
+ &sda_layer_info,
+ 0
+ );
+ }
+
+ return sda_layer_type;
+}
+
+SDALayer *sda_layer_new (
+ SDALayerDrawFunc draw_func,
+ gpointer user_data
+) {
+ return g_object_new (
+ SDA_TYPE_LAYER,
+ "draw-func", draw_func,
+ "user-data", user_data,
+ NULL
+ );
+}
+
+gint
+sda_layer_compare_by_z_index (
+ const SDALayer *a,
+ const SDALayer *b
+) {
+ return a->priv->z_index - b->priv->z_index;
+}
+
+void
+sda_layer_set_z_index (SDALayer *layer, gint z_index) {
+ g_object_set (layer, "z-index", z_index, NULL);
+}
+
+void
+sda_layer_set_visible (SDALayer *layer, gboolean visible) {
+ g_object_set (layer, "visible", visible, NULL);
+}
diff --git a/saedit/spritedrawingarea/sdalayer.h b/saedit/spritedrawingarea/sdalayer.h
new file mode 100644
index 0000000..e1bac04
--- /dev/null
+++ b/saedit/spritedrawingarea/sdalayer.h
@@ -0,0 +1,54 @@
+#ifndef _SDALAYER_H_
+#define _SDALAYER_H_
+
+#include <glib-object.h>
+#include <cairo.h>
+
+G_BEGIN_DECLS
+
+#define SDA_TYPE_LAYER (sda_layer_get_type ())
+#define SDA_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SDA_TYPE_LAYER, SDALayer))
+#define SDA_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SDA_TYPE_LAYER, SDALayerClass))
+#define IS_SDA_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SDA_TYPE_LAYER))
+#define IS_SDA_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SDA_TYPE_LAYER))
+#define SDA_LAYER_GET_CLASS(obj) ((obj), SDA_TYPE_LAYER, SDALayerClass)
+
+typedef struct _SDALayer SDALayer;
+typedef struct _SDALayerClass SDALayerClass;
+typedef struct _SDALayerPrivate SDALayerPrivate;
+
+struct _SDALayerClass {
+ GObjectClass parent_class;
+};
+
+GType
+sda_layer_get_type (void);
+
+typedef void
+(* SDALayerDrawFunc) (
+ SDALayer *layer,
+ cairo_t *cr,
+ gpointer user_data
+);
+
+SDALayer *
+sda_layer_new (
+ SDALayerDrawFunc draw_func,
+ gpointer user_data
+);
+
+gint
+sda_layer_compare_by_z_index (
+ const SDALayer *a,
+ const SDALayer *b
+);
+
+void
+sda_layer_set_z_index (SDALayer *layer, gint z_index);
+
+void
+sda_layer_set_visible (SDALayer *layer, gboolean visible);
+
+G_END_DECLS
+
+#endif
diff --git a/saedit/spritedrawingarea/sdalayerprivate.h b/saedit/spritedrawingarea/sdalayerprivate.h
new file mode 100644
index 0000000..daf47bc
--- /dev/null
+++ b/saedit/spritedrawingarea/sdalayerprivate.h
@@ -0,0 +1,19 @@
+#ifndef _SDALAYERPRIVATE_H_
+#define _SDALAYERPRIVATE_H_
+
+struct _SDALayerPrivate {
+ gboolean visible;
+ gint offset_x, offset_y;
+ gint z_index;
+
+ SDALayerDrawFunc draw_func;
+ gpointer user_data;
+};
+
+struct _SDALayer {
+ GObject object;
+
+ SDALayerPrivate *priv;
+};
+
+#endif
diff --git a/saedit/spritedrawingarea/spritedrawingarea.c b/saedit/spritedrawingarea/spritedrawingarea.c
new file mode 100644
index 0000000..376c8e1
--- /dev/null
+++ b/saedit/spritedrawingarea/spritedrawingarea.c
@@ -0,0 +1,437 @@
+#include "spritedrawingarea.h"
+#include "sdalayer.h"
+#include "sdalayerprivate.h"
+
+struct _SpriteDrawingAreaPrivate {
+ gint center_x, center_y;
+ gdouble scale_factor;
+
+ gboolean drag_active;
+ gdouble drag_x, drag_y;
+
+ GList *layers;
+};
+
+enum {
+ PROP_0,
+ PROP_CENTER_X,
+ PROP_CENTER_Y,
+ PROP_SCALE_FACTOR,
+ PROP_COUNT
+};
+
+void
+sprite_drawing_area_get_center (
+ SpriteDrawingArea *sdarea,
+ gint *center_x,
+ gint *center_y
+) {
+ g_object_get (
+ sdarea,
+ "center-x", center_x,
+ "center-y", center_y,
+ NULL
+ );
+}
+
+void
+sprite_drawing_area_set_center (
+ SpriteDrawingArea *sdarea,
+ gint center_x,
+ gint center_y
+) {
+ g_object_set (
+ sdarea,
+ "center-x", center_x,
+ "center-y", center_y,
+ NULL
+ );
+}
+
+gdouble
+sprite_drawing_area_get_scale_factor (
+ SpriteDrawingArea *sdarea
+) {
+ gdouble result;
+
+ g_object_get (
+ sdarea,
+ "scale-factor", &result,
+ NULL
+ );
+
+ return result;
+}
+
+void
+sprite_drawing_area_set_scale_factor (
+ SpriteDrawingArea *sdarea,
+ gdouble scale_factor
+) {
+ g_object_set (
+ sdarea,
+ "scale-factor", scale_factor,
+ NULL
+ );
+}
+
+static gboolean
+_sprite_drawing_area_draw_handler (
+ GtkWidget *widget,
+ cairo_t *cr,
+ gpointer user_data
+) {
+ SpriteDrawingArea *sdarea = SPRITE_DRAWING_AREA (widget);
+ GList *l;
+
+ gint width = gtk_widget_get_allocated_width (widget);
+ gint height = gtk_widget_get_allocated_height (widget);
+
+ gdouble scale = sprite_drawing_area_get_scale_factor (sdarea);
+
+ gint center_x, center_y;
+
+ sprite_drawing_area_get_center (sdarea, &center_x, &center_y);
+
+ cairo_translate (
+ cr,
+ 0.5 * (gdouble) width,
+ 0.5 * (gdouble) height
+ );
+
+ cairo_scale (cr, scale, scale);
+
+ cairo_translate (
+ cr,
+ -center_x,
+ -center_y
+ );
+
+ sdarea->priv->layers = g_list_sort (
+ sdarea->priv->layers,
+ (GCompareFunc) sda_layer_compare_by_z_index
+ );
+
+ for (l = sdarea->priv->layers; l != NULL; l = l->next) {
+ SDALayer *layer = SDA_LAYER (l->data);
+
+ if (!layer->priv->visible)
+ continue;
+
+ cairo_save (cr);
+
+ cairo_translate (
+ cr,
+ layer->priv->offset_x,
+ layer->priv->offset_y
+ );
+
+ if (layer->priv->draw_func != NULL)
+ layer->priv->draw_func (layer, cr, layer->priv->user_data);
+
+ cairo_restore (cr);
+ }
+
+ return FALSE;
+}
+
+static void
+sprite_drawing_area_get_property (
+ GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec
+) {
+ SpriteDrawingArea *sdarea = SPRITE_DRAWING_AREA (object);
+
+ switch (prop_id) {
+ case PROP_CENTER_X:
+ g_value_set_int (
+ value, sdarea->priv->center_x
+ );
+ break;
+
+ case PROP_CENTER_Y:
+ g_value_set_int (
+ value, sdarea->priv->center_y
+ );
+ break;
+
+ case PROP_SCALE_FACTOR:
+ g_value_set_double (
+ value, sdarea->priv->scale_factor
+ );
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+sprite_drawing_area_set_property (
+ GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec
+) {
+ SpriteDrawingArea *sdarea = SPRITE_DRAWING_AREA (object);
+
+ switch (prop_id) {
+ case PROP_CENTER_X:
+ sdarea->priv->center_x = g_value_get_int (value);
+ break;
+
+ case PROP_CENTER_Y:
+ sdarea->priv->center_y = g_value_get_int (value);
+ break;
+
+ case PROP_SCALE_FACTOR:
+ sdarea->priv->scale_factor = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+gboolean
+_sprite_drawing_area_drag_start_callback (
+ GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data
+) {
+ SpriteDrawingArea *sdarea = SPRITE_DRAWING_AREA (widget);
+
+ if (event->button.button != 1)
+ return FALSE;
+
+ sdarea->priv->drag_active = TRUE;
+ sdarea->priv->drag_x = event->button.x;
+ sdarea->priv->drag_y = event->button.y;
+
+ return FALSE;
+}
+
+gboolean
+_sprite_drawing_area_drag_end_callback (
+ GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data
+) {
+ SpriteDrawingArea *sdarea = SPRITE_DRAWING_AREA (widget);
+
+ if (event->button.button != 1)
+ return FALSE;
+
+ sdarea->priv->drag_active = FALSE;
+ return FALSE;
+}
+
+gboolean
+_sprite_drawing_area_drag_motion_callback (
+ GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data
+) {
+ SpriteDrawingArea *sdarea = SPRITE_DRAWING_AREA (widget);
+
+ gdouble event_x, event_y;
+
+ gint center_x, center_y;
+ gint delta_x, delta_y;
+
+ gdouble scale_factor;
+
+ if ((event->motion.state & GDK_BUTTON1_MASK) == 0)
+ return FALSE;
+
+ if (!sdarea->priv->drag_active)
+ return FALSE;
+
+ event_x = event->motion.x;
+ event_y = event->motion.y;
+
+ sprite_drawing_area_get_center (sdarea, &center_x, &center_y);
+
+ scale_factor = sprite_drawing_area_get_scale_factor (sdarea);
+
+ delta_x =
+ (gint) (event_x / scale_factor + 0.5) - (gint) (sdarea->priv->drag_x / scale_factor + 0.5);
+ delta_y =
+ (gint) (event_y / scale_factor + 0.5) - (gint) (sdarea->priv->drag_y / scale_factor + 0.5);
+
+ sprite_drawing_area_set_center (
+ sdarea,
+ center_x - delta_x,
+ center_y - delta_y
+ );
+
+ sdarea->priv->drag_x = event_x;
+ sdarea->priv->drag_y = event_y;
+
+ gtk_widget_queue_draw (widget);
+ return FALSE;
+}
+
+static void
+sprite_drawing_area_init (
+ SpriteDrawingArea *sdarea,
+ SpriteDrawingAreaClass *klass
+) {
+ /* Setting up private */
+ sdarea->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ sdarea,
+ TYPE_SPRITE_DRAWING_AREA,
+ SpriteDrawingAreaPrivate
+ );
+
+ sdarea->priv->layers = NULL;
+
+ g_signal_connect (
+ sdarea,
+ "button-press-event",
+ (GCallback) _sprite_drawing_area_drag_start_callback,
+ NULL
+ );
+
+ g_signal_connect (
+ sdarea,
+ "button-release-event",
+ (GCallback) _sprite_drawing_area_drag_end_callback,
+ NULL
+ );
+
+ g_signal_connect (
+ sdarea,
+ "motion-notify-event",
+ (GCallback) _sprite_drawing_area_drag_motion_callback,
+ NULL
+ );
+}
+
+static void
+sprite_drawing_area_class_init (
+ SpriteDrawingAreaClass *klass,
+ gpointer class_data
+) {
+ GObjectClass *object_class;
+
+ g_type_class_add_private (klass, sizeof (SpriteDrawingAreaPrivate));
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = sprite_drawing_area_set_property;
+ object_class->get_property = sprite_drawing_area_get_property;
+
+ g_signal_override_class_handler (
+ "draw",
+ TYPE_SPRITE_DRAWING_AREA,
+ (GCallback) _sprite_drawing_area_draw_handler
+ );
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CENTER_X,
+ g_param_spec_int (
+ "center-x",
+ "Center X",
+ "The X coordinate of the point on the field "
+ "camera is centered on, in pixels",
+ -SPRITE_DRAWING_AREA_FIELD_SIZE,
+ SPRITE_DRAWING_AREA_FIELD_SIZE,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ )
+ );
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CENTER_Y,
+ g_param_spec_int (
+ "center-y",
+ "Center Y",
+ "The Y coordinate of the point on the field "
+ "camera is centered on, in pixels",
+ -SPRITE_DRAWING_AREA_FIELD_SIZE,
+ SPRITE_DRAWING_AREA_FIELD_SIZE,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ )
+ );
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SCALE_FACTOR,
+ g_param_spec_double (
+ "scale-factor",
+ "Scale factor",
+ "The scaling factor that is used in making "
+ "the camera zooming effect",
+ 0.01, 100.0, 1.0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ )
+ );
+}
+
+GType
+sprite_drawing_area_get_type (void) {
+ static GType sdarea_type = 0;
+
+ if (sdarea_type == 0) {
+ const GTypeInfo sdarea_info = {
+ sizeof (SpriteDrawingAreaClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) sprite_drawing_area_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (SpriteDrawingArea),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) sprite_drawing_area_init,
+ NULL, /* value_table */
+ };
+
+ sdarea_type = g_type_register_static (
+ GTK_TYPE_DRAWING_AREA,
+ "SpriteDrawingArea",
+ &sdarea_info,
+ 0
+ );
+
+ }
+
+ return sdarea_type;
+}
+
+GtkWidget *
+sprite_drawing_area_new (void) {
+ GtkWidget *sdarea = GTK_WIDGET (
+ g_object_new (TYPE_SPRITE_DRAWING_AREA, NULL)
+ );
+ return sdarea;
+}
+
+void
+sprite_drawing_area_add_layer (
+ SpriteDrawingArea *sdarea,
+ SDALayer *layer
+) {
+ sdarea->priv->layers = g_list_insert_sorted (
+ sdarea->priv->layers,
+ layer,
+ (GCompareFunc) sda_layer_compare_by_z_index
+ );
+}
+
+void
+sprite_drawing_area_remove_layer (
+ SpriteDrawingArea *sdarea,
+ SDALayer *layer
+) {
+ sdarea->priv->layers = g_list_remove (
+ sdarea->priv->layers,
+ layer
+ );
+}
diff --git a/saedit/spritedrawingarea/spritedrawingarea.h b/saedit/spritedrawingarea/spritedrawingarea.h
new file mode 100644
index 0000000..ee3aa6d
--- /dev/null
+++ b/saedit/spritedrawingarea/spritedrawingarea.h
@@ -0,0 +1,71 @@
+#ifndef __SPRITE_DRAWING_AREA_H__
+#define __SPRITE_DRAWING_AREA_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include "sdalayer.h"
+
+G_BEGIN_DECLS
+
+#define TYPE_SPRITE_DRAWING_AREA (sprite_drawing_area_get_type ())
+#define SPRITE_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_SPRITE_DRAWING_AREA, SpriteDrawingArea))
+#define SPRITE_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_SPRITE_DRAWING_AREA, SpriteDrawingAreaClass))
+#define IS_SPRITE_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_SPRITE_DRAWING_AREA))
+#define IS_SPRITE_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_SPRITE_DRAWING_AREA))
+#define SPRITE_DRAWING_AREA_GET_CLASS(obj) ((obj), TYPE_SPRITE_DRAWING_AREA, SpriteDrawingAreaClass)
+
+typedef struct _SpriteDrawingArea SpriteDrawingArea;
+typedef struct _SpriteDrawingAreaPrivate SpriteDrawingAreaPrivate;
+typedef struct _SpriteDrawingAreaClass SpriteDrawingAreaClass;
+
+struct _SpriteDrawingArea {
+ GtkDrawingArea darea;
+
+ SpriteDrawingAreaPrivate *priv;
+};
+
+struct _SpriteDrawingAreaClass {
+ GtkDrawingAreaClass darea_class;
+
+ void (* draw_field) (
+ GtkWidget *sdarea,
+ gpointer data
+ );
+};
+
+static const gint SPRITE_DRAWING_AREA_FIELD_SIZE = 1024;
+
+GType
+sprite_drawing_area_get_type (void);
+
+GtkWidget*
+sprite_drawing_area_new (void);
+
+void
+sprite_drawing_area_set_scale_factor (
+ SpriteDrawingArea *sdarea,
+ gdouble scale_factor
+);
+
+void
+sprite_drawing_area_add_layer (
+ SpriteDrawingArea *sdarea,
+ SDALayer *layer
+);
+
+void
+sprite_drawing_area_remove_layer (
+ SpriteDrawingArea *sdarea,
+ SDALayer *layer
+);
+
+void
+sprite_drawing_area_set_center (
+ SpriteDrawingArea *sdarea,
+ gint center_x,
+ gint center_y
+);
+
+G_END_DECLS
+
+#endif
diff --git a/saedit/template.xml b/saedit/template.xml
deleted file mode 100644
index 3747c31..0000000
--- a/saedit/template.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0"?>
-<!-- Original Author :
-Copyright (C) 2012 -->
-<sprite>
-
-</sprite>
diff --git a/saedit/treefolderview/treefolderview.c b/saedit/treefolderview/treefolderview.c
new file mode 100644
index 0000000..e1b02f0
--- /dev/null
+++ b/saedit/treefolderview/treefolderview.c
@@ -0,0 +1,400 @@
+#include "treefolderview.h"
+#include "treefolderviewprivate.h"
+
+enum {
+ STORE_COLUMN_FILE_NAME,
+ STORE_COLUMN_IS_FOLDER,
+ STORE_COLUMN_FILE_ICON,
+ STORE_COLUMN_FILE_INFO,
+ STORE_COLUMN_WAS_EXPANDED,
+ STORE_COLUMN_COUNT
+};
+
+enum {
+ SIGNAL_FILE_ACTIVATED,
+ SIGNAL_COUNT
+};
+
+struct _TreeFolderViewPrivate {
+ gchar *filename;
+ GtkFileFilter *file_filter;
+};
+
+static guint tfview_signals [SIGNAL_COUNT] = { 0 };
+
+static GtkTreeStore *
+tree_folder_view_get_store (TreeFolderView *tfview) {
+ GtkTreeModelFilter *model;
+
+ model = GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (tfview)));
+ return GTK_TREE_STORE (gtk_tree_model_filter_get_model (model));
+}
+
+static gboolean
+file_filter_filter_file_info (GtkFileFilter *filter, GFileInfo *info) {
+ gboolean result;
+ gchar *mimetype = NULL;
+ GtkFileFilterFlags required;
+ GtkFileFilterInfo filter_info = { 0, };
+
+ if (filter == NULL)
+ return TRUE;
+
+ required = gtk_file_filter_get_needed (filter);
+
+ filter_info.contains |= GTK_FILE_FILTER_DISPLAY_NAME;
+ filter_info.display_name = g_file_info_get_display_name (info);
+
+ if (required & GTK_FILE_FILTER_MIME_TYPE) {
+ const gchar *ctype = g_file_info_get_content_type (info);
+ if (ctype != NULL) {
+ mimetype = g_content_type_get_mime_type (ctype);
+ if (mimetype != NULL) {
+ filter_info.contains |= GTK_FILE_FILTER_MIME_TYPE;
+ filter_info.mime_type = mimetype;
+ }
+ }
+ }
+
+ if (required & GTK_FILE_FILTER_FILENAME) {
+ filter_info.filename = g_file_info_get_name (info);
+ filter_info.contains |= GTK_FILE_FILTER_FILENAME;
+ }
+
+ result = gtk_file_filter_filter (filter, &filter_info);
+
+ g_free(mimetype);
+
+ return result;
+}
+
+static gchar *
+tree_folder_view_get_file_path_from_tree_path (
+ TreeFolderView *tfview,
+ GtkTreePath *path
+) {
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ model = GTK_TREE_MODEL (tree_folder_view_get_store (tfview));
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ return tree_folder_view_get_file_path_from_iter (tfview, &iter);
+}
+
+static gchar *
+tree_folder_view_get_file_path_from_iter (
+ TreeFolderView *tfview,
+ GtkTreeIter *file_iter
+) {
+ gchar *result, *data;
+ GPtrArray *names;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreePath *_path;
+
+ model = GTK_TREE_MODEL (tree_folder_view_get_store (tfview));
+ _path = gtk_tree_model_get_path (model, file_iter);
+
+ names = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
+
+ while (gtk_tree_path_get_depth (_path) > 0) {
+ gtk_tree_model_get_iter (model, &iter, _path);
+ gtk_tree_model_get (
+ model, &iter,
+ STORE_COLUMN_FILE_NAME, &data, -1
+ );
+ g_ptr_array_insert (names, 0, data);
+ gtk_tree_path_up (_path);
+ }
+
+ g_ptr_array_insert (names, 0, g_strdup (tfview->priv->filename));
+ g_ptr_array_add (names, NULL);
+ result = g_strjoinv ("/", (gchar **) names->pdata);
+
+ g_ptr_array_free (names, TRUE);
+ gtk_tree_path_free (_path);
+
+ return result;
+}
+
+static void
+tree_folder_view_row_expanded (
+ GtkTreeView *tree_view,
+ GtkTreeIter *filter_iter,
+ GtkTreePath *path
+) {
+ gboolean w_exp;
+ gchar *file_path;
+ GtkTreeIter iter, citer;
+ GtkTreeStore *store;
+
+ gtk_tree_model_filter_convert_iter_to_child_iter (
+ GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (tree_view)),
+ &iter,
+ filter_iter
+ );
+
+ store = tree_folder_view_get_store (
+ TREE_FOLDER_VIEW (tree_view)
+ );
+
+ gtk_tree_model_get (
+ GTK_TREE_MODEL (store), &iter,
+ STORE_COLUMN_WAS_EXPANDED, &w_exp,
+ -1
+ );
+
+ if (w_exp)
+ return;
+
+ gtk_tree_store_set (
+ store, &iter,
+ STORE_COLUMN_WAS_EXPANDED, TRUE,
+ -1
+ );
+
+ if (!gtk_tree_model_iter_has_child (GTK_TREE_MODEL (store), &iter))
+ return;
+
+ gtk_tree_model_iter_children (GTK_TREE_MODEL (store), &citer, &iter);
+ do {
+ file_path = tree_folder_view_get_file_path_from_iter (
+ TREE_FOLDER_VIEW (tree_view),
+ &citer
+ );
+
+ tree_store_append_file_children (
+ store,
+ &citer,
+ file_path,
+ FALSE
+ );
+
+ g_free (file_path);
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &citer));
+}
+
+static gboolean
+tree_model_filter_file_visible_func (
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data
+) {
+ gboolean is_folder;
+ GFileInfo *info;
+ GtkFileFilter *filter;
+ TreeFolderView *tfview;
+
+ tfview = TREE_FOLDER_VIEW (data);
+
+ gtk_tree_model_get (
+ model, iter,
+ STORE_COLUMN_FILE_INFO, &info,
+ STORE_COLUMN_IS_FOLDER, &is_folder,
+ -1
+ );
+
+ if (info == NULL)
+ return FALSE;
+
+ if (g_file_info_get_is_hidden (info))
+ return FALSE;
+
+ if (is_folder)
+ return TRUE;
+
+ filter = tree_folder_view_get_filter (tfview);
+ return file_filter_filter_file_info (filter, info);
+}
+
+static void
+tree_store_append_file_children (
+ GtkTreeStore *store,
+ GtkTreeIter *iter,
+ const gchar *path,
+ gboolean expanded
+) {
+ gchar *npath;
+ const gchar *name;
+ GDir *dir;
+
+ dir = g_dir_open (path, 0, NULL);
+
+ if (dir == NULL)
+ return;
+
+ while ((name = g_dir_read_name (dir)) != NULL) {
+ npath = g_strconcat (path, "/", name, NULL);
+
+ tree_store_append_file_recursive (
+ store,
+ iter,
+ npath,
+ name,
+ expanded
+ );
+
+ g_free(npath);
+ }
+
+ g_dir_close (dir);
+}
+
+static gint
+tree_store_append_file_recursive (
+ GtkTreeStore *store,
+ GtkTreeIter *parent_iter,
+ const gchar *path,
+ const gchar *display_name,
+ gboolean append_children
+) {
+ GFile *file;
+ GFileInfo *info;
+ GtkTreeIter iter;
+
+ file = g_file_new_for_path (path);
+ info = g_file_query_info (file, "*", 0, NULL, NULL);
+ g_object_unref (file);
+
+ gtk_tree_store_append (store, &iter, parent_iter);
+ gtk_tree_store_set (
+ store, &iter,
+ STORE_COLUMN_FILE_NAME, display_name,
+ STORE_COLUMN_FILE_ICON, g_content_type_get_icon (g_file_info_get_content_type (info)),
+ STORE_COLUMN_FILE_INFO, info,
+ STORE_COLUMN_IS_FOLDER, FALSE,
+ STORE_COLUMN_WAS_EXPANDED, FALSE,
+ -1
+ );
+
+ if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
+ return 0;
+
+ gtk_tree_store_set (
+ store, &iter,
+ STORE_COLUMN_IS_FOLDER, TRUE,
+ STORE_COLUMN_FILE_ICON, g_themed_icon_new ("folder"), -1
+ );
+
+ if (!append_children)
+ return 1;
+
+ tree_store_append_file_children (store, &iter, path, FALSE);
+
+ return 1;
+}
+
+static gint
+tree_store_iter_compare_func (
+ GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data
+) {
+ gboolean isf_a, isf_b;
+ gint cmp;
+ gchar *val_a, *val_b;
+
+ gtk_tree_model_get (model, a, STORE_COLUMN_IS_FOLDER, &isf_a, -1);
+ gtk_tree_model_get (model, b, STORE_COLUMN_IS_FOLDER, &isf_b, -1);
+
+ if (isf_a != isf_b)
+ return isf_b;
+
+ gtk_tree_model_get (model, a, STORE_COLUMN_FILE_NAME, &val_a, -1);
+ gtk_tree_model_get (model, b, STORE_COLUMN_FILE_NAME, &val_b, -1);
+
+ cmp = g_strcmp0 (val_a, val_b);
+ g_free (val_a);
+ g_free (val_b);
+
+ return cmp;
+}
+
+static void
+tree_folder_view_row_activated (
+ GtkTreeView *tree_view,
+ GtkTreePath *filter_path,
+ GtkTreeViewColumn *col
+) {
+ gboolean is_folder;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GtkTreeStore *store;
+
+ path = gtk_tree_model_filter_convert_path_to_child_path (
+ GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (tree_view)),
+ filter_path
+ );
+
+ store = tree_folder_view_get_store (TREE_FOLDER_VIEW (tree_view));
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
+ gtk_tree_model_get (
+ GTK_TREE_MODEL (store), &iter,
+ STORE_COLUMN_IS_FOLDER, &is_folder, -1
+ );
+
+ if (is_folder)
+ return;
+
+ g_signal_emit (
+ tree_view,
+ tfview_signals[SIGNAL_FILE_ACTIVATED],
+ 0,
+ tree_folder_view_get_file_path_from_tree_path (
+ TREE_FOLDER_VIEW (tree_view),
+ path
+ )
+ );
+
+ gtk_tree_path_free (path);
+}
+
+void
+tree_folder_view_set_filename (
+ TreeFolderView *tfview,
+ const gchar *filename
+) {
+ GtkTreeStore *store = tree_folder_view_get_store (tfview);
+
+ /* TODO: values inside are not freed */
+ gtk_tree_store_clear (store);
+
+ g_free (tfview->priv->filename);
+ tfview->priv->filename = g_strdup (filename);
+
+ if (filename != NULL) {
+ tree_store_append_file_children (
+ store,
+ NULL,
+ filename,
+ TRUE
+ );
+ }
+}
+
+GtkFileFilter *
+tree_folder_view_get_filter (TreeFolderView *tfview) {
+ return tfview->priv->file_filter;
+}
+
+void
+tree_folder_view_set_filter (TreeFolderView *tfview, GtkFileFilter *filter) {
+ if (G_IS_OBJECT (tfview->priv->file_filter))
+ g_object_unref (tfview->priv->file_filter);
+
+ tfview->priv->file_filter = filter;
+ g_object_ref (filter);
+
+ gtk_tree_model_filter_refilter (
+ GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (tfview))));
+}
+
+gchar *
+tree_folder_view_get_filename (TreeFolderView *tfview) {
+ return g_strdup (tfview->priv->filename);
+}
+
+#include "type.c"
diff --git a/saedit/treefolderview/treefolderview.h b/saedit/treefolderview/treefolderview.h
new file mode 100644
index 0000000..78aeefc
--- /dev/null
+++ b/saedit/treefolderview/treefolderview.h
@@ -0,0 +1,64 @@
+#ifndef __TREE_FOLDER_VIEW_H__
+#define __TREE_FOLDER_VIEW_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_TREE_FOLDER_VIEW (tree_folder_view_get_type ())
+#define TREE_FOLDER_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TREE_FOLDER_VIEW, TreeFolderView))
+#define TREE_FOLDER_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_TREE_FOLDER_VIEW, TreeFolderViewClass))
+#define IS_TREE_FOLDER_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TREE_FOLDER_VIEW))
+#define IS_TREE_FOLDER_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_TREE_FOLDER_VIEW))
+#define TREE_FOLDER_VIEW_GET_CLASS(obj) ((obj), TYPE_TREE_FOLDER_VIEW, TreeFolderViewClass)
+
+typedef struct _TreeFolderView TreeFolderView;
+typedef struct _TreeFolderViewPrivate TreeFolderViewPrivate;
+typedef struct _TreeFolderViewClass TreeFolderViewClass;
+
+struct _TreeFolderView {
+ GtkTreeView tview;
+
+ TreeFolderViewPrivate *priv;
+};
+
+struct _TreeFolderViewClass {
+ GtkTreeViewClass tview_class;
+
+ void (* file_activated) (
+ TreeFolderView *tfview,
+ gchar *filename
+ );
+};
+
+GType
+tree_folder_view_get_type (void);
+
+GtkWidget*
+tree_folder_view_new (void);
+
+void
+tree_folder_view_set_filename (
+ TreeFolderView *tfview,
+ const gchar *filename
+);
+
+GtkFileFilter*
+tree_folder_view_get_filter (TreeFolderView *tfview);
+
+void
+tree_folder_view_set_filter (
+ TreeFolderView *tfview,
+ GtkFileFilter *filter
+);
+
+gchar *
+tree_folder_view_get_filename (TreeFolderView *tfview);
+
+gchar *
+tree_folder_view_get_display_name (TreeFolderView *tfview);
+
+G_END_DECLS
+
+#endif
diff --git a/saedit/treefolderview/treefolderviewprivate.h b/saedit/treefolderview/treefolderviewprivate.h
new file mode 100644
index 0000000..01119fc
--- /dev/null
+++ b/saedit/treefolderview/treefolderviewprivate.h
@@ -0,0 +1,45 @@
+#ifndef __TREE_FOLDER_VIEW_PRIVATE_H__
+#define __TREE_FOLDER_VIEW_PRIVATE_H__
+
+enum {
+ PROP_0,
+ PROP_FILTER,
+ PROP_FILENAME,
+ PROP_MODEL,
+ PROP_COUNT
+};
+
+static GtkTreeStore *
+tree_folder_view_get_store (TreeFolderView *tfview);
+
+static gchar *
+tree_folder_view_get_file_path_from_tree_path (
+ TreeFolderView *tfview,
+ GtkTreePath *path
+);
+
+static gchar *
+tree_folder_view_get_file_path_from_iter (
+ TreeFolderView *tfview,
+ GtkTreeIter *file_iter
+);
+
+static void
+tree_store_append_file_children (
+ GtkTreeStore *store,
+ GtkTreeIter *iter,
+ const gchar *path,
+ gboolean expanded
+);
+
+static gint
+tree_store_append_file_recursive (
+ GtkTreeStore *store,
+ GtkTreeIter *parent_iter,
+ const gchar *path,
+ const gchar *display_name,
+ gboolean append_children
+);
+
+#endif
+
diff --git a/saedit/treefolderview/type.c b/saedit/treefolderview/type.c
new file mode 100644
index 0000000..fbb0f21
--- /dev/null
+++ b/saedit/treefolderview/type.c
@@ -0,0 +1,250 @@
+static void
+tree_folder_view_init (
+ TreeFolderView *tfview,
+ TreeFolderViewClass *klass
+) {
+ GType *types;
+ GtkCellRenderer *renderer;
+ GtkTreeModel *model;
+ GtkTreeStore *store;
+ GtkTreeView *tview;
+ GtkTreeViewColumn *col;
+
+ tview = GTK_TREE_VIEW (tfview);
+
+ /* Setting up TreeView properties */
+ gtk_tree_view_set_headers_visible (tview, FALSE);
+ gtk_tree_view_set_enable_tree_lines (tview, TRUE);
+
+ /* Setting up only column */
+ col = gtk_tree_view_column_new ();
+
+ /* Filename renderer */
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_end (col, renderer, TRUE);
+ gtk_tree_view_column_set_attributes (
+ col,
+ renderer,
+ "text", STORE_COLUMN_FILE_NAME,
+ NULL
+ );
+
+ /* Filetype renderer */
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_end (col, renderer, FALSE);
+ gtk_tree_view_column_set_attributes (
+ col,
+ renderer,
+ "gicon", STORE_COLUMN_FILE_ICON,
+ NULL
+ );
+
+ gtk_tree_view_append_column (tview, col);
+
+ /* Setting up TreeStore */
+ types = g_new (GType, STORE_COLUMN_COUNT);
+ types [STORE_COLUMN_FILE_NAME] = G_TYPE_STRING;
+ types [STORE_COLUMN_FILE_ICON] = G_TYPE_ICON;
+ types [STORE_COLUMN_FILE_INFO] = G_TYPE_FILE_INFO;
+ types [STORE_COLUMN_IS_FOLDER] = G_TYPE_BOOLEAN;
+ types [STORE_COLUMN_WAS_EXPANDED] = G_TYPE_BOOLEAN;
+
+ store = gtk_tree_store_newv (STORE_COLUMN_COUNT, types);
+ g_free(types);
+
+ gtk_tree_sortable_set_sort_func (
+ GTK_TREE_SORTABLE (store),
+ STORE_COLUMN_FILE_NAME,
+ (GtkTreeIterCompareFunc) tree_store_iter_compare_func,
+ NULL,
+ NULL
+ );
+
+ gtk_tree_sortable_set_sort_column_id (
+ GTK_TREE_SORTABLE (store),
+ STORE_COLUMN_FILE_NAME,
+ GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID
+ );
+
+ /* Setting up TreeModelFilter */
+ model = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
+ gtk_tree_model_filter_set_visible_func (
+ GTK_TREE_MODEL_FILTER (model),
+ (GtkTreeModelFilterVisibleFunc) tree_model_filter_file_visible_func,
+ (gpointer) tview,
+ NULL
+ );
+ gtk_tree_view_set_model (tview, model);
+
+ /* Setting up private */
+ tfview->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ tfview,
+ TYPE_TREE_FOLDER_VIEW,
+ TreeFolderViewPrivate
+ );
+}
+
+static void
+tree_folder_view_set_property (
+ GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec
+) {
+ TreeFolderView *tfview = TREE_FOLDER_VIEW (object);
+
+ switch (prop_id) {
+ case PROP_FILTER:
+ tree_folder_view_set_filter (
+ tfview,
+ GTK_FILE_FILTER (g_value_get_object (value))
+ );
+ break;
+ case PROP_FILENAME:
+ tree_folder_view_set_filename (
+ tfview,
+ g_value_get_string (value)
+ );
+ break;
+ case PROP_MODEL:
+ g_warning(
+ "\"model\" property of GtkTreeView is overriden "
+ "in TreeFolderView and shouldn't be changed"
+ );
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tree_folder_view_get_property (
+ GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec
+) {
+ TreeFolderView *tfview = TREE_FOLDER_VIEW (object);
+
+ switch (prop_id) {
+ case PROP_FILTER:
+ g_value_set_object (
+ value, tree_folder_view_get_filter (tfview)
+ );
+ break;
+ case PROP_FILENAME:
+ g_value_set_string (
+ value, tree_folder_view_get_filename (tfview)
+ );
+ break;
+ case PROP_MODEL:
+ g_value_set_object (
+ value, gtk_tree_view_get_model (GTK_TREE_VIEW (tfview))
+ );
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tree_folder_view_class_init (
+ TreeFolderViewClass *klass,
+ gpointer class_data
+) {
+ GObjectClass *object_class;
+ GtkTreeViewClass *tree_view_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ tree_view_class = GTK_TREE_VIEW_CLASS (klass);
+
+ tree_view_class->row_activated = tree_folder_view_row_activated;
+ tree_view_class->row_expanded = tree_folder_view_row_expanded;
+
+ tfview_signals [SIGNAL_FILE_ACTIVATED] = g_signal_new (
+ "file-activated",
+ TYPE_TREE_FOLDER_VIEW,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (TreeFolderViewClass, file_activated),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING
+ );
+
+ g_type_class_add_private (klass, sizeof (TreeFolderViewPrivate));
+
+ object_class->set_property = tree_folder_view_set_property;
+ object_class->get_property = tree_folder_view_get_property;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_FILTER,
+ g_param_spec_object (
+ "filter",
+ "File filter",
+ "File filter for selecting "
+ "which files should be displayed.",
+ GTK_TYPE_FILE_FILTER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ )
+ );
+
+ g_object_class_install_property (
+ object_class,
+ PROP_FILENAME,
+ g_param_spec_string (
+ "filename",
+ "Folder filename",
+ "Full path to a folder "
+ "which contents are displayed.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT
+ )
+ );
+
+ g_object_class_override_property (
+ object_class,
+ PROP_MODEL,
+ "model"
+ );
+}
+
+GType
+tree_folder_view_get_type (void) {
+ static GType tfview_type = 0;
+
+ if (tfview_type == 0) {
+ const GTypeInfo tfview_info = {
+ sizeof (TreeFolderViewClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) tree_folder_view_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (TreeFolderView),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) tree_folder_view_init,
+ NULL, /* value_table */
+ };
+
+ tfview_type = g_type_register_static (
+ GTK_TYPE_TREE_VIEW,
+ "TreeFolderView",
+ &tfview_info,
+ 0
+ );
+
+ }
+
+ return tfview_type;
+}
+
+GtkWidget *
+tree_folder_view_new (void) {
+ GtkWidget *tfview = GTK_WIDGET (
+ g_object_new (TYPE_TREE_FOLDER_VIEW, NULL)
+ );
+ return tfview;
+}
diff --git a/saedit/valg b/saedit/valg
deleted file mode 100755
index 8726b81..0000000
--- a/saedit/valg
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-valgrind --read-var-info=yes --track-origins=yes --malloc-fill=11 --free-fill=55 --show-reachable=yes --leak-check=full --leak-resolution=high --partial-loads-ok=yes --error-limit=no --num-callers=30 "./saedit" 2>./valg.log
diff --git a/saedit/xml.c b/saedit/xml.c
index eefc536..90c4d76 100644
--- a/saedit/xml.c
+++ b/saedit/xml.c
@@ -1,49 +1,100 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
-
#include "xml.h"
+#include <string.h>
-gchar** xml_attr_new(const gchar *name, const gchar *value) {
+gchar **
+xml_attr_new (
+ const gchar *name,
+ const gchar *value
+) {
gchar **attr = g_new0(gchar*, 2);
attr[0] = g_strdup(name);
attr[1] = g_strdup(value);
return attr;
}
-gchar* xml_node_get_attr_value(const XMLNode *node, const gchar *attr_name) {
+gchar *
+xml_node_get_attr_value (
+ const XMLNode *node,
+ const gchar *attr_name
+) {
gchar **attr = node->attributes;
guint i;
for (i = 0; i < g_strv_length(attr); i += 2)
if (g_str_equal(attr[i], attr_name))
- return attr[i + 1];
+ return g_strdup (attr[i + 1]);
return NULL;
}
+gint
+xml_node_get_int_attr_value (
+ const XMLNode *node,
+ const gchar *attr_name,
+ gint retval
+) {
+ gchar *val = xml_node_get_attr_value (node, attr_name);
+
+ if (val != NULL) {
+ try_strtoint (val, &retval);
+ g_free (val);
+ }
+
+ return retval;
+}
-gint xml_node_compare_with_name_func(gconstpointer a, gconstpointer b) {
- return g_strcmp0((gchar *)b, ((XMLNode *)a)->name);
+gint
+xml_node_get_int_attr_value_limited (
+ const XMLNode *node,
+ const gchar *attr_name,
+ gint retval,
+ gint lower,
+ gint upper
+) {
+ g_assert (lower <= upper);
+
+ retval = xml_node_get_int_attr_value (
+ node, attr_name, retval
+ );
+
+ if (retval < lower)
+ retval = lower;
+
+ if (retval > upper)
+ retval = upper;
+ return retval;
}
-gint xml_node_compare_with_action_node_by_imageset_name_func(gconstpointer a, gconstpointer b) {
- return g_strcmp0("action", ((XMLNode *)a)->name) ||
- g_strcmp0((gchar *)b, xml_node_get_attr_value((XMLNode *)a, "imageset"));
+gint
+xml_node_compare_with_name_func (
+ gconstpointer a,
+ gconstpointer b
+) {
+ return g_strcmp0((gchar *) b, ((XMLNode *) a)->name);
}
-gint xml_node_compare_with_attr_func(const XMLNode *node, const gchar **attr) {
+gint
+xml_node_compare_with_action_node_by_imageset_name_func (
+ gconstpointer a,
+ gconstpointer b
+) {
+ return g_strcmp0("action", ((XMLNode *) a)->name) ||
+ g_strcmp0((gchar *) b, xml_node_get_attr_value((XMLNode *) a, "imageset"));
+}
+
+gint
+xml_node_compare_with_attr_func (
+ const XMLNode *node,
+ const gchar **attr
+) {
return g_strcmp0(attr[1],
xml_node_get_attr_value(node, attr[0]));
}
static GMarkupParser parser;
+void _xml_free_g_func (XMLNode *node, gpointer user_data) {
+ xml_free (node);
+}
+
void xml_free (XMLNode *node) {
g_free (node->name);
@@ -51,7 +102,7 @@ void xml_free (XMLNode *node) {
g_strfreev (node->attributes);
- g_list_foreach (node->sub_nodes, (GFunc) xml_free, NULL);
+ g_list_foreach (node->sub_nodes, (GFunc) _xml_free_g_func, NULL);
g_list_free (node->sub_nodes);
g_slice_free (XMLNode, node);
@@ -62,16 +113,18 @@ static void _start_root_element_cb ( GMarkupParseContext *context,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
- GError **error) {
+ GError **error
+) {
XMLNode **node = (XMLNode **) user_data;
- g_assert (node != NULL);
-
- XMLNode *p = g_slice_new0 (XMLNode);
+ XMLNode *p;
+ GArray *attributes;
+ g_assert (node != NULL);
+ p = g_slice_new0 (XMLNode);
p->name = g_strdup (element_name);
- GArray *attributes = g_array_new (TRUE, TRUE, sizeof (gchar *));
+ attributes = g_array_new (TRUE, TRUE, sizeof (gchar *));
while (*attribute_names != NULL && *attribute_values != NULL) {
gchar *p2;
p2 = g_strdup (*attribute_names++);
@@ -87,28 +140,31 @@ static void _start_root_element_cb ( GMarkupParseContext *context,
}
-static void _start_element_cb ( GMarkupParseContext *context,
- const gchar *element_name,
- const gchar **attribute_names,
- const gchar **attribute_values,
- gpointer user_data,
- GError **error) {
-
- XMLNode *node = (XMLNode *) user_data;
+static void _start_element_cb (
+ GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error
+) {
+ XMLNode *p, *node = (XMLNode *) user_data;
+ GArray *attributes;
+ gint char_n, line_n;
if (node->text) {
g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, " ");
return;
}
- XMLNode *p = g_slice_new0 (XMLNode);
+ p = g_slice_new0 (XMLNode);
node->sub_nodes = g_list_append (node->sub_nodes, p);
g_markup_parse_context_push (context, &parser, p);
p->name = g_strdup (element_name);
- GArray *attributes = g_array_new (TRUE, TRUE, sizeof (gchar *));
+ attributes = g_array_new (TRUE, TRUE, sizeof (gchar *));
while (*attribute_names != NULL && *attribute_values != NULL) {
gchar *p2;
p2 = g_strdup (*attribute_names++);
@@ -119,15 +175,17 @@ static void _start_element_cb ( GMarkupParseContext *context,
p->attributes = (gchar **)g_array_free (attributes, FALSE);
- gint char_n, line_n;
g_markup_parse_context_get_position(context, &line_n, &char_n);
- p->line_number = line_n - (char_n <= 1 ? 1 : 0);
+ p->line_no = line_n - (char_n <= 1 ? 1 : 0);
}
-static void _end_element_cb ( GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data,
- GError **error) {
+static void
+_end_element_cb (
+ GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error
+) {
XMLNode *p = (XMLNode *) g_markup_parse_context_pop (context);
if (p->text && p->sub_nodes) {
@@ -139,8 +197,11 @@ static void _end_element_cb ( GMarkupParseContext *context,
}
}
-static gboolean _is_space ( const gchar *text,
- gsize text_len) {
+static gboolean
+_is_space (
+ const gchar *text,
+ gsize text_len
+) {
gsize i = 0;
for (i = 0; text[i] != '\0' && i < text_len; i++) {
@@ -158,11 +219,14 @@ static gboolean _is_space ( const gchar *text,
return TRUE;
}
-static void _text_cb ( GMarkupParseContext *context,
- const gchar *text,
- gsize text_len,
- gpointer user_data,
- GError **error) {
+static void
+_text_cb (
+ GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error
+) {
XMLNode *p = (XMLNode *)user_data;
if (_is_space (text, text_len)) {
@@ -185,15 +249,10 @@ static GMarkupParser parser = {
0,
};
-XMLNode *xml_parse_file (const gchar *filename) {
+XMLNode *
+xml_parse_file (const gchar *filename) {
gboolean retval = FALSE;
GError *error = NULL;
- FILE *pf = fopen (filename, "r");
-
- if (pf == NULL) {
- return NULL;
- }
-
GMarkupParseContext *context;
XMLNode *node;
@@ -205,6 +264,12 @@ XMLNode *xml_parse_file (const gchar *filename) {
0,
};
+ FILE *pf = fopen (filename, "r");
+
+ if (pf == NULL) {
+ return NULL;
+ }
+
do {
context = g_markup_parse_context_new (&root_parser, 0, &node, 0);
@@ -238,7 +303,8 @@ XMLNode *xml_parse_file (const gchar *filename) {
return NULL;
}
-XMLNode *xml_parse_buffer (const gchar *buffer, GError **error) {
+XMLNode *
+xml_parse_buffer (const gchar *buffer, GError **error) {
gboolean retval;
GMarkupParseContext *context;
@@ -266,7 +332,6 @@ XMLNode *xml_parse_buffer (const gchar *buffer, GError **error) {
return node;
} while (0);
- //g_warning ("Parse buffer failed: %s", (*error)->message);
g_markup_parse_context_free (context);
return NULL;
}
@@ -279,7 +344,11 @@ static void output_indent (int level, GString *output) {
}
}
-static void xml_output_indent (const XMLNode *node, int level, GString *output) {
+static void xml_output_indent (
+ const XMLNode *node,
+ int level,
+ GString *output
+) {
gchar **attrs;
output_indent (level, output);
@@ -293,8 +362,8 @@ static void xml_output_indent (const XMLNode *node, int level, GString *output)
}
if (node->sub_nodes != NULL) {
- g_string_append (output, ">\n");
GList *sub_node;
+ g_string_append (output, ">\n");
for (sub_node = node->sub_nodes; sub_node != NULL; sub_node = sub_node->next) {
xml_output_indent (sub_node->data, level + 1, output);
diff --git a/saedit/xml.h b/saedit/xml.h
index 584c568..2622f17 100644
--- a/saedit/xml.h
+++ b/saedit/xml.h
@@ -1,13 +1,3 @@
-/*=======================================*\
-| ____ ____ |
-| / \ /\ | |
-| \____ / \ |____ |
-| \ /____\ | |
-| \____/prite / \nimation |____ditor |
-| |
-| Copyleft Vasily_Makarov 2011 |
-| |
-\*=======================================*/
#ifndef XML_H
#define XML_H
@@ -18,25 +8,63 @@
typedef struct {
gchar *name;
gchar *text;
- gchar **attributes;
- int line_number;
- GList *sub_nodes;
+ gchar **attributes;
+ gint line_no;
+ GList *sub_nodes;
} XMLNode;
-XMLNode *xml_parse_file (const gchar *name);
+XMLNode *
+xml_parse_file (
+ const gchar *name
+);
-XMLNode *xml_parse_buffer (const gchar *buffer, GError **error);
+XMLNode *
+xml_parse_buffer (
+ const gchar *buffer,
+ GError **error
+);
-void xml_free (XMLNode *node);
+void xml_free (XMLNode *node);
+void xml_output (
+ const XMLNode *node,
+ GString *output
+);
+
+gchar **
+xml_attr_new (
+ const gchar *name,
+ const gchar *value
+);
+
+gchar *
+xml_node_get_attr_value (
+ const XMLNode *node,
+ const gchar *attr_name
+);
+
+gint
+xml_node_get_int_attr_value (
+ const XMLNode *node,
+ const gchar *attr_name,
+ gint retval
+);
+
+gint
+xml_node_get_int_attr_value_limited (
+ const XMLNode *node,
+ const gchar *attr_name,
+ gint retval,
+ gint lower,
+ gint upper
+);
-void xml_output (const XMLNode *node,
- GString *output);
-gchar **xml_attr_new(const gchar *name, const gchar *value);
-gchar *xml_node_get_attr_value(const XMLNode *node, const gchar *attr_name);
gint xml_node_compare_with_name_func(gconstpointer a, gconstpointer b);
+
+/*
gint xml_node_compare_with_action_node_by_imageset_name_func(gconstpointer a, gconstpointer b);
gint xml_node_compare_with_attr_func(const XMLNode *node, const gchar **attr);
+*/
#endif
diff --git a/saedit/xmlsetup.c b/saedit/xmlsetup.c
new file mode 100644
index 0000000..1036436
--- /dev/null
+++ b/saedit/xmlsetup.c
@@ -0,0 +1,165 @@
+#include "main.h"
+#include "xml.h"
+#include "xmlsetup.h"
+#include "config.h"
+#include "drawfuncs.h"
+
+GList *_xml_setup_layers = NULL;
+GList *_xml_setup_interactors = NULL;
+
+void
+xml_setup_setup (XMLNode *node) {
+ GList *l;
+
+ g_return_if_fail (g_strcmp0 (node->name, "saedit") == 0);
+
+ for (l = node->sub_nodes; l != NULL; l = l->next) {
+ XMLNode *current = (XMLNode *) l->data;
+
+ if (g_strcmp0 (current->name, "layer") == 0) {
+ SDALayer *layer = NULL;
+ gint z_index = -1;
+ gint offset_x, offset_y;
+ gchar *type = xml_node_get_attr_value (current, "type");
+
+ if (type == NULL) {
+ /* TODO: layer type is not specified */
+ continue;
+ }
+
+ if (g_strcmp0 (type, "repeater") == 0) {
+ Interactor **repeater;
+ SpriteContext *repeater_context;
+ XMLNode *sprite = NULL;
+ gchar *file;
+
+ file = xml_node_get_attr_value (current, "file");
+
+ if (file == NULL) {
+ /* TODO: file is not specified */
+ } else {
+ gchar *filename =
+ config_data_path_get_full_sprite_path (file);
+
+ sprite = xml_parse_file (filename);
+
+
+ g_free (file);
+ g_free (filename);
+ }
+
+ repeater_context = sprite_context_new (
+ config_keys_get_data_folder_path ()
+ );
+
+ sprite_context_add_sprite (repeater_context, sprite, 0);
+ repeater = (Interactor **) g_new0 (gpointer, 1);
+ *repeater = interactor_new (repeater_context);
+
+ layer = sda_layer_new (
+ interactor_sprite_layer_draw_func,
+ repeater
+ );
+
+ interactor_add_repeater (interactor, *repeater);
+
+ _xml_setup_interactors = g_list_append (
+ _xml_setup_interactors,
+ repeater
+ );
+ } else
+ if (g_strcmp0 (type, "background") == 0) {
+ GdkRGBA *rgba = (GdkRGBA *) g_new0 (GdkRGBA, 1);
+ gchar *color;
+
+ color = xml_node_get_attr_value (current, "color");
+
+ if (color != NULL && gdk_rgba_parse (rgba, color)) {
+ layer = sda_layer_new (
+ background_layer_draw_func,
+ rgba
+ );
+ } else {
+ /* TODO: color not specified or wrong */
+ }
+ } else {
+ /* TODO: unknown type of layer */
+ }
+
+ g_free (type);
+
+ if (layer == NULL) {
+ continue;
+ }
+
+ z_index = xml_node_get_int_attr_value (
+ current,
+ "zindex",
+ z_index
+ );
+
+ offset_x = xml_node_get_int_attr_value (
+ current,
+ "offsetX",
+ 0
+ );
+
+ offset_y = xml_node_get_int_attr_value (
+ current,
+ "offsetY",
+ 0
+ );
+
+ g_object_set (
+ G_OBJECT (layer),
+ "z-index", z_index,
+ "offset-x", offset_x,
+ "offset-y", offset_y,
+ NULL
+ );
+
+ sprite_drawing_area_add_layer (
+ SPRITE_DRAWING_AREA (d_area),
+ layer
+ );
+
+ _xml_setup_layers = g_list_append (
+ _xml_setup_layers,
+ layer
+ );
+ } else {
+ /* TODO: unknown tag in the setup */
+ }
+ }
+}
+
+void
+xml_setup_clear () {
+ GList *l;
+
+ for (l = _xml_setup_interactors; l != NULL; l = l->next) {
+ Interactor **intr = (Interactor **) l->data;
+ interactor_free (*intr);
+ }
+
+ for (l = _xml_setup_layers; l != NULL; l = l->next) {
+ SDALayer *layer = (SDALayer *) l->data;
+ gpointer user_data;
+
+ g_object_get (layer, "user-data", &user_data, NULL);
+ g_free (user_data);
+
+ sprite_drawing_area_remove_layer (
+ SPRITE_DRAWING_AREA (d_area),
+ layer
+ );
+
+ g_object_unref (layer);
+ }
+
+ g_list_free (_xml_setup_interactors);
+ g_list_free (_xml_setup_layers);
+
+ _xml_setup_interactors = NULL;
+ _xml_setup_layers = NULL;
+}
diff --git a/saedit/xmlsetup.h b/saedit/xmlsetup.h
new file mode 100644
index 0000000..37c9b65
--- /dev/null
+++ b/saedit/xmlsetup.h
@@ -0,0 +1,10 @@
+#ifndef _XML_SETUP_H_
+#define _XML_SETUP_H_
+
+void
+xml_setup_setup (XMLNode *node);
+
+void
+xml_setup_clear (void);
+
+#endif