From b9d57b7ee57e79820c97c4d452c07972b73f3c7c Mon Sep 17 00:00:00 2001 From: Jesusaves Date: Tue, 22 Feb 2022 07:09:31 -0300 Subject: Include saedit2 --- saedit/.gitignore | 6 + saedit/.gitlab-ci.yml | 6 + saedit/LICENSE | 674 ++++++++++++++++++++++++++ saedit/Makefile | 33 ++ saedit/README.md | 7 + saedit/action.c | 176 +++++++ saedit/action.h | 59 +++ saedit/animation.c | 386 +++++++++++++++ saedit/animation.h | 59 +++ saedit/buffer.c | 87 ++++ saedit/buffer.h | 22 + saedit/callbacks.c | 432 +++++++++++++++++ saedit/callbacks.h | 96 ++++ saedit/common.c | 22 + saedit/common.h | 12 + saedit/config.c | 191 ++++++++ saedit/config.h | 32 ++ saedit/context.c | 227 +++++++++ saedit/context.h | 50 ++ saedit/drawfuncs.c | 107 ++++ saedit/drawfuncs.h | 33 ++ saedit/errors.c | 23 + saedit/errors.h | 12 + saedit/file.c | 103 ++++ saedit/file.h | 16 + saedit/glade.sh | 3 + saedit/glade/saedit-catalog.xml | 26 + saedit/iface.ui | 577 ++++++++++++++++++++++ saedit/imageset.c | 130 +++++ saedit/imageset.h | 50 ++ saedit/interactor.c | 439 +++++++++++++++++ saedit/interactor.h | 110 +++++ saedit/logo.svg | 657 +++++++++++++++++++++++++ saedit/main.c | 235 +++++++++ saedit/main.h | 51 ++ saedit/spritedrawingarea/sdalayer.c | 271 +++++++++++ saedit/spritedrawingarea/sdalayer.h | 54 +++ saedit/spritedrawingarea/sdalayerprivate.h | 19 + saedit/spritedrawingarea/spritedrawingarea.c | 437 +++++++++++++++++ saedit/spritedrawingarea/spritedrawingarea.h | 71 +++ saedit/treefolderview/treefolderview.c | 400 +++++++++++++++ saedit/treefolderview/treefolderview.h | 64 +++ saedit/treefolderview/treefolderviewprivate.h | 45 ++ saedit/treefolderview/type.c | 250 ++++++++++ saedit/xml.c | 385 +++++++++++++++ saedit/xml.h | 70 +++ saedit/xmlsetup.c | 165 +++++++ saedit/xmlsetup.h | 10 + 48 files changed, 7390 insertions(+) create mode 100644 saedit/.gitignore create mode 100644 saedit/.gitlab-ci.yml create mode 100644 saedit/LICENSE create mode 100644 saedit/Makefile create mode 100644 saedit/README.md create mode 100644 saedit/action.c create mode 100644 saedit/action.h create mode 100644 saedit/animation.c create mode 100644 saedit/animation.h create mode 100644 saedit/buffer.c create mode 100644 saedit/buffer.h create mode 100644 saedit/callbacks.c create mode 100644 saedit/callbacks.h create mode 100644 saedit/common.c create mode 100644 saedit/common.h create mode 100644 saedit/config.c create mode 100644 saedit/config.h create mode 100644 saedit/context.c create mode 100644 saedit/context.h create mode 100644 saedit/drawfuncs.c create mode 100644 saedit/drawfuncs.h create mode 100644 saedit/errors.c create mode 100644 saedit/errors.h create mode 100644 saedit/file.c create mode 100644 saedit/file.h create mode 100755 saedit/glade.sh create mode 100644 saedit/glade/saedit-catalog.xml create mode 100644 saedit/iface.ui create mode 100644 saedit/imageset.c create mode 100644 saedit/imageset.h create mode 100644 saedit/interactor.c create mode 100644 saedit/interactor.h create mode 100644 saedit/logo.svg create mode 100644 saedit/main.c create mode 100644 saedit/main.h create mode 100644 saedit/spritedrawingarea/sdalayer.c create mode 100644 saedit/spritedrawingarea/sdalayer.h create mode 100644 saedit/spritedrawingarea/sdalayerprivate.h create mode 100644 saedit/spritedrawingarea/spritedrawingarea.c create mode 100644 saedit/spritedrawingarea/spritedrawingarea.h create mode 100644 saedit/treefolderview/treefolderview.c create mode 100644 saedit/treefolderview/treefolderview.h create mode 100644 saedit/treefolderview/treefolderviewprivate.h create mode 100644 saedit/treefolderview/type.c create mode 100644 saedit/xml.c create mode 100644 saedit/xml.h create mode 100644 saedit/xmlsetup.c create mode 100644 saedit/xmlsetup.h 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/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. + 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 . + +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 +. + + 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 +. diff --git a/saedit/Makefile b/saedit/Makefile new file mode 100644 index 0000000..207eeaf --- /dev/null +++ b/saedit/Makefile @@ -0,0 +1,33 @@ +CC ?= gcc + +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 + +LDFLAGS += `pkg-config --libs gtk+-3.0 gtksourceview-3.0` +LDFLAGS += -rdynamic -Ltreefolderview -Lspritedrawingarea + +FLAGS = ${CFLAGS} ${LDFLAGS} + +all: saedit glade/libsaedit.so + +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} + +clean: + rm -f *.o */*.o *~ glade/libsaedit.so saedit diff --git a/saedit/README.md b/saedit/README.md new file mode 100644 index 0000000..4be496d --- /dev/null +++ b/saedit/README.md @@ -0,0 +1,7 @@ +# Instructions + +1. Run make +2. Fix dependencies until if exits successfully +3. Run ./saedit binary, which will have been created if it ran successfully. + +Bugs report to Vasily. 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 +#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 + +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 +#include +#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/buffer.c b/saedit/buffer.c new file mode 100644 index 0000000..5c51bcc --- /dev/null +++ b/saedit/buffer.c @@ -0,0 +1,87 @@ +#include +#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 + +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 (""); + 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 +#include + +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 new file mode 100644 index 0000000..1715e41 --- /dev/null +++ b/saedit/common.h @@ -0,0 +1,12 @@ +#ifndef COMMON_H +#define COMMON_H + +#include + +gboolean +try_strtoint ( + const gchar *str, + gint *result +); + +#endif diff --git a/saedit/config.c b/saedit/config.c new file mode 100644 index 0000000..1e20411 --- /dev/null +++ b/saedit/config.c @@ -0,0 +1,191 @@ +#include "config.h" +#include "xml.h" +#include +#include + +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 + ); + + g_free (dir); + return result; +} + +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; +} + +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) { + list = node->sub_nodes; + while (TRUE) { + list = g_list_find_custom ( + list, + "option", + xml_node_compare_with_name_func + ); + + if (list == NULL) + break; + + 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; + } + } + + return g_strdup (""); +} + +gchar * +config_data_path_get_full_sprite_path (const gchar *rel_path) { + gchar *data_folder, *sprites_path, *filename; + + data_folder = config_keys_get_data_folder_path (); + sprites_path = config_data_paths_get_sprites_path (); + + filename = g_strjoin ( + "/", + data_folder, + sprites_path, + rel_path, + NULL + ); + + g_free (sprites_path); + g_free (data_folder); + + return filename; +} diff --git a/saedit/config.h b/saedit/config.h new file mode 100644 index 0000000..cae98bd --- /dev/null +++ b/saedit/config.h @@ -0,0 +1,32 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#include + +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 +#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, + "%s error: %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 + +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 +#include "errors.h" +#include "buffer.h" +#include "main.h" +#include "file.h" + +#include + + +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 @@ + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + 1 + 10 + 1 + 0.10000000000000001 + 0.5 + + + + False + logo.svg + + + + + + + + True + True + vertical + + + True + False + + + True + False + _File + True + + + True + False + accelgroup1 + + + gtk-new + True + False + True + True + + + + + + + gtk-open + True + False + True + True + + + + + + + gtk-save + True + False + True + True + + + + + + + gtk-save-as + True + False + True + True + + + + + + + True + False + + + + + True + False + Choose data folder... + + + + + + True + False + + + + + gtk-quit + True + False + True + True + + + + + + + + + True + False + _View + True + + + True + False + + + True + False + Reset to center + True + + + + + + + True + False + + + + + True + False + Tile grid + True + True + + + + + + + True + False + Pixel grid + True + + + + + + + + + + + True + False + _Help + True + + + True + False + + + gtk-about + True + False + True + True + + + + + + + + + + False + True + 0 + + + + + True + True + 1 + + + 100 + True + True + + + True + True + filefilter-xml + /home/ + + + + + + + + + False + False + + + + + 100 + True + True + vertical + 100 + + + 100 + True + False + vertical + + + True + False + + + True + False + GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK + + + True + True + 0 + + + + + True + True + vertical + zoom-adjustment + True + False + 1 + False + + + False + True + 1 + + + + + True + True + 0 + + + + + True + False + + + True + False + Action: + + + False + True + 5 + 0 + + + + + True + False + liststore-actions + 2 + + + + + 0 + + + + + 1 + right + + + 1 + + + + + True + True + 1 + + + + + True + False + Direction: + + + False + True + 5 + 2 + + + + + True + False + on + + + + True + True + 3 + + + + + False + True + 1 + + + + + True + False + + + True + False + Parse buffer + gtk-execute + + + + False + True + + + + + True + False + + + False + True + + + + + True + False + First frame + gtk-goto-first + + + + True + True + + + + + True + False + False + Previous frame + gtk-go-back + + + True + True + + + + + True + False + Play/Pause + True + media-playback-start + + + + True + True + + + + + True + False + Next frame + True + gtk-go-forward + + + + True + True + + + + + True + False + False + Last frame + True + gtk-goto-last + + + True + True + + + + + False + True + 2 + + + + + True + False + + + + + 100 + 100 + True + True + + + True + True + 2 + 2 + sourceview-buffer + True + True + True + 4 + 4 + True + True + + + + + True + False + + + + + True + False + + + + + True + True + 1 + + + + + + + False + True + dialog + window-main + Sprite Animation Editor + alpha2 + Copyleft (É”) Vasily_Makarov + Editor, parser and player for the TMW/Evol project animations. Greatly inspired by the project developers team. + Danil "Vasily Makarov" Sagunov <vasily@evolonline.org> + logo.svg + gpl-3-0 + + + + + + + False + vertical + 2 + + + False + end + + + + + + + + + False + False + 0 + + + + + + + + + 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 + +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 +#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 +#include + +#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/logo.svg b/saedit/logo.svg new file mode 100644 index 0000000..a68864a --- /dev/null +++ b/saedit/logo.svg @@ -0,0 +1,657 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/saedit/main.c b/saedit/main.c new file mode 100644 index 0000000..103e0c4 --- /dev/null +++ b/saedit/main.c @@ -0,0 +1,235 @@ +#include +#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 + +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); + } + + gtk_combo_box_text_remove_all (cb_directions); + gtk_list_store_clear (store_actions); + + if (context != NULL) { + sprite_context_free (context); + context = NULL; + } +} + +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 ( + "", + opened_file_name + strlen (data_folder), + NULL + ); + } else { + file_name = g_strdup (opened_file_name); + } + + if (file_name == NULL) { + file_name = g_strdup ("New animation"); + } + + 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 + ); + + 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 + ); + + sda_layer_set_z_index (pixel_grid_layer, 512 + 1); + sda_layer_set_visible (pixel_grid_layer, FALSE); + + sprite_drawing_area_add_layer ( + SPRITE_DRAWING_AREA (d_area), + pixel_grid_layer + ); + + gtk_widget_show_all (main_window); + + gtk_main(); + + return 0; +} diff --git a/saedit/main.h b/saedit/main.h new file mode 100644 index 0000000..1e482f3 --- /dev/null +++ b/saedit/main.h @@ -0,0 +1,51 @@ +#ifndef _MAIN_H_ +#define _MAIN_H_ + +#include +#include "treefolderview.h" +#include "context.h" +#include "interactor.h" +#include "spritedrawingarea.h" + +GtkWidget *main_window; +GtkWidget *d_area; +GtkWidget *source_view; +GtkWidget *tf_view; +GtkWidget *tbtn_play; + +GtkComboBox *cb_actions; +GtkComboBoxText *cb_directions; +GtkListStore *store_actions; +GtkAdjustment *zoom_adj; + +SpriteContext *context; +Interactor *interactor; + +SDALayer *tile_grid_layer; +SDALayer *pixel_grid_layer; + +void +buffer_set_modified (gboolean modified); + +gboolean +buffer_get_modified (void); + +void +release_context (void); + +void +intr_updated (Interactor *interactor); + +void +update_window_title (void); + +GtkWidget * +window_main_get_source_view (void); + +void +update_window_title (void); + +void +release_context (void); + +#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 +#include + +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, ¢er_x, ¢er_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, ¢er_x, ¢er_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 +#include +#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/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 +#include + +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/xml.c b/saedit/xml.c new file mode 100644 index 0000000..90c4d76 --- /dev/null +++ b/saedit/xml.c @@ -0,0 +1,385 @@ +#include "xml.h" +#include + +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 **attr = node->attributes; + guint i; + for (i = 0; i < g_strv_length(attr); i += 2) + if (g_str_equal(attr[i], attr_name)) + 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_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_name_func ( + gconstpointer a, + gconstpointer b +) { + return g_strcmp0((gchar *) b, ((XMLNode *) a)->name); +} + +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); + + g_free (node->text); + + g_strfreev (node->attributes); + + g_list_foreach (node->sub_nodes, (GFunc) _xml_free_g_func, NULL); + g_list_free (node->sub_nodes); + + g_slice_free (XMLNode, node); +} + +static void _start_root_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; + XMLNode *p; + GArray *attributes; + + g_assert (node != NULL); + + p = g_slice_new0 (XMLNode); + p->name = g_strdup (element_name); + + attributes = g_array_new (TRUE, TRUE, sizeof (gchar *)); + while (*attribute_names != NULL && *attribute_values != NULL) { + gchar *p2; + p2 = g_strdup (*attribute_names++); + g_array_append_val (attributes, p2); + p2 = g_strdup (*attribute_values++); + g_array_append_val (attributes, p2); + } + + p->attributes = (gchar **) g_array_free (attributes, FALSE); + + g_markup_parse_context_push (context, &parser, p); + *node = p; +} + + +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; + } + + 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); + + attributes = g_array_new (TRUE, TRUE, sizeof (gchar *)); + while (*attribute_names != NULL && *attribute_values != NULL) { + gchar *p2; + p2 = g_strdup (*attribute_names++); + g_array_append_val (attributes, p2); + p2 = g_strdup (*attribute_values++); + g_array_append_val (attributes, p2); + } + + p->attributes = (gchar **)g_array_free (attributes, FALSE); + + g_markup_parse_context_get_position(context, &line_n, &char_n); + 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 +) { + XMLNode *p = (XMLNode *) g_markup_parse_context_pop (context); + + if (p->text && p->sub_nodes) { + g_warning ("Error"); + } + + if (p->text == NULL && p->sub_nodes == NULL) { + p->text = g_strdup (""); + } +} + +static gboolean +_is_space ( + const gchar *text, + gsize text_len +) { + gsize i = 0; + + for (i = 0; text[i] != '\0' && i < text_len; i++) { + switch (text[i]) { + case '\t': + case ' ': + case '\n': + case '\r': + continue; + default: + return FALSE; + } + } + + return TRUE; +} + +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)) { + return; + } + + if (p->sub_nodes || p->text) { + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, " "); + return; + } + + p->text = g_strndup (text, text_len); +} + +static GMarkupParser parser = { + _start_element_cb, + _end_element_cb, + _text_cb, + 0, + 0, +}; + +XMLNode * +xml_parse_file (const gchar *filename) { + gboolean retval = FALSE; + GError *error = NULL; + GMarkupParseContext *context; + XMLNode *node; + + static const GMarkupParser root_parser = { + _start_root_element_cb, + _end_element_cb, + _text_cb, + 0, + 0, + }; + + FILE *pf = fopen (filename, "r"); + + if (pf == NULL) { + return NULL; + } + + do { + context = g_markup_parse_context_new (&root_parser, 0, &node, 0); + + while (!feof (pf)) { + gchar buf[1024]; + gssize len = 0; + + len = fread (buf, 1, sizeof (buf), pf); + retval = g_markup_parse_context_parse (context, buf, len, &error); + + if (!retval) + break; + } + fclose (pf); + + if (!retval) + break; + + retval = g_markup_parse_context_end_parse (context, &error); + if (!retval) + break; + + g_markup_parse_context_free (context); + + return node; + } while (0); + + g_warning ("Parse %s failed: %s", filename, error->message); + g_error_free (error); + g_markup_parse_context_free (context); + return NULL; +} + +XMLNode * +xml_parse_buffer (const gchar *buffer, GError **error) { + gboolean retval; + + GMarkupParseContext *context; + XMLNode *node; + + static const GMarkupParser root_parser = { + _start_root_element_cb, + _end_element_cb, + _text_cb, + 0, + 0, + }; + + context = g_markup_parse_context_new (&root_parser, 0, &node, 0); + + do { + retval = g_markup_parse_context_parse (context, buffer, strlen (buffer), error); + if (!retval) + break; + + retval = g_markup_parse_context_end_parse (context, error); + if (!retval) + break; + g_markup_parse_context_free (context); + return node; + } while (0); + + g_markup_parse_context_free (context); + return NULL; +} + + +static void output_indent (int level, GString *output) { + gint i; + for (i = 0; i < level; i++) { + g_string_append (output, " "); + } +} + +static void xml_output_indent ( + const XMLNode *node, + int level, + GString *output +) { + gchar **attrs; + + output_indent (level, output); + g_string_append_printf (output, "<%s", node->name); + + attrs = node->attributes; + + while (attrs != NULL && *attrs != NULL) { + g_string_append_printf (output, " %s", *(attrs++)); + g_string_append_printf (output, "=\"%s\"", *(attrs++)); + } + + if (node->sub_nodes != NULL) { + 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); + } + output_indent (level, output); + g_string_append_printf (output, "\n",node->name); + } else if (node->text != NULL) { + gchar *text = g_markup_escape_text (node->text, -1); + g_string_append_printf (output, ">%s\n", text, node->name); + g_free (text); + } else { + g_string_append (output, "/>\n"); + } +} + +void xml_output (const XMLNode *node, GString *output) { + xml_output_indent (node, 0, output); +} + diff --git a/saedit/xml.h b/saedit/xml.h new file mode 100644 index 0000000..2622f17 --- /dev/null +++ b/saedit/xml.h @@ -0,0 +1,70 @@ +#ifndef XML_H +#define XML_H + +#include +#include +#include "common.h" + +typedef struct { + gchar *name; + gchar *text; + gchar **attributes; + gint line_no; + GList *sub_nodes; +} XMLNode; + + +XMLNode * +xml_parse_file ( + const gchar *name +); + +XMLNode * +xml_parse_buffer ( + const gchar *buffer, + GError **error +); + +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 +); + + +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 -- cgit v1.2.3-60-g2f50