commit d8753fcbe00f9cbbb02b389e1556ad01e47d2079 Author: Juan Date: Sat Oct 12 01:07:35 2013 +0200 Added add project files to repository diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..3374dca --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +Alejandro Tomas Colombini Gomez +Juan Ferrer Toribio +Javier Gallego Ferris diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -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. + + + Copyright (C) + + 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: + + Copyright (C) + 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/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..007e939 --- /dev/null +++ b/INSTALL @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, +Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf limitation. Until the limitation is lifted, you can use +this workaround: + + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..e0c247c --- /dev/null +++ b/Makefile.am @@ -0,0 +1,21 @@ +include $(top_srcdir)/Makefile.decl + +ACLOCAL_AMFLAGS = -I build/m4 +DISTCHECK_CONFIGURE_FLAGS = --enable-vala --enable-gtk-doc + +SUBDIRS = \ + gvn \ + sql \ + db \ + plugin \ + vn \ + module \ + main \ + po \ + vapi \ + glade \ + docs + +EXTRA_DIST = \ + autogen.sh \ + Makefile.decl diff --git a/Makefile.decl b/Makefile.decl new file mode 100644 index 0000000..850631c --- /dev/null +++ b/Makefile.decl @@ -0,0 +1,74 @@ +# Definition of the install directories +# Libraries +if ENABLE_INSTALL + hedera_libdir = $(libdir)/$(PACKAGE) + hedera_datadir = $(datadir)/$(PACKAGE) +else + hedera_libdir = $(abs_top_builddir) + hedera_datadir = $(abs_top_srcdir) +endif + +gvn_libdir = $(hedera_libdir) +sql_libdir = $(hedera_libdir) +vn_libdir = $(hedera_libdir) +field_libdir = $(vn_libdir) +column_libdir = $(vn_libdir) +plugin_libdir = $(hedera_libdir)/plugin + +if ENABLE_INSTALL + db_libdir = $(hedera_libdir) + module_libdir = $(hedera_libdir)/module +else + db_libdir = $(hedera_libdir)/db + module_libdir = $(hedera_libdir)/module/src/.libs +endif + +mysql_libdir = $(plugin_libdir)/mysql +pg_libdir = $(plugin_libdir)/pg + +# Binaries (programs) +hedera_bindir = $(bindir) + +# Headers and development files +hedera_includedir = $(includedir)/$(PACKAGE) + +gvn_includedir = $(hedera_includedir)/gvn +sql_includedir = $(hedera_includedir)/sql +db_includedir = $(hedera_includedir)/db +vn_includedir = $(hedera_includedir)/vn +field_includedir = $(vn_includedir)/field +column_includedir = $(vn_includedir)/column + +pkgconfigdir = $(datadir)/pkgconfig + +if ENABLE_VALA + girdir = $(datadir)/gir-1.0 + typelibdir = $(libdir)/girepository-1.0 + vapidata = $(top_srcdir)/vapi + vapis = $(top_builddir)/vapi + vapidir = $(datadir)/vala-$(VALA_VERSION)/vapi +endif + +gladevn_libdir = $(libdir)/glade/modules +gladevn_datadir = $(datadir)/glade/catalogs + +# Data +vn_datadir = $(hedera_datadir)/vn +vn_imagedir = $(vn_datadir)/image +vn_guidir = $(vn_datadir)/gui + +if ENABLE_INSTALL + vn_xmldir = $(datadir)/xml/$(PACKAGE) + module_querydir = $(hedera_datadir)/module/sql + module_datadir = $(hedera_datadir)/module +else + vn_xmldir = $(vn_datadir)/schema + module_querydir = $(hedera_datadir)/module/sql + module_datadir = $(hedera_datadir)/module/data +endif + +desktopdir = $(datadir)/applications + +vapigen_v = $(vapigen_v_@AM_V@) +vapigen_v_ = $(vapigen_v_@AM_DEFAULT_V@) +vapigen_v_0 = @echo " VAPIGEN $(subst $(vapis)/,,$@)"; diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..673dc8d --- /dev/null +++ b/README @@ -0,0 +1,110 @@ +En sistemas Debian, para configurar la biblioteca para instalarla en una ruta +estándar y con bindings de Vala, partiendo del paquete sin modificar o la copia +del repositorio descargada por primera vez, se deben ejecutar los siguientes +comandos: + $ ./autogen.sh + $ cd build + $ ./configure --enable-vala --prefix='/usr' + $ make + $ make install + +En otras distribuciones o SOs es posible que el valor de la opcion --prefix +deba ser distinto (por defecto es /usr/local). En cualquier caso, para instalar +en la ruta por defecto del sistema se requieren permisos de administrador. + +A continuación se listan las opciones de configuración específicas del paquete, +su valor por defecto y una breve descripción de su efecto. Estas opciones se +emplearán del mismo modo que se ha utilizado la opcion '--enable-vala' en el +ejemplo de instalación anterior. + + --prefix(=/usr/local) + Configura la ruta de instalación. En Debian es recomendable configurar + la biblioteca usando el prefijo '/usr' para evitar el uso explícito de + variables de entorno. En caso de instalar la biblioteca en una ruta distinta + a '/usr', antes de compilar aplicaciones que usen la biblioteca, se deberá dar + el valor '*prefix*/share/pkgconfig' a la variable PKG_CONFIG_PATH y a la + variable LD_RUN_PATH el valor '*prefix*/lib'. Para más información + diríjase a la documentación de la biblioteca. + + --enable-install(=yes) + Desactivando esta opción se configura el paquete para que se ejecute + sin ser instalado. Si por el contrario se deja su valor por defecto, se + producirá un error al ejecutar las aplicaciones que usen de la biblioteca + sin haberla instalado, pero funcionará correctamente tras instalarla. + + --enable-vala(=no) + Con esta opción activa se generarán todos los archivos necesarios para + compilar código Vala que emplee la biblioteca Hedera, al hacer esto se + generan también datos intermedios de GObject Introspection, útiles para + ser usados por enlaces de la biblioteca a otros lenguajes. + + --enable-gtk-doc(=no) + Generar la documentación de la biblioteca al compilar. + + --enable-debug(=no) + Añadir información de debug a la compilación. + +Para mantenimiento, si se cambia la versión de la biblioteca, el cambio debe +reflejarse en los siguientes lugares: + configure.ac en la macro AC_INIT. + La llamada a la macro AC_SUBS que sigue a AC_INIT, indicando la versión con +un subrayado en lugar del punto. + Todos los vapi/*.metadata y vapi/*.deps (tanto en los nombres de fichero +como en el contenido de los .deps). + Los nombres de los ficheros *.pc.in. + +Si se ha obtenido la copia del proyecto desde el repositorio Subversion, también +se contará con el directorio ./debian, que contiene lo necesario para construir +los paquetes .deb para instalar y desinstalar limpiamente la biblioteca en +sistemas que los soporten. Para construir el paquete sin firmarlo (para pruebas +o instalaciones propias mediante este método), se deberá ejecutar +en el directorio raíz del proyecto: + $ debuild -uc -us + +Se debe tener en cuenta que los paquetes y otros ficheros generados se crean en +el directorio padre del raíz. +________________________________________________________________________________ +________________________________________________________________________________ + +To configure the library for install under a standard path on Debian systems +and generate the Vala bindings to it, from the fresh package or on the first +configuration of the repository copy, the next commands must be used: + $ ./autogen.sh + $ cd build + $ ./configure --enable-vala --prefix='/usr' + $ make + $ make install + +In other distributions or OSs the value for the --prefix option may be different +(it defaults to /usr/local). In any case, to install on this path you must have +root access. + +A list of the configuration options of the package is presented below, with +the default values and a brief description of each one. These options will be +used the same way it was done in the installation example above. + + --prefix(=/usr/local) + Configures the installation path. As said before, on Debian it's + recommended to configure the library using the prefix '/usr' to avoid the + additional setting of environment variables. In case you want to install + the library in another path, before compiling some application against the + library you'll have to set the variables PKG_CONFIG_PATH to + 'your_prefix/share/pkgconfig' and LD_RUN_PATH to 'your_prefix/lib'. For + more information on that look at the library's documentation. + + --enable-install(=yes) + Disabling this option, the package configures to be executed without + being installed. If the it's left to its default value, an error will be + prompted while trying to execute applications using the library without + installing, but it will work after installation. + + --enable-vala(=no) + With this option enabled all needed files to compile Vala code using the + Hedera library will be generated, doing so will also produce intermediate + GObject Introspection files useful to use for bindings to other languages. + + --enable-gtk-doc(=no) + Whether or not to generate the library's documentation on compile time. + + --enable-debug(=no) + Add debug information to the compilation. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..5d770a0 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,5 @@ +gtkdocize --copy +autoreconf -fi +glib-gettextize --force --copy +intltoolize --copy --force --automake +automake --add-missing --copy diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..d3454e2 --- /dev/null +++ b/configure.ac @@ -0,0 +1,133 @@ + +AC_INIT([hedera], [1.0]) + +AC_CONFIG_AUX_DIR([build]) +AC_CONFIG_MACRO_DIR([build/m4]) +AC_CONFIG_HEADERS([build/config.h]) +LT_INIT([dlopen]) +AC_PREREQ([2.69]) +AM_SILENT_RULES([yes]) + +# Checks for programs +AC_PROG_CC + +PKG_CHECK_MODULES([glib], [glib-2.0]) +PKG_CHECK_MODULES([gdome], [gdome2]) +PKG_CHECK_MODULES([gtk], [gtk+-3.0]) +PKG_CHECK_MODULES([gladeui], gladeui-2.0) + +GTK_DOC_CHECK([1.14],[--flavour no-tmpl]) +IT_PROG_INTLTOOL([0.40.1], [no-xml]) + +# Checks for Ragel State Machine Compiler +AC_PATH_PROG(RAGEL, [ragel], [no]) +if test x"$RAGEL" = x"no" ; then + AC_MSG_ERROR([Ragel State Machine Compiler (ragel) not found.]) +fi + +# Checks if Vala bindings should be generated +dnl TODO: Use AC_ARG_WITH to pick the Vala version? +AC_MSG_CHECKING([wether to generate Vala bindings...]) +AC_ARG_ENABLE([vala], + [AS_HELP_STRING([--enable-vala], + [Enable vala bindings generation [default = no]])], + [ENABLE_VALA="$enableval"], + [ENABLE_VALA=no]) +AC_MSG_RESULT([$ENABLE_VALA]) + +AM_CONDITIONAL(HAVE_INTROSPECTION, [test FALSE]) + +if test x"$ENABLE_VALA" = x"yes" ; then + GOBJECT_INTROSPECTION_CHECK([1.30.0]) + AC_SUBST([GIR_SCANNER_ARGS], [--warn-all]) + if $HAVE_INTROSPECTION; then + AC_SUBST([VALA_VERSION], [0.20]) + AM_PROG_VALAC([$VALA_VERSION]) + AC_PATH_PROG(VAPIGEN, [vapigen], ["no"]) + fi +fi + +AM_CONDITIONAL(ENABLE_VALA, [test x"$ENABLE_VALA" = x"yes" -a x"$VAPIGEN" != x"no"]) + +# Internationalization +GETTEXT_PACKAGE=hedera +AC_SUBST([GETTEXT_PACKAGE]) +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [GETTEXT package name]) +ALL_LINGUAS="es ca nl" +AM_GLIB_GNU_GETTEXT +AM_XGETTEXT_OPTION([-k_ -kQ_:1g -kN_ -kC_:1c,2 -kNC_:1c,2]) + +# GSettings configuration +GLIB_GSETTINGS + +CFLAGS=" -Wall" + +# Check for debug mode +AC_MSG_CHECKING([whether to build with debug information...]) +AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug], + [Enable debug data generation [default = no]])], + [ENABLE_DEBUG="$enableval"], + [ENABLE_DEBUG=no]) +AC_MSG_RESULT([$ENABLE_DEBUG]) + +if test x"$ENABLE_DEBUG" = x"yes"; then + CFLAGS+=" -ggdb" +fi + +# Check if it will be installed +AC_MSG_CHECKING([whether to configure to install...]) +AC_ARG_ENABLE([install], + [AS_HELP_STRING([--enable-install], + [Enable install configuration [default = yes]])], + [ENABLE_INSTALL="$enableval"], + [ENABLE_INSTALL=yes]) +AC_MSG_RESULT([$installit]) +AM_CONDITIONAL(ENABLE_INSTALL, [test x"$ENABLE_INSTALL" = x"yes"]) + +if test x"$ENABLE_INSTALL" = x"yes"; then + CFLAGS+=" -O3" +else + CFLAGS+=" -O0" +fi + +AC_SUBST([CPPFLAGS]) +AC_SUBST([CFLAGS]) +AC_SUBST([LDFLAGS]) + +# Portability check disabled for GTK-DOC (can be changed if needed) +AM_INIT_AUTOMAKE([-Wno-portability]) +AC_CONFIG_FILES([ + Makefile + gvn/gvn.pc + sql/sql.pc + db/db.pc + vn/vn.pc + main/hedera.pc + gvn/Makefile + sql/Makefile + sql/parser/Makefile + db/Makefile + plugin/Makefile + plugin/pg/Makefile + plugin/mysql/Makefile + vn/Makefile + vn/field/Makefile + vn/column/Makefile + module/Makefile + module/src/Makefile + module/data/Makefile + module/sql/Makefile + main/Makefile + main/vn-hedera.desktop + vapi/Makefile + glade/Makefile + docs/Makefile + docs/reference/Makefile + docs/reference/hedera/Makefile + po/Makefile.in +]) + +AC_SUBST([uVERSION], [${VERSION//./_}]) + +AC_OUTPUT diff --git a/db/Makefile.am b/db/Makefile.am new file mode 100644 index 0000000..480e18a --- /dev/null +++ b/db/Makefile.am @@ -0,0 +1,100 @@ +include $(top_srcdir)/Makefile.decl + +db_lib_LTLIBRARIES = libdb.la +db_include_HEADERS = \ + db.h \ + db-iter.h \ + db-calc.h \ + db-iterator.h \ + db-param.h \ + db-request.h \ + db-conn.h \ + db-row.h \ + db-result.h \ + db-result-set.h \ + db-model.h \ + db-model-holder.h \ + db-file-loader.h \ + db-plugin.h + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(DEFINES) \ + $(glib_CFLAGS) +libdb_la_LIBADD = \ + $(glib_LIBS) \ + $(top_builddir)/sql/libsql.la +libdb_la_SOURCES = \ + $(db_include_HEADERS) \ + db-iter.c \ + db-calc.c \ + db-iterator.c \ + db-param.c \ + db-request.c \ + db-conn.c \ + db-row.c \ + db-result.c \ + db-result-set.c \ + db-model.c \ + db-model-holder.c \ + db-file-loader.c \ + db-plugin.c + +if ENABLE_INSTALL +DEFINES = \ + -D_PLUGIN_DIR=\"$(plugin_libdir)/%s\" +else +DEFINES = \ + -D_PLUGIN_DIR=\"$(plugin_libdir)/%s/.libs\" +endif + +pkgconfig_DATA = db.pc + +EXTRA_DIST = db.pc.in + +DISTCLEANFILES = db.pc + +if ENABLE_VALA +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_SCANNER_ARGS = $(GIR_SCANNER_ARGS) + +INTROSPECTION_COMPILER_ARGS = \ + --includedir=$(top_builddir)/gvn \ + --includedir=$(top_builddir)/sql + +introspection_sources = $(filter-out db-row.*,$(libdb_la_SOURCES)) + +Db-$(VERSION).gir: $(db_lib_LTLIBRARIES) $(top_builddir)/sql/Sql-$(VERSION).gir + Db_@uVERSION@_gir_SCANNERFLAGS = \ + --include-uninstalled=$(top_builddir)/gvn/Gvn-$(VERSION).gir \ + --include-uninstalled=$(top_builddir)/sql/Sql-$(VERSION).gir + Db_@uVERSION@_gir_CFLAGS = -I$(top_srcdir) + Db_@uVERSION@_gir_LIBS = $(db_lib_LTLIBRARIES) + Db_@uVERSION@_gir_FILES = $(introspection_sources) + Db_@uVERSION@_gir_EXPORT_PACKAGES = db + INTROSPECTION_GIRS = Db-$(VERSION).gir + +gir_DATA = $(INTROSPECTION_GIRS) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES = $(gir_DATA) $(typelib_DATA) + +$(vapis)/db.vapi: $(INTROSPECTION_GIRS) $(vapidata)/Db-$(VERSION).metadata + $(vapigen_v)$(VAPIGEN) -q \ + --directory $(vapis) \ + --vapidir $(vapis) \ + --girdir $(top_builddir)/gvn \ + --girdir $(top_builddir)/sql \ + --metadatadir $(vapidata) \ + --library db \ + Db-$(VERSION).gir + +vapi_DATA = $(vapis)/db.vapi + +CLEANFILES += $(vapis)/$(vapi_DATA) + +endif +endif diff --git a/db/db-calc.c b/db/db-calc.c new file mode 100644 index 0000000..b402a8f --- /dev/null +++ b/db/db-calc.c @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-calc.h" +#include "db-row.h" + +/** + * SECTION: db-calc + * @short_description: the result of an operation made over a column or between + * columns + * @title: DbCalc + * + * A #GvnParam that holds the result of the user-given operation applied over + * a given column of a #DbModel. When any of these values is modified, #DbCalc + * updates its value according to the changes. + * + * #DbCalc has as built-in operations the sumatory and average that can be + * selected passing #DB_CALC_SUM OR #DB_CALC_AVG, respectively. + * + * While creating a #DbCalc it's possible to specify either a single column + * number, to calculate using only this column and no additional operations, or + * a #DbCalcFunc function, to use more than one column or do any operation + * needed with the #DbModel fields, in the latter case, custom data can also be + * passed. + **/ + +G_DEFINE_TYPE (DbCalc, db_calc, GVN_TYPE_PARAM); + +/** + * db_calc_new: + * @model: the #DbModel in which the operations will apply + * @type: the #DbCalcOperationType + * @col: the number of a column (starting with 0) + * + * Creates a new #DbCalc using @col to select the only column to be processed + * in each row of @model. + * + * Return value: a new #DbCalc + **/ +DbCalc * db_calc_new (DbModel * model, DbCalcOperationType type, gint col) +{ + g_return_val_if_fail (DB_IS_MODEL (model), NULL); + g_return_val_if_fail (0 <= col || col < db_model_get_ncols (model), NULL); + + return g_object_new + (DB_TYPE_CALC, "model", model, "type", type, "col", col, NULL); +} + +/** + * db_calc_new_with_func: + * @model: the #DbModel in which the operations will apply + * @type: the #DbCalcOperationType + * @func: (scope call): a #DbCalcFunc + * @data: additional data that will be passed to @func + * + * Creates a new #DbCalc using @func as the operation that will apply to each + * row of @model. + * + * Return value: a new #DbCalc + **/ +DbCalc * db_calc_new_with_func (DbModel * model, DbCalcOperationType type, DbCalcFunc func, gpointer data) +{ + g_return_val_if_fail (DB_IS_MODEL (model), NULL); + g_return_val_if_fail (func, NULL); + + return g_object_new + (DB_TYPE_CALC, "model", model, "type", type, "function", func, "data", data, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static gdouble db_calc_value_to_double (const GValue * v) +{ + switch (G_VALUE_TYPE (v)) + { + case G_TYPE_UINT: + return (gdouble) g_value_get_uint (v); + case G_TYPE_UINT64: + return (gdouble) g_value_get_uint64 (v); + case G_TYPE_ULONG: + return (gdouble) g_value_get_ulong (v); + case G_TYPE_INT: + return (gdouble) g_value_get_int (v); + case G_TYPE_INT64: + return (gdouble) g_value_get_int64 (v); + case G_TYPE_LONG: + return (gdouble) g_value_get_long (v); + case G_TYPE_FLOAT: + return (gdouble) g_value_get_float (v); + case G_TYPE_DOUBLE: + return g_value_get_double (v); + case G_TYPE_STRING: + return g_strtod (g_value_get_string (v), NULL); + default: + return 0; + } +} + +static void db_calc_internal_func + (DbCalc * obj, DbIter * iter, GValue * out, gpointer data) +{ + const GValue * value = db_model_get_value (obj->model, iter, obj->col, NULL); + g_value_copy (value, g_value_init (out, G_VALUE_TYPE (value))); +} + +static void db_calc_before_unset (DbModel * model, DbIter * iter, DbCalc * obj) +{ + GValue value = {0}; + + obj->func (obj, iter, &value, obj->data); + + if (!gvn_value_is_null (&value)) + { + gdouble current, old; + + current = db_calc_value_to_double (gvn_param_get_value (GVN_PARAM (obj))); + old = db_calc_value_to_double (&value); + + if (obj->type == DB_CALC_AVG) + { + if (obj->count != 1) + current = (current * (gdouble) obj->count - old) / (gdouble) (obj->count - 1); + else + current = 0; + } + else if (obj->type == DB_CALC_SUM) + current -= old; + + obj->count--; + g_value_unset (&value); + g_value_set_double (g_value_init (&value, G_TYPE_DOUBLE), current); + GVN_PARAM_GET_CLASS (obj)->put_value (GVN_PARAM (obj), &value); + } + + g_value_unset (&value); +} + +static void db_calc_before_delete (DbModel * model, gint path, DbCalc * obj) +{ + DbIter iter; + db_model_get_iter (model, &iter, path); + db_calc_before_unset (model, &iter, obj); +} + +static void db_calc_after_set (DbModel * model, DbIter * iter, DbCalc * obj) +{ + GValue value = {0}; + + obj->func (obj, iter, &value, obj->data); + + if (!gvn_value_is_null (&value)) + { + gdouble current, new; + + current = db_calc_value_to_double (gvn_param_get_value (GVN_PARAM (obj))); + new = db_calc_value_to_double (&value); + + if (obj->type == DB_CALC_AVG) + current = (current * (gdouble) obj->count + new) / (gdouble) (obj->count + 1); + else if (obj->type == DB_CALC_SUM) + current += new; + + obj->count++; + g_value_unset (&value); + g_value_set_double (g_value_init (&value, G_TYPE_DOUBLE), current); + GVN_PARAM_GET_CLASS (obj)->put_value (GVN_PARAM (obj), &value); + } + + g_value_unset (&value); +} + +static void db_calc_refresh (DbModel * model, DbModelStatus status, DbCalc * obj) +{ + DbIter iter; + gdouble current = 0; + GValue val = {0}; + + if (status != DB_MODEL_STATUS_READY) return; + if (!db_model_get_iter_first (model, &iter)) return; + + obj->count = 0; + + do { + obj->func (obj, &iter, &val, obj->data); + + if (!gvn_value_is_null (&val)) + { + current += db_calc_value_to_double (&val); + obj->count++; + } + + g_value_unset (&val); + } + while (db_model_iter_next (model, &iter)); + + if (obj->type == DB_CALC_AVG) + current = (obj->count > 0) ? current / (gdouble) obj->count : 0; + + g_value_set_double (g_value_init (&val, G_TYPE_DOUBLE), current); + GVN_PARAM_GET_CLASS (obj)->put_value (GVN_PARAM (obj), &val); + g_value_unset (&val); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +enum +{ + PROP_MODEL = 1 + ,PROP_TYPE + ,PROP_FUNC + ,PROP_DATA + ,PROP_COL +}; + +static void db_calc_set_property (DbCalc * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_MODEL: + obj->model = g_value_dup_object (value); + g_object_connect (obj->model + ,"signal::line-updated", db_calc_before_unset, obj + ,"signal-after::line-updated", db_calc_after_set, obj + ,"signal::line-deleted", db_calc_before_delete, obj + ,"signal::status-changed", db_calc_refresh, obj + , NULL + ); + break; + case PROP_TYPE: + obj->type = g_value_get_int (value); + break; + case PROP_FUNC: + if (g_value_get_pointer (value)) + obj->func = g_value_get_pointer (value); + break; + case PROP_DATA: + obj->data = g_value_get_pointer (value); + break; + case PROP_COL: + obj->col = g_value_get_int (value); + + if (!obj->func) + obj->func = db_calc_internal_func; + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void db_calc_get_property (DbCalc * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_MODEL: + g_value_set_object (value, obj->model); + break; + case PROP_TYPE: + g_value_set_int (value, obj->type); + break; + case PROP_FUNC: + g_value_set_pointer (value, obj->func); + break; + case PROP_DATA: + g_value_set_pointer (value, obj->data); + break; + case PROP_COL: + g_value_set_int (value, obj->col); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void db_calc_init (DbCalc * obj) +{ + GvnParamSpec * spec; + + obj->model = NULL; + obj->type = 0; + obj->func = NULL; + obj->data = NULL; + obj->col = 0; + obj->count = 0; + + spec = gvn_param_spec_new_with_attrs (G_TYPE_DOUBLE, FALSE, TRUE, NULL); + GVN_PARAM_GET_CLASS (obj)->set_spec (GVN_PARAM (obj), spec); +} + +static void db_calc_finalize (DbCalc * obj) +{ + if (obj->model) + { + g_object_disconnect (obj->model + ,"any_signal::line-updated", db_calc_before_unset, obj + ,"any_signal::line-updated", db_calc_after_set, obj + ,"any_signal::line-deleted", db_calc_before_delete, obj + ,"any_signal::status-changed", db_calc_refresh, obj + ,NULL + ); + g_clear_object (&obj->model); + } +} + +static void db_calc_class_init (DbCalcClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) db_calc_finalize; + klass->set_property = (GObjectSetPropertyFunc) db_calc_set_property; + klass->get_property = (GObjectGetPropertyFunc) db_calc_get_property; + + g_object_class_install_property (klass, PROP_MODEL + ,g_param_spec_object ("model" + ,_("Model") + ,_("The model where the operations will be applied") + ,DB_TYPE_MODEL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE) + ); + + g_object_class_install_property (klass, PROP_TYPE + ,g_param_spec_int ("type" + ,_("Operation type") + ,_("The type of the operation applied over the function") + ,0 + ,1 + ,0 + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE) + ); + + g_object_class_install_property (klass, PROP_FUNC + ,g_param_spec_pointer ("function" + ,_("Function") + ,_("The function to execute") + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE) + ); + + g_object_class_install_property (klass, PROP_DATA + ,g_param_spec_pointer ("data" + ,_("Data") + ,_("The user provided data for the function") + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE) + ); + + g_object_class_install_property (klass, PROP_COL + ,g_param_spec_int ("col" + ,_("Column") + ,_("A column to apply the operations over it") + ,0 + ,G_MAXINT32 + ,0 + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE) + ); +} diff --git a/db/db-calc.h b/db/db-calc.h new file mode 100644 index 0000000..60a503b --- /dev/null +++ b/db/db-calc.h @@ -0,0 +1,93 @@ +/* + Copyright (C) 2012 - Juan Ferrer Toribio + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. +*/ + +#ifndef DB_CALC_H +#define DB_CALC_H + +#include +#include +#include "db-model.h" + +#define DB_CALC_LOG_DOMAIN (g_quark_from_string ("DbCalc")) + +#define DB_TYPE_CALC (db_calc_get_type ()) +#define DB_CALC(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, DB_TYPE_CALC, DbCalc)) +#define DB_IS_CALC(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, DB_TYPE_CALC)) +#define DB_CALC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((obj), DB_TYPE_CALC, DbCalcClass)) +#define DB_IS_CALC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_CLASS)) +#define DB_CALC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_CALC, DbCalcClass)) + +typedef struct _DbCalc DbCalc; +typedef struct _DbCalcClass DbCalcClass; + +/** + * DbCalcFunc: + * @obj: a #DbCalc + * @iter: a #DbIter pointing to a row of the model + * @out: the return position for the resulting value + * @user_data: (closure): the data passed to the function + * + * The prototype of the functions used by the #DbCalc to perform operations + * on more than one column or to perform additional operations to a column, + * previously to add them to the calculations performed by the @DbCalc itself. + **/ +typedef void (*DbCalcFunc) (DbCalc * obj, DbIter * iter, GValue * out, gpointer user_data); + +/** + * DbCalcOperationType: + * @DB_CALC_SUM: sumatory of the values + * @DB_CALC_AVG: average of the values + * + * Defines which type of operation will be performed over the column values. + **/ +typedef enum +{ + DB_CALC_SUM, + DB_CALC_AVG +} +DbCalcOperationType; + +struct _DbCalc +{ + GvnParam parent; + + DbModel * model; + DbCalcOperationType type; + DbCalcFunc func; + gpointer data; + guint col; + guint count; +}; + +struct _DbCalcClass +{ + /* */ + GvnParamClass klass; +}; + +GType db_calc_get_type (); +DbCalc * db_calc_new (DbModel * model + ,DbCalcOperationType type + ,gint col); +DbCalc * db_calc_new_with_func (DbModel * model + ,DbCalcOperationType type + ,DbCalcFunc func + ,gpointer data); + +#endif diff --git a/db/db-conn.c b/db/db-conn.c new file mode 100644 index 0000000..cd9126e --- /dev/null +++ b/db/db-conn.c @@ -0,0 +1,1069 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-conn.h" +#include +#include + +#define IS_OPEN(obj) (obj->status & DB_CONN_OPEN) +#define IS_CLOSING(obj) (obj->status & DB_CONN_CLOSING) +#define IS_OPENING(obj) (obj->status & DB_CONN_OPENING) +#define IS_TRANSACTION(obj) (obj->status & DB_CONN_TRANSACTION) +#define IS_LOST(obj) (obj->status & DB_CONN_LOST) + +typedef struct +{ + DbConn * obj; + gpointer data; +} +IdleData; + +enum { + STATUS_CHANGED + ,ERROR + ,LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +/** + * SECTION: db-conn + * @Short_description: connection managing and wrapping class. + * @Title: DbConn + * @See_also: #DbPg + * + * This class manages a connection to any kind of database (as long as it has + * a plugin to acces to it). It uses the plugin set on the configuration + * parameters (not yet implemented) to connect, query and disconnect. + * + * To do this use the methods db_conn_open(), db_conn_query() and + * db_conn_close() respectively. + * + * It is also possible to parse a SQL query and get an #SqlStmt object using the + * db_conn_parse() method. + * + * This class is thread safe. + **/ +G_DEFINE_TYPE (DbConn, db_conn, G_TYPE_OBJECT); + +/** + * db_conn_new: + * + * Creates a new connection. + * + * Return value: the #DbConn + **/ +DbConn * db_conn_new () +{ + return g_object_new (DB_TYPE_CONN, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +/* + * Frees the #IdleData struct. + */ +static void idle_data_free (IdleData * idle_data) +{ + g_object_unref (idle_data->obj); + g_free (idle_data); +} + +/* + * Notifies that an error happened. + */ +static gboolean db_conn_idle_error (IdleData * idle_data) +{ + GError * error = idle_data->data; + g_signal_emit (idle_data->obj, signals[ERROR], 0, error); + g_error_free (error); + return FALSE; +} + +/* + * Sets and error. + */ +static void db_conn_set_error (DbConn * obj, const GError * error) +{ + if (error) + { + IdleData * idle_data = g_new (IdleData, 1); + idle_data->obj = g_object_ref (obj); + idle_data->data = g_error_copy (error); + + g_idle_add_full (G_PRIORITY_DEFAULT, + (GSourceFunc) db_conn_idle_error, idle_data, (GDestroyNotify) idle_data_free); + } +} + +/* + * Notifies that connection status has changed. + */ +static gboolean db_conn_idle_status (IdleData * idle_data) +{ + DbConnStatus status = GPOINTER_TO_INT (idle_data->data); + g_signal_emit (idle_data->obj, signals[STATUS_CHANGED], 0, status); + return FALSE; +} + +/* + * Sets the status of the connection. + */ +static void db_conn_set_status (DbConn * obj, DbConnStatus status) +{ + IdleData * idle_data; + + if (obj->status != status) + { + idle_data = g_new (IdleData, 1); + idle_data->obj = g_object_ref (obj); + idle_data->data = GINT_TO_POINTER (status); + + g_idle_add_full (G_PRIORITY_DEFAULT, + (GSourceFunc) db_conn_idle_status, idle_data, (GDestroyNotify) idle_data_free); + + obj->status = status; + } +} + +/* + * Cancels all outstanding requests. + */ +static void db_conn_cancel_requests (DbConn * obj) +{ + DbRequest * request; + + while ((request = g_queue_pop_head (obj->requests))) + { + db_request_cancel (request); + db_request_complete (request); + } + + db_plugin_kill_query (obj->plugin); +} + +/* + * Executes asynchronous requests. + */ +static void db_conn_thread (DbConn * obj) +{ + gboolean exit = FALSE; + gboolean exec_success; + DbRequest * request; + + g_mutex_lock (obj->mutex); + + while (!exit) + { + if (IS_OPEN (obj) && !IS_TRANSACTION (obj)) + { + db_conn_set_status (obj, obj->status | DB_CONN_LOADING); + + while (TRUE) + { + if (!(request = g_queue_pop_head (obj->requests))) + break; + + g_mutex_unlock (obj->mutex); + exec_success = db_request_exec (request, NULL); + g_mutex_lock (obj->mutex); + + if (!exec_success && IS_LOST (obj)) + { + g_queue_push_head (obj->requests, request); + } + else + { + db_request_complete (request); + g_object_unref (request); + } + + if (IS_LOST (obj)) + break; + } + + db_conn_set_status (obj, obj->status & ~DB_CONN_LOADING); + } + + if (!IS_CLOSING (obj)) + g_cond_wait (obj->thread_cond, obj->mutex); + else + exit = TRUE; + } + + g_mutex_unlock (obj->mutex); +} + +/* + * Adds a request to the queue. + */ +static void db_conn_add_request (DbConn * obj, DbRequest * request) +{ + g_mutex_lock (obj->mutex); + + if ((IS_OPEN (obj) || IS_LOST (obj)) && !IS_CLOSING (obj)) + { + g_queue_push_tail (obj->requests, g_object_ref (request)); + + if (!IS_TRANSACTION (obj)) + g_cond_signal (obj->thread_cond); + } + else + { + db_request_cancel (request); + db_request_complete (request); + } + + g_mutex_unlock (obj->mutex); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * db_conn_load_plugin: + * @obj: a #DbConn + * @plugin: the plugin name + * @err: return address of a #GError or #NULL + * + * Tryes to load a plugin. + * + * Return vale: %TRUE if plugin was loaded successfully, %FALSE ortherwise + **/ +gboolean db_conn_load_plugin (DbConn * obj, const gchar * plugin, GError ** err) +{ + gchar * aux; + gchar * path; + gchar * plugin_name; + GModule * module; + DbPluginGetTypeFunc symbol; + gboolean ret = FALSE; + + g_return_val_if_fail (DB_IS_CONN (obj), FALSE); + g_return_val_if_fail (plugin, FALSE); + + g_mutex_lock (obj->mutex); + + if (!obj->plugin) + { + aux = g_strdup_printf (_PLUGIN_DIR, plugin); + plugin_name = g_strdup_printf ("db%s", plugin); + path = g_module_build_path (aux, plugin_name); + g_free (aux); + + if ((module = g_module_open (path, 0))) + g_module_make_resident (module); + + aux = g_strdup_printf ("db_%s_get_type", plugin); + + if (module && g_module_symbol (module, aux, (gpointer) &symbol)) + { + obj->plugin_name = g_strdup (plugin); + obj->plugin = g_object_new (symbol (), NULL); + ret = TRUE; + } + else if (err) + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_PLUGIN + ,_("Can't load DbPlugin '%s': %s") + ,plugin + ,g_module_error () + ); + else + g_warning (_("Can't load DbPlugin '%s': %s") + ,plugin + ,g_module_error () + ); + + g_free (plugin_name); + g_free (path); + g_free (aux); + } + else if (err) + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_PLUGIN + ,_("Plugin can't be loaded") + ); + + g_mutex_unlock (obj->mutex); + + return ret; +} + +/** + * db_conn_set_query_path: + * @obj: a #DbConn + * @path: the path + * + * Sets the query search path. + **/ +void db_conn_set_query_path (DbConn * obj, const gchar * path) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_mutex_lock (obj->settings_mutex); + + g_free (obj->query_path); + g_strfreev (obj->query_dirs); + obj->query_path = g_strdup (path); + obj->query_dirs = g_strsplit (obj->query_path, G_SEARCHPATH_SEPARATOR_S, 0); + + g_mutex_unlock (obj->settings_mutex); +} + +/** + * db_conn_get_query_path: + * @obj: a #DbConn + * + * Sets the query search path. + * + * Return value: the path, should be freed with g_free() + **/ +gchar * db_conn_get_query_path (DbConn * obj) +{ + gchar * path; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + g_mutex_lock (obj->settings_mutex); + path = g_strdup (obj->query_path); + g_mutex_unlock (obj->settings_mutex); + + return path; +} + +/** + * db_conn_open: + * @obj: a #DbConn + * @host: Host name + * @schema: Database name + * @user: User name + * @pass: Password + * + * Opens a new connection to the database and creates the thread that handles + * all requests made to it asincronously. + * + * Return value: %TRUE if the connection was successfully open, %FALSE otherwise + **/ +gboolean db_conn_open (DbConn * obj, const gchar * host, const gchar * schema, + const gchar * user, const gchar * pass, GError ** err) +{ + gboolean opened = FALSE; + + g_return_val_if_fail (DB_IS_CONN (obj), FALSE); + + g_mutex_lock (obj->mutex); + + if (!IS_OPEN (obj) && !IS_OPENING (obj) && !IS_CLOSING (obj)) + { + DbConnStatus last_status = obj->status; + + db_conn_set_status (obj, obj->status | DB_CONN_OPENING | DB_CONN_LOADING); + + g_mutex_unlock (obj->mutex); + opened = db_plugin_open (obj->plugin + ,host + ,schema + ,user + ,pass + ,err + ); + g_mutex_lock (obj->mutex); + + if (opened) + { + g_mutex_lock (obj->settings_mutex); + g_free (obj->host); + g_free (obj->schema); + g_free (obj->user); + g_free (obj->pass); + obj->host = g_strdup (host); + obj->schema = g_strdup (schema); + obj->user = g_strdup (user); + obj->pass = g_strdup (pass); + g_mutex_unlock (obj->settings_mutex); + + db_conn_set_status (obj, DB_CONN_OPEN); + + if (last_status & DB_CONN_LOST) + g_cond_signal (obj->thread_cond); + else + obj->thread = g_thread_new ("DbConn", + (GThreadFunc) db_conn_thread, obj); + } + else + db_conn_set_status (obj, last_status); + } + else + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_OPENING + ,_("Can't open a new connection, it's already open.") + ); + + g_mutex_unlock (obj->mutex); + return opened; +} + +void db_conn_set_ssl (DbConn * obj, const gchar * ca) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + db_plugin_set_ssl (obj->plugin, ca); +} + +/** + * db_conn_reconnect: + * @obj: a #DbConn + * + * Tries to reconnect to database. + **/ +gboolean db_conn_reconnect (DbConn * obj, GError ** err) +{ + gboolean opened; + gchar * host; + gchar * schema; + gchar * user; + gchar * pass; + + g_return_val_if_fail (DB_IS_CONN (obj), FALSE); + + g_mutex_lock (obj->settings_mutex); + host = g_strdup (obj->host); + schema = g_strdup (obj->schema); + user = g_strdup (obj->user); + pass = g_strdup (obj->pass); + g_mutex_unlock (obj->settings_mutex); + + opened = db_conn_open (obj, host, schema, user, pass, err); + + g_free (host); + g_free (schema); + g_free (user); + g_free (pass); + + return opened; +} + +/** + * db_conn_close: + * @obj: a #DbConn + * @wait: specifies whether to wait for pending requests before close the connection + * + * Closes the current connection to the database and the associated thread. + **/ +void db_conn_close (DbConn * obj, gboolean wait) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_mutex_lock (obj->mutex); + + if ((IS_OPEN (obj) || (IS_LOST (obj) && !IS_OPENING (obj))) && !IS_CLOSING (obj)) + { + db_conn_set_status (obj, obj->status | DB_CONN_CLOSING | DB_CONN_LOADING); + + if (!wait) + db_conn_cancel_requests (obj); + + g_cond_signal (obj->thread_cond); + g_mutex_unlock (obj->mutex); + + g_thread_join (obj->thread); + obj->thread = NULL; + + db_plugin_close (obj->plugin); + + g_mutex_lock (obj->mutex); + db_conn_set_status (obj, DB_CONN_CLOSED); + + g_mutex_lock (obj->settings_mutex); + g_free (obj->host); + g_free (obj->schema); + g_free (obj->user); + g_free (obj->pass); + obj->host = NULL; + obj->schema = NULL; + obj->user = NULL; + obj->pass = NULL; + g_mutex_unlock (obj->settings_mutex); + } + + g_mutex_unlock (obj->mutex); +} + +/** + * db_conn_exec: + * @obj: a #DbConn + * @sql: the SQL statement + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Sends a query to the database and waits for the resultset. The user is + * responsible for releasing the result with db_result_set_free(). + * + * Return value: (transfer full) (allow-none): the #DbResultSet on success, %NULL on failure + **/ +DbResultSet * db_conn_exec (DbConn * obj, const gchar * sql, GError ** err) +{ + GError * query_error = NULL; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + if (sql && g_strcmp0 (sql, "")) + { + DbResultSet * set; + + // XXX: Only for debug purposes. + g_message ("DbConn: Query sent: %s", sql); + + set = db_plugin_query (obj->plugin, sql, &query_error); + + if (!set && query_error) + { + if (query_error->code == DB_CONN_ERROR_LOST) + { + g_mutex_lock (obj->mutex); + + if (IS_OPEN (obj) && !IS_CLOSING (obj)) + db_conn_set_status (obj, DB_CONN_CLOSED | DB_CONN_LOST); + + g_mutex_unlock (obj->mutex); + } + + db_conn_set_error (obj, query_error); + g_propagate_error (err, query_error); + } + + return set; + } + else + { + query_error = g_error_new ( + DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_QUERY_EMPTY + ,_("The query is empty.") + ); + + db_conn_set_error (obj, query_error); + g_propagate_error (err, query_error); + } + + return NULL; +} + +/** + * db_conn_query: + * @obj: the #DbConn + * @sql: the SQL statement + * + * Sends a query to the database and waits for the result. + * + * Return value: (transfer full): the new #DbRequest + **/ +DbRequest * db_conn_query (DbConn * obj, const gchar * sql) +{ + DbRequest * request; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + request = db_request_new (obj, sql); + db_request_exec (request, NULL); + return request; +} + +/** + * db_conn_query_with_stmt: + * @obj: the #DbConn + * @stmt: the #SqlStmt + * + * Sends a stmt to the database and waits for the result. + * + * Return value: (transfer full): the new #DbRequest + **/ +DbRequest * db_conn_query_with_stmt (DbConn * obj, SqlStmt * stmt) +{ + DbRequest * request; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + request = db_request_new_with_stmt (obj, stmt); + db_request_exec (request, NULL); + return request; +} + +/** + * db_conn_query_async: + * @obj: the #DbConn + * @sql: the SQL statement + * @callback: + * @user_data: + * @notify: + * + * Return value: (transfer full): the new #DbRequest + **/ +DbRequest * db_conn_query_async (DbConn * obj, const gchar * sql, + DbRequestDoneCallback callback, gpointer user_data, GDestroyNotify notify) +{ + DbRequest * request; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + request = db_request_new (obj, sql); + db_request_set_callback (request, callback, user_data, notify); + db_conn_add_request (obj, request); + return request; +} + +/** + * db_conn_query_with_stmt_async: + * @obj: the #DbConn + * @stmt: the #SqlStmt + * @callback: + * @user_data: + * @notify: + * + * Return value: (transfer full): the new #DbRequest + **/ +DbRequest * db_conn_query_with_stmt_async (DbConn * obj, SqlStmt * stmt, + DbRequestDoneCallback callback, gpointer user_data, GDestroyNotify notify) +{ + DbRequest * request; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + request = db_request_new_with_stmt (obj, stmt); + db_request_set_callback (request, callback, user_data, notify); + db_conn_add_request (obj, request); + return request; +} + +/** + * db_conn_query_value: + * @obj: a #DbConn + * @sql: a SQL statement + * @value: (out): return location for a #GValue + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Sends a sql to the database and takes the first row value of the result. + * + * Return value: %TRUE on success, %FALSE on failure + **/ +gboolean db_conn_query_value (DbConn * obj, const gchar * sql, GValue * value, GError ** err) +{ + gboolean success; + DbRequest * request; + + g_return_val_if_fail (DB_IS_CONN (obj), FALSE); + + request = db_conn_query (obj, sql); + success = db_request_fetch_value (request, value, err); + g_object_unref (request); + + return success; +} + +/** + * db_conn_kill_query: + * @obj: a #DbConn + * + * Attempts to kill the query that is running, if any. + **/ +void db_conn_kill_query (DbConn * obj) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + db_plugin_kill_query (obj->plugin); +} + +/** + * db_conn_retry: + * @obj: a #DbConn + * + * Tries rerunning pending requests. + **/ +void db_conn_retry (DbConn * obj) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_cond_signal (obj->thread_cond); +} + +/** + * db_conn_parse: + * @obj: a #DbConn + * @sql: an SQL string + * + * Return value: (transfer full): a #SqlStmt + **/ +SqlStmt * db_conn_parse (DbConn * obj, gchar * sql) +{ + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + return db_plugin_parse (obj->plugin, sql); +} + +/** + * db_conn_render: + * @obj: a #DbConn. + * @object: the #GObject to render + * @err: (out) (allow-none): the return location for #GError + * + * Renders a #GObject object as a SQL string to send it in a database + * query. It takes the connection to know the codification in wich to escape + * the data. + * + * Return value: (transfer full): the rendered string, or %NULL if error. + **/ +gchar * db_conn_render (DbConn * obj, gpointer object, GError ** err) +{ + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + + return db_plugin_render (obj->plugin, object, err); +} + +/** + * db_conn_start_transaction: + * @obj: a #DbConn + * + * Enter the transaction state, all requests made are retained until you call + * the method db_conn_comit. + **/ +void db_conn_start_transaction (DbConn * obj) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_mutex_lock (obj->mutex); + + if (IS_OPEN (obj) && !IS_CLOSING (obj)) + { + obj->transaction++; + db_conn_set_status (obj, obj->status | DB_CONN_TRANSACTION); + } + + g_mutex_unlock (obj->mutex); +} + +/** + * db_conn_commit: + * @obj: a #DbConn + * + * Commits the current transaction. If transaction arrives to 0, all + * outstanding requests in the queue are sent. + **/ +void db_conn_commit (DbConn * obj) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_mutex_lock (obj->mutex); + + if (IS_TRANSACTION (obj)) + { + obj->transaction--; + + if (!obj->transaction) + { + db_conn_set_status (obj, obj->status & ~DB_CONN_TRANSACTION); + g_mutex_unlock (obj->mutex); + db_conn_retry (obj); + return; + } + } + + g_mutex_unlock (obj->mutex); +} + +/** + * db_conn_rollback: + * @obj: a #DbConn + * + * Deletes all pending requests from the input queue and rollbacks the current + * transactions, if any. + **/ +void db_conn_rollback (DbConn * obj) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_mutex_lock (obj->mutex); + + if (IS_TRANSACTION (obj)) + { + db_conn_cancel_requests (obj); + obj->transaction = 0; + db_conn_set_status (obj, obj->status & ~DB_CONN_TRANSACTION); + } + + g_mutex_unlock (obj->mutex); +} + +/** + * db_conn_get_user: + * @obj: a #DbConn + * + * Gets the connection user. + * + * Return value: the user, should be freed using g_free() + **/ +gchar * db_conn_get_user (DbConn * obj) +{ + gchar * user; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + g_mutex_lock (obj->settings_mutex); + user = g_strdup (obj->user); + g_mutex_unlock (obj->settings_mutex); + + return user; +} + +/** + * db_conn_get_host: + * @obj: a #DbConn + * + * Gets the connection host. + * + * Return value: the host, should be freed using g_free() + **/ +gchar * db_conn_get_host (DbConn * obj) +{ + gchar * host; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + g_mutex_lock (obj->settings_mutex); + host = g_strdup (obj->host); + g_mutex_unlock (obj->settings_mutex); + + return host; +} + +/** + * db_conn_get_schema: + * @obj: a #DbConn + * + * Gets the connection schema. + * + * Return value: the schema, should be freed using g_free() + **/ +gchar * db_conn_get_schema (DbConn * obj) +{ + gchar * schema; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + g_mutex_lock (obj->settings_mutex); + schema = g_strdup (obj->schema); + g_mutex_unlock (obj->settings_mutex); + + return schema; +} + +/** + * db_conn_create_stmt_from_file: + * @obj: a #DbConn + * @query_file: path to a file containing SQL statement + * + * Reads an SQL query from the specified query_file and creates a new + * #SqlString. The file path must be relative to any path in the "query-path" + * property. + * + * Return value: (transfer full): an #SqlString + **/ +SqlString * db_conn_create_stmt_from_file (DbConn * obj, const gchar * query_file) +{ + gint i; + gchar * file; + SqlString * stmt = NULL; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + g_return_val_if_fail (query_file, NULL); + + file = g_strconcat (query_file, ".sql", NULL); + + for (i = 0; obj->query_dirs &&obj->query_dirs[i] && !stmt; i++) + { + gchar * buffer; + gchar * path = g_build_filename (obj->query_dirs[i], file, NULL); + + if (g_file_get_contents (path, &buffer, NULL, NULL)) + { + stmt = sql_string_new (buffer); + g_free (buffer); + } + + g_free (path); + } + + if (!stmt) + g_warning ("DbConn: Can't create statement from file: %s", query_file); + + g_free (file); + return stmt; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_PLUGIN = 1 + ,PROP_QUERY_PATH + ,PROP_HOST + ,PROP_USER + ,PROP_DB +}; + +static void db_conn_set_property (DbConn * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PLUGIN: + db_conn_load_plugin (obj, g_value_get_string (value), NULL); + break; + case PROP_QUERY_PATH: + db_conn_set_query_path (obj, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void db_conn_get_property (DbConn * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PLUGIN: + g_value_set_string (value, obj->plugin_name); + break; + case PROP_QUERY_PATH: + g_value_set_string (value, db_conn_get_query_path (obj)); + break; + case PROP_HOST: + g_value_take_string (value, db_conn_get_host (obj)); + break; + case PROP_USER: + g_value_take_string (value, db_conn_get_user (obj)); + break; + case PROP_DB: + g_value_take_string (value, db_conn_get_schema (obj)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_conn_init (DbConn * obj) +{ + obj->plugin = NULL; + obj->plugin_name = NULL; + obj->query_path = NULL; + obj->query_dirs = NULL; + obj->schema = NULL; + obj->host = NULL; + obj->user = NULL; + obj->pass = NULL; + obj->thread = NULL; + obj->thread_cond = g_new (GCond, 1); + obj->transaction = 0; + obj->status = DB_CONN_CLOSED; + obj->requests = g_queue_new (); + obj->mutex = g_new (GMutex, 1); + obj->settings_mutex = g_new (GMutex, 1); + g_mutex_init (obj->mutex); + g_mutex_init (obj->settings_mutex); + g_cond_init (obj->thread_cond); +} + +static void db_conn_finalize (DbConn * obj) +{ + db_conn_close (obj, FALSE); + + if (obj->plugin) + g_free (obj->plugin_name); + + g_mutex_clear (obj->settings_mutex); + g_mutex_clear (obj->mutex); + g_cond_clear (obj->thread_cond); + g_free (obj->query_path); + g_strfreev (obj->query_dirs); + g_free (obj->settings_mutex); + g_free (obj->mutex); + g_free (obj->thread_cond); + g_free (obj->host); + g_free (obj->user); + g_free (obj->pass); + g_free (obj->schema); + g_queue_free_full (obj->requests, (GDestroyNotify) g_object_unref); + G_OBJECT_CLASS (db_conn_parent_class)->finalize (G_OBJECT (obj)); +} + +static void db_conn_class_init (DbConnClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->set_property = (GObjectSetPropertyFunc) db_conn_set_property; + klass->get_property = (GObjectGetPropertyFunc) db_conn_get_property; + klass->finalize = (GObjectFinalizeFunc) db_conn_finalize; + + signals[STATUS_CHANGED] = g_signal_new ("status-changed", + DB_TYPE_CONN, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT + ); + signals[ERROR] = g_signal_new ("error", + DB_TYPE_CONN, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER + ); + + g_object_class_install_property (klass, PROP_PLUGIN, + g_param_spec_string ("plugin" + ,_("Plugin") + ,_("The name of the plugin") + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_QUERY_PATH, + g_param_spec_string ("query-path" + ,_("Query path") + ,_("The path where query files are located") + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_HOST, + g_param_spec_string ("host" + ,_("Host") + ,_("The host name to connect to") + ,NULL + ,G_PARAM_READABLE + )); + g_object_class_install_property (klass, PROP_USER, + g_param_spec_string ("user" + ,_("User") + ,_("The user name") + ,NULL + ,G_PARAM_READABLE + )); + g_object_class_install_property (klass, PROP_DB, + g_param_spec_string ("db" + ,_("DB name") + ,_("The default schema") + ,NULL + ,G_PARAM_READABLE + )); +} diff --git a/db/db-conn.h b/db/db-conn.h new file mode 100644 index 0000000..bf34d4d --- /dev/null +++ b/db/db-conn.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_CONN_H +#define DB_CONN_H + +#include +#include "db-plugin.h" + +#define DB_CONN_LOG_DOMAIN (g_quark_from_string ("DbConn")) + +#define DB_TYPE_CONN (db_conn_get_type ()) +#define DB_CONN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_CONN, DbConn)) +#define DB_IS_CONN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_CONN)) +#define DB_CONN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_CONN, DbConnClass)) +#define DB_IS_CONN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_CONN)) +#define DB_CONN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_CONN, DbConnClass)) + +//#define PLUGIN_DIR "./plugin/%s/.libs" + +typedef struct _DbConn DbConn; +typedef struct _DbConnClass DbConnClass; + +#include "db-request.h" + +struct _DbConn +{ + GObject parent; + DbPlugin * plugin; + gchar * plugin_name; + gchar * query_path; + gchar ** query_dirs; + gchar * host; + gchar * user; + gchar * pass; + gchar * schema; + gint status; // DbConnStatus + GMutex * mutex; + GMutex * settings_mutex; + GThread * thread; + GCond * thread_cond; + GQueue * requests; + guint transaction; +}; + +struct _DbConnClass +{ + GObjectClass parent; +}; + +typedef enum +{ + DB_CONN_ERROR_LOST = 1 + ,DB_CONN_ERROR_PLUGIN + ,DB_CONN_ERROR_OPENING + ,DB_CONN_ERROR_BAD_LOGIN + ,DB_CONN_ERROR_BAD_RESPONSE + ,DB_CONN_ERROR_QUERY_EMPTY + ,DB_CONN_ERROR_QUERY_NONFATAL + ,DB_CONN_ERROR_QUERY_FATAL + ,DB_CONN_ERROR_QUERY_FAILED + ,DB_CONN_ERROR_UNKNOW +} +DbConnError; + +/** + * DbConnStatus: + * @DB_CONN_CLOSED: The connection is closed + * @DB_CONN_OPENING: The connection is opening + * @DB_CONN_IDLE: The connection is open but it's idle + * @DB_CONN_TRANSACTION: A transatcion has been started + * @DB_CONN_LOADING: The connection is making a query + * @DB_CONN_CLOSE_REQUESTED: Waiting for a query to close the connection + * @DB_CONN_CLOSING: The connection is closing + * @DB_CONN_LOST: The connection is closed because it has been lost + * + * Identifies the status of the connection. + **/ +typedef enum +{ + DB_CONN_CLOSED = 0 + ,DB_CONN_OPEN = 1 << 0 + ,DB_CONN_LOADING = 1 << 1 + ,DB_CONN_LOST = 1 << 2 + ,DB_CONN_TRANSACTION = 1 << 3 + ,DB_CONN_CLOSING = 1 << 4 + ,DB_CONN_OPENING = 1 << 5 +} +DbConnStatus; + +GType db_conn_get_type (); +DbConn * db_conn_new (); +gboolean db_conn_load_plugin (DbConn * obj, const gchar * plugin, GError ** err); +void db_conn_set_query_path (DbConn * obj, const gchar * path); +gchar * db_conn_get_query_path (DbConn * obj); +gboolean db_conn_open (DbConn * obj + ,const gchar * host + ,const gchar * schema + ,const gchar * user + ,const gchar * pass + ,GError ** err); +void db_conn_close (DbConn * obj, gboolean wait); +void db_conn_set_ssl (DbConn * obj, const gchar * ca); +gboolean db_conn_reconnect (DbConn * obj, GError ** err); + +DbResultSet * db_conn_exec (DbConn * obj, const gchar * sql, GError ** err); + +DbRequest * db_conn_query (DbConn * obj, const gchar * sql); +DbRequest * db_conn_query_async (DbConn * obj + ,const gchar * sql + ,DbRequestDoneCallback callback + ,gpointer user_data + ,GDestroyNotify notify); + +DbRequest * db_conn_query_with_stmt (DbConn * obj, SqlStmt * stmt); +DbRequest * db_conn_query_with_stmt_async (DbConn * obj + ,SqlStmt * stmt + ,DbRequestDoneCallback callback + ,gpointer user_data + ,GDestroyNotify notify); + +gboolean db_conn_query_value (DbConn * obj, const gchar * sql, GValue * value, GError ** err); + +void db_conn_retry (DbConn * obj); +void db_conn_kill_query (DbConn * obj); + +SqlStmt * db_conn_parse (DbConn * obj, gchar * sql); +gchar * db_conn_render (DbConn * obj, gpointer object, GError ** err); +guchar * db_conn_escape_binary (DbConn * obj, const guchar * from, gsize from_size, gsize * to_size); + +void db_conn_start_transaction (DbConn * obj); +void db_conn_commit (DbConn * obj); +void db_conn_rollback (DbConn * obj); + +gchar * db_conn_get_user (DbConn * obj); +gchar * db_conn_get_host (DbConn * obj); +gchar * db_conn_get_schema (DbConn * obj); + +SqlString * db_conn_create_stmt_from_file (DbConn * obj, const gchar * query_file); + +#endif \ No newline at end of file diff --git a/db/db-file-loader.c b/db/db-file-loader.c new file mode 100644 index 0000000..e2f62eb --- /dev/null +++ b/db/db-file-loader.c @@ -0,0 +1,756 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-file-loader.h" +#include "gio/gio.h" +#include +#include +#include + +#define CACHE_CLEAN_PERIOD 600 +#define CACHE_DEFAULT_SIZE 100000000 +#define CACHE_CLEAN_MARGIN (CACHE_DEFAULT_SIZE*20)/100 + +/** + * SECTION: db-file-loader + * @Short_description: utility to download/upload resources from an URL + * @Title: DbFileLoader + * + * #DbFileLoader can download and upload files from a remote or local location by + * its URL. Both operations will allways be made asynchronously, to retrieve the + * results you must pass a #DbFileLoaderCallbackFunc. + * + * By default, #DbFileLoader uses a local cache on disk to store the downloaded + * files. To avoid the use of this feature, create the #DbFileLoader using + * db_file_loader_new_simple(). The default cache directory is 'hedera', under + * g_get_user_cache_dir(). To use another directory as cache or set its size, + * use g_object_set(). + */ + +struct _DbFileLoaderPrivate +{ + gchar * dir; + gchar * cache; + goffset size; + gchar * host; + gchar * path; + GInetSocketAddress * addr; + GThreadPool * pool; + GMutex * mutex; + GMutex * cache_mutex; + GHashTable * downloading; + guint src; +}; + +G_DEFINE_TYPE (DbFileLoader, db_file_loader, G_TYPE_OBJECT) + +typedef struct +{ + DbFileLoader * obj; + gchar * name; + GBytes * data; + GError * error; + DbFileLoaderCallbackFunc func; + gpointer user_data; + GCancellable * cancel; +} +File; + +typedef struct +{ + guint64 atime; + goffset size; + gchar * path; +} +CacheFile; + +static const gchar * WDAY[] = {"Err", + "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; + +static const gchar * MONTH[] = {"Err", + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +static const gchar * format_request = { + "GET /%s/%s HTTP/1.1\r\n" // Path to the file + "Host: %s\r\n" // Hostname + "%s" // If-modified-since: date\r\n + "\r\n" +}; + +/** + * db_file_loader_new: + * @host: the host where the files are stored + * @path: the base directory to find the loaded resources + * + * Creates a new #DbFileLoader pointing to @path in @host. The cache is set + * to default. + * + * Return value: a #DbFileLoader + **/ +DbFileLoader * db_file_loader_new (const gchar * host, const gchar * path) +{ + g_return_val_if_fail (host, NULL); + g_return_val_if_fail (path, NULL); + + return g_object_new + (DB_TYPE_FILE_LOADER, "host", host, "path", path, "cache", NULL, NULL); +} + +/** + * db_file_loader_new_simple: + * @host: the host where the files are stored + * @path: the base directory to find the loaded resources + * + * Creates a new #DbFileLoader pointing to @path in @host. To use a cache, + * call db_file_loader_new(). + * + * Return value: a #DbFileLoader + **/ +DbFileLoader * db_file_loader_new_simple (const gchar * host, const gchar * path) +{ + DbFileLoader * fl; + + g_return_val_if_fail (host, NULL); + g_return_val_if_fail (path, NULL); + + fl = g_object_new (DB_TYPE_FILE_LOADER, "host", host, "path", path, NULL); + + if (fl->priv->cache) + { + g_free (fl->priv->cache); + fl->priv->cache = NULL; + } + + return fl; +} + +/* + * db_file_loader_new_full: + * @host: the host where the files are stored + * @path: the base directory to find the loaded resources + * @cache: (allow-none): the base directory for the local cache or %NULL + * @size: maximal size for the cache + * + * Creates a new #DbFileLoader pointing to @path in @host. Note that the user + * needs permission to read and write in @cache. If @cache is %NULL, it will be + * set to default, if @max_cache is 0, the cache has no maximal size. + * + * Return value: a #DbFileLoader or %NULL if @cache is an invalid directory + **/ +DbFileLoader * db_file_loader_new_full + (const gchar * host, const gchar * path, const gchar * cache, goffset size) +{ + g_return_val_if_fail (host, NULL); + g_return_val_if_fail (path, NULL); + +// Comprobar que cache existe y se puede escribir, comprobar tamaño disponible + return g_object_new (DB_TYPE_FILE_LOADER, + "host", host, "path", path, + "cache", cache, "size", size, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +static File * file_new (DbFileLoader * obj, const gchar * path, const GBytes * data, + DbFileLoaderCallbackFunc func, gpointer user_data, gboolean async) +{ + File * file = g_new (File, 1); + + file->obj = g_object_ref (obj); + file->name = g_strdup (path); + file->data = data ? g_boxed_copy (G_TYPE_BYTES, data) : NULL; + file->error = NULL; + file->func = func; + file-> user_data = user_data; + file->cancel = async ? g_cancellable_new () : NULL; + + return file; +} + +static void file_free (File * file) +{ + if (file) + { + g_free (file->name); + + if (file->error) + g_error_free (file->error); + + if (file->data) + g_bytes_unref (file->data); + + g_object_unref (file->obj); + g_free (file); + } +} + +/* + * Connection management and usage + */ +static GFile * db_file_loader_get_cache_file (DbFileLoader * obj, const gchar * subpath) +{ + gchar * path = g_strconcat (obj->priv->cache, "/", subpath, NULL); + GFile * file = g_file_new_for_path (path); + g_free (path); + + return file; +} + +static gboolean db_file_loader_callback (File * file) +{ + if (file->cancel && !g_cancellable_is_cancelled (file->cancel)) + file->func (file->obj, file->data, file->error, file->user_data); + + g_hash_table_remove (file->obj->priv->downloading, file); + + return FALSE; +} + +static gboolean db_file_loader_resolve_host (DbFileLoader * obj, GCancellable * cancel, GError ** error) +{ + gboolean ret = FALSE; + GResolver * r = g_resolver_get_default (); + GList * ads = g_resolver_lookup_by_name (r, obj->priv->host, cancel, error); + + if (ads) + { + GInetAddress * a = g_list_nth_data (ads, 0);// TODO? Use the entire list +// TODO Add the option to set the connection port!! + obj->priv->addr = G_INET_SOCKET_ADDRESS (g_inet_socket_address_new (a, 80)); + g_resolver_free_addresses (ads); + ret = TRUE; + } + + g_object_unref (r); + return ret; +} + +static GIOStream * db_file_loader_connect (DbFileLoader * obj, GCancellable * cancel, GError ** error) +{ + GSocketClient * client = g_socket_client_new (); + GIOStream * connection = G_IO_STREAM (g_socket_client_connect + (client, G_SOCKET_CONNECTABLE (obj->priv->addr), cancel, error)); + + g_object_unref (client); + return connection; +} + +static gchar * db_file_loader_create_request (DbFileLoader * obj, const gchar * subpath) +{ + gchar * ifmod; + gchar * request; + gchar * date = NULL; + + if (obj->priv->cache) + { + GFileInfo * info; + GFile * file = db_file_loader_get_cache_file (obj, subpath); + + info = g_file_query_info + (file + ,G_FILE_ATTRIBUTE_TIME_CHANGED + ,G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS + ,NULL, NULL); + + g_object_unref (file); + + if (info) + { + guint64 date_attr = g_file_info_get_attribute_uint64 + (info, G_FILE_ATTRIBUTE_TIME_CHANGED); + GDateTime * dt = g_date_time_new_from_unix_utc (date_attr); + gchar * f_year_time = g_date_time_format (dt, "%Y %T GMT"); + gchar * f_month_day = g_date_time_format (dt, "%d"); + date = g_strdup_printf ("%s, %s %s %s" + ,WDAY[g_date_time_get_day_of_week (dt)] + ,f_month_day + ,MONTH[g_date_time_get_month (dt)] + ,f_year_time + ); + + g_free (f_month_day); + g_free (f_year_time); + g_date_time_unref (dt); + g_object_unref (info); + } + } + + ifmod = date ? g_strconcat ("If-modified-since: ", date, "\r\n", NULL) : ""; + request = g_strdup_printf (format_request, + obj->priv->path, subpath, obj->priv->host, ifmod); + + if (date) + { + g_free (date); + g_free (ifmod); + } + + return request; +} + +static gboolean db_file_loader_store (DbFileLoader * obj + ,const gchar * subpath + ,const gchar * data + ,gsize len + ,GError ** error) +{ + gsize dir_len; + gboolean ret = FALSE; + gchar * path = g_strconcat (obj->priv->cache, "/",subpath, NULL); + GFile * file = g_file_new_for_path (path); + gchar * name = g_file_get_basename (file); + + dir_len = strlen (path) - strlen (name); + gchar dir[dir_len]; + g_strlcpy (dir, path, dir_len); + g_free (name); + + g_mutex_lock (obj->priv->cache_mutex); + + if (g_mkdir_with_parents (dir, 00700) >= 0) + { + if (!g_file_query_exists (file, NULL) + && g_file_set_contents (path, data, len, error)) + ret = TRUE; + } + else + g_set_error (error, DB_FILE_LOADER_LOG_DOMAIN, + g_file_error_from_errno (errno), _("%s not cached"), subpath); + + g_mutex_unlock (obj->priv->cache_mutex); + + g_free (path); + g_object_unref (file); + + return ret; +} + +static gchar * db_file_loader_load_from_cache (DbFileLoader * obj + ,const gchar * subpath + ,gsize * len + ,GError ** error) +{ + gchar * data; + GFile * file = db_file_loader_get_cache_file (obj, subpath); + + g_file_load_contents (file, NULL, &data, len, NULL, error); + g_object_unref (file); + + return data; +} + +static void db_file_loader_job_download (File * file, DbFileLoader * obj) +{ + gsize len; + gchar ** split; + gchar * status_line = NULL; + gchar * request = NULL; + gchar * data = NULL; + GError * error = NULL; + GCancellable * cancel = file->cancel; + GIOStream * connection = NULL; + GOutputStream * send_stream; + GDataInputStream * receive_stream = NULL; + + g_mutex_lock (obj->priv->mutex); + + if (!obj->priv->addr && !db_file_loader_resolve_host (obj, cancel, &error)) + { + g_mutex_unlock (obj->priv->mutex); + goto final; + } + + g_mutex_unlock (obj->priv->mutex); + + if (!(connection = db_file_loader_connect (obj, cancel, &error))) + goto final; + + request = db_file_loader_create_request (obj, file->name); + send_stream = g_io_stream_get_output_stream (connection); + + if (0 > g_output_stream_write (send_stream, request, strlen (request), cancel, &error) + || !g_output_stream_close (send_stream, cancel, &error)) + goto final; + + receive_stream = g_data_input_stream_new (g_io_stream_get_input_stream (connection)); + g_data_input_stream_set_newline_type (receive_stream, G_DATA_STREAM_NEWLINE_TYPE_CR_LF); + status_line = g_data_input_stream_read_line (receive_stream, &len, cancel, &error); + + if (!status_line) + goto final; + + split = g_strsplit (status_line, " ", -1); + guint status = g_strv_length (split) >= 2 ? atoi (split[1]) : 0; + g_strfreev (split); + + switch (status) + { + gchar * line; + + case 200: + { + gboolean content_known; + guint i, nbytes = 0; + line = NULL; + + do + {// Read header fields + g_free (line); + line = g_data_input_stream_read_line + (receive_stream, &len, cancel, &error); + + if (g_str_has_prefix (line, "Content-Length: ")) + { + nbytes = atoi (line + 16); + content_known = TRUE; + } + } + while (line && len && !error); + + g_free (line); + + if (!error) + { + if (!content_known) + g_set_error (&error, DB_FILE_LOADER_LOG_DOMAIN, status, + _("Unknown content length of file %s"), file->name); + else + { + len = nbytes; + data = g_new (gchar, len); + + for (i = 0; !error && i < len; i++) + data[i] = g_data_input_stream_read_byte + (receive_stream, cancel, &error); + + if (!error && data) + db_file_loader_store (obj, file->name, data, len, &error); + } + } + + break; + } + case 304: + { + data = db_file_loader_load_from_cache (obj, file->name, &len, &error); + break; + } + default: + if (status_line && !error) + g_set_error (&error, DB_FILE_LOADER_LOG_DOMAIN, status, + "%s: '%s'", status_line, file->name); + } + + file->data = g_bytes_new_take (data, len); + +final: + g_free (request); + g_free (status_line); + + if (receive_stream) + { + g_input_stream_close (G_INPUT_STREAM (receive_stream), NULL, + error ? NULL : &error); + g_object_unref (receive_stream); + } + + if (connection) + { + g_io_stream_close (connection, NULL, error ? NULL : &error); + g_object_unref (connection); + } + + file->error = error; + + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + ,(GSourceFunc) db_file_loader_callback, file + ,(GDestroyNotify) file_free + ); +} + +/** + * db_file_loader_download: + * @obj: a #DbFileLoader + * @path: the path to the file from @obj's path + * @func: (scope async): the #DbFileLoaderCallbackFunc to call after downloading + * or in case of error + * @user_data: (closure): data to pass to @func + * + * Downloads a file from @file, which is a relative path to the file from the + * base URL of the #DbFileLoader. The result will be availble in @func. + **/ +void db_file_loader_download (DbFileLoader * obj + ,const gchar * path + ,DbFileLoaderCallbackFunc func + ,gpointer user_data) +{ + File * file; + + g_return_if_fail (DB_IS_FILE_LOADER (obj)); + g_return_if_fail (path); + + if (!obj->priv->pool) + obj->priv->pool = g_thread_pool_new ((GFunc) db_file_loader_job_download, + obj, -1, FALSE, NULL); + + file = file_new (obj, path, NULL, func, user_data, TRUE); + g_hash_table_add (obj->priv->downloading, file); + + g_thread_pool_push (obj->priv->pool, file, NULL); +} + +static void db_file_loader_job_upload (DbFileLoader * obj, File * file) +{ + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) db_file_loader_callback, file, + (GDestroyNotify) file_free); +} + +/** + * db_file_loader_upload: + * @obj: a #DbFileLoader + * @data: a #GBytes with the data to upload + * @path: a string with the path to the file + * @func: (scope async): the #DbFileLoaderCallbackFunc to call after uploading + * or in case of error + * @user_data: (closure): data to pass to @func + * + * Uploads @data to @file, which is a relative path to the file from the base + * URL of the #DbFileLoader. To retrieve the result, connect to the + * "downloaded" signal and to the "error" signal to manage errors. + **/ +void db_file_loader_upload (DbFileLoader * obj + ,GBytes * data + ,const gchar * path + ,DbFileLoaderCallbackFunc func + ,gpointer user_data) +{ + File * file; + + g_return_if_fail (DB_IS_FILE_LOADER (obj)); + g_return_if_fail (data); + g_return_if_fail (path); +//TODO? create a pool only for uploads + if (!obj->priv->pool) + obj->priv->pool = g_thread_pool_new ((GFunc) db_file_loader_job_upload, + obj, -1, TRUE, NULL); + + file = file_new (obj, path, data, func, user_data, TRUE); + + g_thread_pool_push (obj->priv->pool, file, NULL); +} + +static void db_file_loader_cancel (DbFileLoader * obj, const gchar * name) +{ + GHashTableIter iter; + File * file; + + g_return_if_fail (DB_IS_FILE_LOADER (obj)); + if (!obj->priv->downloading) return; + + g_hash_table_iter_init (&iter, obj->priv->downloading); + + while (g_hash_table_iter_next (&iter, (gpointer*) &file, NULL)) + if (!name || !g_strcmp0 (file->name, name)) + { + g_cancellable_cancel (file->cancel); + g_hash_table_iter_steal (&iter); + } +} + +/** + * db_file_loader_cancel_all: + * @obj: a #DbFileLoader + * + * Cancels all downloads pending or in progress. Note that after cancelling a + * task it may delay until it returns to the main loop. For the cause of this, + * see g_cancellable_cancel(). + **/ +void db_file_loader_cancel_all (DbFileLoader * obj) +{ + db_file_loader_cancel (obj, NULL); +} + +/** + * db_file_loader_cancel_by_name: + * @obj: a #DbFileLoader + * @name: an string with a filename + * + * Cancels the download of the file with name @name, that may be pending or in + * progress. See db_file_loader_cancel_all() for more information. + **/ +void db_file_loader_cancel_by_name (DbFileLoader * obj, const gchar * name) +{ + if (name) + db_file_loader_cancel (obj, name); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + HOST_PROP = 1 + ,PATH_PROP + ,CACHE_PROP + ,SIZE_PROP +}; + +static void db_file_loader_set_property (DbFileLoader * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case HOST_PROP: + { + g_free (obj->priv->host); + obj->priv->host = g_value_dup_string (value); + break; + } + case PATH_PROP: + { + g_free (obj->priv->path); + obj->priv->path = g_value_dup_string (value); + break; + } + case CACHE_PROP: + { + const gchar * cache = g_value_get_string (value); + g_free (obj->priv->cache); + obj->priv->cache = g_build_filename ( + (cache ? cache : g_get_user_cache_dir ()) + ,"hedera" + ,obj->priv->host + ,obj->priv->path + ,NULL); + break; + } + case SIZE_PROP: + { + obj->priv->size = g_value_get_int64 (value); + obj->priv->size = obj->priv->size <= 0 ? 0 : obj->priv->size; + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void db_file_loader_get_property (DbFileLoader * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case HOST_PROP: + g_value_set_string (value, obj->priv->host); + break; + case PATH_PROP: + g_value_set_string (value, obj->priv->path); + break; + case CACHE_PROP: + g_value_set_string (value, obj->priv->cache); + case SIZE_PROP: + g_value_set_int64 (value, obj->priv->size); + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_file_loader_init (DbFileLoader * obj) +{ + obj->priv = G_TYPE_INSTANCE_GET_PRIVATE + (obj, DB_TYPE_FILE_LOADER, DbFileLoaderPrivate); + obj->priv->host = NULL; + obj->priv->path = NULL; + obj->priv->cache = NULL; + obj->priv->size = CACHE_DEFAULT_SIZE; + obj->priv->addr = NULL; + obj->priv->pool = NULL; + obj->priv->mutex = g_new (GMutex, 1); + g_mutex_init (obj->priv->mutex); + obj->priv->cache_mutex = g_new (GMutex, 1); + g_mutex_init (obj->priv->cache_mutex); + obj->priv->downloading = g_hash_table_new_full + ((GHashFunc) g_direct_hash, (GEqualFunc) g_direct_equal, NULL, NULL); +} + +static void db_file_loader_finalize (DbFileLoader * obj) +{ + G_OBJECT_CLASS (db_file_loader_parent_class)->finalize (G_OBJECT (obj)); + + g_free (obj->priv->host); + g_free (obj->priv->path); + g_free (obj->priv->cache); + + g_clear_object (&obj->priv->addr); + + if (obj->priv->pool) + g_thread_pool_free (obj->priv->pool, TRUE, TRUE); + + g_mutex_clear (obj->priv->cache_mutex); + g_free (obj->priv->cache_mutex); + g_mutex_clear (obj->priv->mutex); + g_free (obj->priv->mutex); + + g_hash_table_destroy (obj->priv->downloading); +} + +static void db_file_loader_class_init (DbFileLoaderClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) db_file_loader_finalize; + k->set_property = (GObjectSetPropertyFunc) db_file_loader_set_property; + k->get_property = (GObjectGetPropertyFunc) db_file_loader_get_property; + g_type_class_add_private (klass, sizeof (DbFileLoaderPrivate)); + + g_object_class_install_property (k, HOST_PROP, + g_param_spec_string ("host" + ,_("Host") + ,_("The host web server name to get the images") + ,NULL + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); + + g_object_class_install_property (k, PATH_PROP, + g_param_spec_string ("path" + ,_("Path") + ,_("The path of the directory to interact with") + ,NULL + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); + + g_object_class_install_property (k, CACHE_PROP, + g_param_spec_string ("cache" + ,_("Cache directory") + ,_("The local directory where the downloaded files will be stored. " + "The default cache directory is 'hedera', under g_get_user_cache_dir().") + ,NULL + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); + + g_object_class_install_property (k, SIZE_PROP, + g_param_spec_int64 ("size" + ,_("Maximal cache size") + ,_("The maximal size for the contents of the cache directory") + ,0 + ,G_MAXOFFSET + ,CACHE_DEFAULT_SIZE + ,G_PARAM_READWRITE + )); +} diff --git a/db/db-file-loader.h b/db/db-file-loader.h new file mode 100644 index 0000000..0d0f70b --- /dev/null +++ b/db/db-file-loader.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_FILE_LOADER_H +#define DB_FILE_LOADER_H + +#include + +#define DB_FILE_LOADER_LOG_DOMAIN g_quark_from_string ("DbFileLoader") + +#define DB_TYPE_FILE_LOADER (db_file_loader_get_type ()) +#define DB_FILE_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, DB_TYPE_FILE_LOADER, DbFileLoader)) +#define DB_IS_FILE_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, DB_TYPE_FILE_LOADER)) +#define DB_FILE_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, DB_TYPE_FILE_LOADER, DbFileLoaderClass)) +#define DB_IS_FILE_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, DB_TYPE_FILE_LOADER)) +#define DB_FILE_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, DB_TYPE_FILE_LOADER, DbFileLoaderClass)) + +typedef struct _DbFileLoader DbFileLoader; +typedef struct _DbFileLoaderClass DbFileLoaderClass; +typedef struct _DbFileLoaderPrivate DbFileLoaderPrivate; + +/** + * DbFileLoaderCallbackFunc: + * @obj: a #DbFileLoader + * @data: the data of a file + * @error: a destination for a #GError + * @user_data: (closure): pointer to user data + * + * Function to call after any operation, successful or not. If the operation was + * cancelled, the error #G_IO_ERROR_CANCELLED will be returned. + **/ +typedef void (* DbFileLoaderCallbackFunc) (DbFileLoader * obj + ,GBytes * data + ,const GError * error + ,gpointer user_data); + +struct _DbFileLoader +{ + GObject parent; + DbFileLoaderPrivate * priv; +}; + +struct _DbFileLoaderClass +{ + /* */ + GObjectClass parent; +}; + +GType db_file_loader_get_type (); +DbFileLoader * db_file_loader_new (const gchar * host + ,const gchar * path); +DbFileLoader * db_file_loader_new_simple (const gchar * host + ,const gchar * path); +void db_file_loader_download (DbFileLoader * obj + ,const gchar * path + ,DbFileLoaderCallbackFunc func + ,gpointer user_data); +void db_file_loader_cancel_all (DbFileLoader * obj); +void db_file_loader_cancel_by_name (DbFileLoader * obj + ,const gchar * name); +void db_file_loader_upload (DbFileLoader * obj + ,GBytes * data + ,const gchar * path + ,DbFileLoaderCallbackFunc func + ,gpointer user_data); + +#endif \ No newline at end of file diff --git a/db/db-iter.c b/db/db-iter.c new file mode 100644 index 0000000..1db1fe8 --- /dev/null +++ b/db/db-iter.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-iter.h" + +/** + * SECTION: db-iter + * @Short_description: an iterator for the #DbModel + * @Title: DbIter + * @See_also: #DbModel + * + * The #DbIter is the structure used to navigate through a #DbModel. See #DbModel + * for the methods required to do it. + **/ +G_DEFINE_BOXED_TYPE (DbIter, db_iter, db_iter_copy, db_iter_free); + +/** + * db_model_iter_compare: + * @a: a #DbIter + * @b: a #DbIter + * + * Compares the two given iters for equality. The equality between two + * #DbIter is determined by its stamp and the row of a #DbModel they are + * pointing to. + * + * Return value: #TRUE if the two iters are the same + **/ +gboolean db_iter_compare (DbIter * a, DbIter * b) +{ + return (a->stamp == b->stamp && a->data == b->data); +} + +/** + * db_iter_copy: + * @obj: the #DbIter to copy + * + * Copies a #DbIter. Note that this function copies only the reference to the + * data in @obj. This is so due to the nature of #DbIter and it's use. + * + * Return value: a new #DbIter + **/ +DbIter * db_iter_copy (DbIter * obj) +{ + g_return_val_if_fail (obj, NULL); + + DbIter * dst = g_new (DbIter, 1); + dst->stamp = obj->stamp; + dst->data = obj->data; + dst->data2 = obj->data2; + dst->data3 = obj->data3; + return dst; +} + +void db_iter_free (DbIter * obj) +{ + g_return_if_fail (obj); + g_free (obj); +} diff --git a/db/db-iter.h b/db/db-iter.h new file mode 100644 index 0000000..f20b5d1 --- /dev/null +++ b/db/db-iter.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_ITER_H +#define DB_ITER_H + +#include + +#define DB_TYPE_ITER (db_iter_get_type()) + +typedef struct _DbIter DbIter; + +/** + * DbIter: + * @stamp: a unique stamp to catch invalid iterators + * @data: model-specific data + * @data2: model-specific data + * @data3: model-specific data + * + * The #DbIter is the primary structure for accessing a #DbModel. The #DbModel + * puts a unique integer in the @stamp member, and model-specific data in + * each of the three @data members. + **/ +struct _DbIter +{ + gint stamp; + gpointer data; + gpointer data2; + gpointer data3; +}; + +GType db_iter_get_type (); + +gboolean db_iter_compare (DbIter * a, DbIter * b); +DbIter * db_iter_copy (DbIter * obj); +void db_iter_free (DbIter * obj); + +#endif diff --git a/db/db-iterator.c b/db/db-iterator.c new file mode 100644 index 0000000..8585dbf --- /dev/null +++ b/db/db-iterator.c @@ -0,0 +1,1159 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-iterator.h" + +#define IS_READY(obj) (obj->model && db_model_get_status (obj->model) == DB_MODEL_STATUS_READY) + +/** + * SECTION:db-iterator + * @short_description: manages a iterator with its events + * @title: DbIterator + * + * The DbIterator manages a connection with a data base. + * There are different ways to create an DbIterator: + * + * + * + * db_iterator_new(): This constructor just needs a #DbModel object + * + * + * + * + * db_iterator_new_with_stmt(): This one needs a #DbConn with the connection and + * a #SqlStmt object + * + * + * + * + * db_iterator_new_with_sql(): This one needs a #DbConn with the connection and + * the sql string + * + * + * + */ +G_DEFINE_TYPE (DbIterator, db_iterator, G_TYPE_OBJECT); + +enum { + ITER_CHANGED + ,DATA_CHANGED + ,ROW_NUM_CHANGED + ,STATUS_CHANGED + ,OPERATIONS_DONE + ,LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +/** + * db_iterator_new: + * @model: the #DbModel used by iterator. + * + * Creates a new #DbIterator. + * + * Return value: a #DbIterator. + **/ +DbIterator * db_iterator_new (DbModel * model) +{ + return g_object_new (DB_TYPE_ITERATOR, "model", model, NULL); +} + +/** + * db_iterator_new_with_stmt: + * @conn: the connection used to create the model. + * @stmt: the #SqlStmt statement used to create the model. + * + * Creates a new #DbIterator. #SqlStmt must be a #SqlSelect or a #SqlString + * object with a valid SELECT statement. + * + * Return value: a #DbIterator. + **/ +DbIterator * db_iterator_new_with_stmt (DbConn * conn, SqlStmt * stmt) +{ + DbIterator * obj; + DbModel * model; + + g_return_val_if_fail (DB_IS_CONN (conn) || !conn, NULL); + g_return_val_if_fail (SQL_IS_STMT (stmt) || !stmt, NULL); + + model = db_model_new (conn, stmt); + obj = g_object_new (DB_TYPE_ITERATOR, "model", model, NULL); + g_object_unref (model); + + return obj; +} + +/** + * db_iterator_new_with_sql: + * @conn: the connection used to create the model. + * @sql: the sql string used to create the model. + * + * Creates a new #DbIterator. sql must be a valid SELECT statement. + * + * Return value: a #DbIterator. + **/ +DbIterator * db_iterator_new_with_sql (DbConn * conn, const gchar * sql) +{ + g_return_val_if_fail (sql, NULL); + g_return_val_if_fail (DB_IS_CONN (conn) || !conn, NULL); + + return g_object_new (DB_TYPE_ITERATOR, "sql", sql, "conn", conn, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void db_iterator_unref_param (DbIterator * obj, DbParam * param) +{ + obj->params = g_list_remove (obj->params, param); +} + +/* + * #DbIterator instances call this function every time line in + * the model is changed. + */ +static void db_iterator_row_num_changed (DbIterator * obj) +{ + if (obj->row_selected) + obj->row = db_model_get_path (obj->model, &obj->iter); + else if (!obj->remember_selection) + obj->row = -1; + + g_signal_emit (obj, signals[ROW_NUM_CHANGED], 0); +} + +/* + * #DbIterator instances call this function every time the data of the params + * mustbe updated. + */ +static void db_iterator_iter_changed (DbIterator * obj) +{ + db_iterator_row_num_changed (obj); + g_signal_emit (obj, signals[ITER_CHANGED], 0); +} + +static void db_iterator_set_iter (DbIterator * obj, DbIter * iter) +{ + if (iter) + { + obj->row_selected = TRUE; + obj->iter = *iter; + db_iterator_iter_changed (obj); + } + else if (obj->row_selected) + { + obj->row_selected = FALSE; + db_iterator_iter_changed (obj); + } +} + +/* + * Function called when row is inserted on the model. + */ +static void db_iterator_on_model_line_inserted (DbModel * model, DbIter * iter, DbIterator * obj) +{ + g_signal_emit (obj, signals[DATA_CHANGED], 0); +} + +/* + * Function called when row is updated on the model. + */ +static void db_iterator_on_model_line_updated_after (DbModel * model, DbIter * iter, DbIterator * obj) +{ + if (obj->row_selected && db_iter_compare (iter, &obj->iter)) + db_iterator_iter_changed (obj); + + g_signal_emit (obj, signals[DATA_CHANGED], 0); +} + +/* + * Function called when row is deleted on the model. + */ +static void db_iterator_on_model_line_deleted (DbModel * model, gint row, DbIterator * obj) +{ + if (obj->row_selected && row == obj->row) + { + DbIter iter; + + if (db_model_get_iter (model, &iter, row + 1) + || db_model_get_iter (model, &iter, row - 1)) + db_iterator_set_iter (obj, &iter); + else + db_iterator_set_iter (obj, NULL); + } +} + +static void db_iterator_on_model_line_deleted_after (DbModel * model, gint row, DbIterator * obj) +{ + if (obj->row_selected) + db_iterator_row_num_changed (obj); + + g_signal_emit (obj, signals[DATA_CHANGED], 0); +} + +/* + * Function called when model rows are reordered. + */ +static void db_iterator_on_model_lines_reordered (DbModel * model, + gint column, gint * new_order, DbIterator * obj) +{ + if (obj->row_selected) + db_iterator_row_num_changed (obj); +} + +/* + * Function called when model status changes. + */ +static void db_iterator_on_model_status_changed (DbModel * model, DbModelStatus status, DbIterator * obj) +{ + if (status == DB_MODEL_STATUS_READY) + { + DbIter iter; + gint nrows = db_model_get_nrows (model); + + g_signal_emit (obj, signals[STATUS_CHANGED], 0, IS_READY(obj)); + + if (obj->row >= 0 && obj->row < nrows + && db_model_get_iter (model, &iter, obj->row)) + db_iterator_set_iter (obj, &iter); + else + db_iterator_set_iter (obj, NULL); + } + else + { + db_iterator_set_iter (obj, NULL); + g_signal_emit (obj, signals[STATUS_CHANGED], 0, IS_READY(obj)); + } +} + +/* + * Function called when model operations are done. + */ +static void db_iterator_on_model_operations_done (DbModel * model, DbIterator * obj) +{ + g_signal_emit (obj, signals[OPERATIONS_DONE], 0); +} + +static void db_iterator_set_model_mode (DbIterator * obj) +{ + if (obj->mode == DB_ITERATOR_MODE_ON_CHANGE) + db_model_set_mode (obj->model, DB_MODEL_MODE_ON_CHANGE); + else + db_model_set_mode (obj->model, DB_MODEL_MODE_ON_DEMAND); +} + +/* + * Sets the model used as a data source by iterator. + */ +static void db_iterator_set_model (DbIterator * obj, DbModel * model) +{ + if (!model) + return; + + if (!obj->model) + { + obj->model = g_object_ref (model); + g_object_connect (obj->model + ,"signal::lines-reordered", db_iterator_on_model_lines_reordered, obj + ,"signal::line-inserted", db_iterator_on_model_line_inserted, obj + ,"signal-after::line-updated", db_iterator_on_model_line_updated_after, obj + ,"signal::line-deleted", db_iterator_on_model_line_deleted, obj + ,"signal-after::line-deleted", db_iterator_on_model_line_deleted_after, obj + ,"signal::status-changed", db_iterator_on_model_status_changed, obj + ,"signal::operations-done", db_iterator_on_model_operations_done, obj + ,NULL + ); + db_iterator_set_model_mode (obj); + db_iterator_on_model_status_changed (model, + db_model_get_status (model), obj); + } + else + g_warning ("DbIterator: Can't reassign the 'model' property"); +} + +/* + * Check if the iterator has any selected row, otherwise issues a message. + */ +static gboolean db_iterator_check_row_selected (DbIterator * obj) +{ + if (obj->row_selected) + return TRUE; + + g_warning ("DbIterator: Row not selected"); + return FALSE; +} + +/* + * Attempts to create a model, if have enough information, and assigns it to + * the iterator. + */ +static void db_iterator_try_create_model (DbIterator * obj) +{ + DbModel * new_model = NULL; + + if (obj->conn) + { + if (obj->sql) + { + if (obj->use_file) + new_model = db_model_new_with_file (obj->conn, obj->sql); + else + new_model = db_model_new_with_sql (obj->conn, obj->sql); + } + if (obj->stmt) + new_model = db_model_new (obj->conn, obj->stmt); + } + + if (new_model) + db_iterator_set_model (obj, new_model); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * db_iterator_get_row: + * @obj: a #DbIterator + * + * Gets the selected row number. + * + * Return value: the row number + **/ +gint db_iterator_get_row (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), -1); + + if (obj->row_selected) + return obj->row; + else + return -1; +} + +/** + * db_iterator_get_model: + * @obj: a #DbIterator + * + * Gets the model used as a data source by iterator. + * + * Return value: (transfer none): the #DbModel + **/ +DbModel * db_iterator_get_model (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), NULL); + + return obj->model; +} + +/** + * db_iterator_get_conn: + * @obj: a #DbIterator + * + * Gets connection used by the @obj model. + * + * Return value: (transfer none): the #DbConn + **/ +DbConn * db_iterator_get_conn (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), NULL); + + if (obj->model) + db_model_get_conn (obj->model); + + return NULL; +} + +/** + * db_iterator_set_conn: + * @obj: a #DbIterator + * @conn: the #DbConn + * + * Sets the connection used by the @obj model. + **/ +void db_iterator_set_conn (DbIterator * obj, DbConn * conn) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (DB_IS_CONN (conn)); + g_return_if_fail (obj->model); + + db_model_set_conn (obj->model, conn); +} + +/** + * db_iterator_is_ready: + * @obj: a #DbIterator + * + * Gets if the iterator is ready. + * + * Return value: %TRUE if the iterator and its model are ready, %FALSE otherwise. + **/ +gboolean db_iterator_is_ready (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), FALSE); + + return IS_READY (obj); +} + +/** + * db_iterator_get_mode: + * @obj: a #DbIterator + * + * Gets the mode in which the iterator is working. + * + * Return value: the #DbIteratorMode + **/ +DbIteratorMode db_iterator_get_mode (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), 0); + + return obj->mode; +} + +/** + * db_iterator_set_mode: + * @obj: a #DbIterator + * @mode: the #DbIteratorMode mode + * + * Sets the mode in which the iterator should work. + **/ +void db_iterator_set_mode (DbIterator * obj, DbIteratorMode mode) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + obj->mode = mode; + + if (obj->model) + db_iterator_set_model_mode (obj); +} + +/** + * db_iterator_get_iter: + * @obj: a #DbIterator + * @iter: (allow-none): return location for selected row iter, or %NULL. + * + * Gets the currently selected iter. + * + * Return value: %TRUE if any row is selected, %FALSE otherwise. + **/ +gboolean db_iterator_get_iter (DbIterator * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), FALSE); + + if (!obj->row_selected) + return FALSE; + + *iter = obj->iter; + return TRUE; +} + +/** + * db_iterator_move_iter: + * @obj: a #DbIterator + * @iter: a #DbIter + * + * Moves the iterator cursor to the specified iter, if model is ready. + **/ +void db_iterator_move_iter (DbIterator * obj, DbIter * iter) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (IS_READY (obj)); + g_return_if_fail (obj->model); + + if (obj->row_selected && db_iter_compare (&obj->iter, iter)) + return; + + if (obj->mode != DB_ITERATOR_MODE_ON_DEMAND) + db_model_perform_operations (obj->model, FALSE); + + db_iterator_set_iter (obj, iter); +} + +/** + * db_iterator_move_first: + * @obj: a #DbIterator + * + * Moves the iterator cursor to the first row. + **/ +void db_iterator_move_first (DbIterator * obj) +{ + DbIter iter; + + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (obj->model); + + if (db_model_get_iter_first (obj->model, &iter)) + db_iterator_move_iter (obj, &iter); +} + +/** + * db_iterator_move_last: + * @obj: a #DbIterator + * + * Moves the iterator cursor to the last row. + **/ +void db_iterator_move_last (DbIterator * obj) +{ + DbIter iter; + + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (obj->model); + + if (db_model_get_last (obj->model, &iter)) + db_iterator_move_iter (obj, &iter); +} + +/** + * db_iterator_move_previous: + * @obj: a #DbIterator + * + * Moves the iterator cursor to the previous row, or displays a message if no + * exists. If no selected row, attempts to move the cursor to the first row. + **/ +void db_iterator_move_previous (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (obj->row_selected) + { + DbIter iter = obj->iter; + + if (db_model_iter_prev (obj->model, &iter)) + db_iterator_move_iter (obj, &iter); + else + g_warning ( + "Can't move the cursor to the previous " + "row, because there are no more rows." + ); + } + else + db_iterator_move_first (obj); +} + +/** + * db_iterator_move_next: + * @obj: a #DbIterator + * + * Moves the iterator cursor to the next row, or displays a message if no exists. + * If no selected row, attempts to move the cursor to the first row. + **/ +void db_iterator_move_next (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (obj->row_selected) + { + DbIter iter = obj->iter; + + if (db_model_iter_next (obj->model, &iter)) + db_iterator_move_iter (obj, &iter); + else + g_warning ( + "Can't move the cursor to the next " + "row, because there are no more rows." + ); + } + else + db_iterator_move_first (obj); +} + +/** + * db_iterator_move_to: + * @obj: a #DbIterator + * @move: a #DbIteratorMove + * + * Moves the iterator cursor to the predefined position. + **/ +void db_iterator_move_to (DbIterator * obj, DbIteratorMove move) +{ + switch (move) + { + case DB_ITERATOR_MOVE_FIRST: + db_iterator_move_first (obj); + break; + case DB_ITERATOR_MOVE_PREVIOUS: + db_iterator_move_previous (obj); + break; + case DB_ITERATOR_MOVE_NEXT: + db_iterator_move_next (obj); + break; + case DB_ITERATOR_MOVE_LAST: + db_iterator_move_last (obj); + break; + } +} + +/** + * db_iterator_insert: + * @obj: a #DbIterator + * + * Inserts a new row in the model and moves the iter to it, if inserted. + * successfully. + **/ +void db_iterator_insert (DbIterator * obj) +{ + DbIter iter; + + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (obj->model); + + if (obj->mode != DB_ITERATOR_MODE_ON_DEMAND) + db_model_perform_operations (obj->model, FALSE); + + if (db_model_insert (obj->model, &iter)) + db_iterator_set_iter (obj, &iter); +} + +/** + * db_iterator_delete: + * @obj: a #DbIterator + * + * Deletes the currently selected row from the model, if any. + **/ +void db_iterator_delete (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (db_iterator_check_row_selected (obj)) + { + db_model_delete (obj->model, &obj->iter); + + if (obj->mode != DB_ITERATOR_MODE_ON_DEMAND) + db_model_perform_operations (obj->model, FALSE); + } +} + +/** + * db_iterator_refresh: + * @obj: a #DbIterator + * + * Refresh the data of the model handled by iterator. + **/ +void db_iterator_refresh (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (obj->model) + db_model_refresh (obj->model); +}; + +/** + * db_iterator_get_spec: + * @obj: a #DbIterator + * @column: the column index. + * + * Gets the spec from the model of the specified column index. + * + * Return value: (transfer none) (allow-none): a #GvnParamSpec or %NULL if + * can't get it because the model isn't ready. + **/ +const GvnParamSpec * db_iterator_get_spec (DbIterator * obj, gint column) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), NULL); + + if (IS_READY (obj)) + return db_model_get_spec (obj->model, column); + else + return NULL; +} + +/** + * db_iterator_get_value: + * @obj: a #DbIterator + * @column: the column index. + * + * Gets the value of the specified column index. + * + * Return value: (transfer none) (allow-none): the value or %NULL if + * can't get it because the model isn't ready. + **/ +const GValue * db_iterator_get_value (DbIterator * obj, gint column) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), NULL); + + if (obj->row_selected) + return db_model_get_value (obj->model, &obj->iter, column, NULL); + + return NULL; +} + +/** + * db_iterator_get_column_index: + * @obj: a #DbIterator + * @name: the name of a column of iterator. + * + * Retrieves the position in the query of the column called @name. + * + * Return value: the position of the column with name @name or -1 if there isn't + * a column called name or if name is %NULL. + **/ +gint db_iterator_get_column_index (DbIterator * obj, const gchar * name) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), -1); + g_return_val_if_fail (IS_READY (obj), -1); + + return db_model_get_column_index (obj->model, name); +} + +/** + * db_iterator_set_value: + * @obj: a #DbIterator + * @column: the column index. + * @value: a #GValue with the new value. + * @err: (out) (allow-none): the return location for a #GError or %NULL. + * + * Sets the value of the specified column index. + **/ +void db_iterator_set_value (DbIterator * obj, gint column, const GValue * value, GError ** err) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (db_iterator_check_row_selected (obj)) + db_model_set_value (obj->model, &obj->iter, column, value, err); +} + +/** + * db_iterator_add_param: + * @obj: a #DbIterator + * @param: the param to add. + * + * Adds a #DbParam to the list of iterator params. + **/ +void db_iterator_add_param (DbIterator * obj, DbParam * param) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (DB_IS_PARAM (param)); + + db_param_set_iterator (param, obj); + g_object_weak_ref (G_OBJECT (param), + (GWeakNotify) db_iterator_unref_param, obj); + obj->params = g_list_prepend (obj->params, param); +} + +/** + * db_iterator_get_param: + * @obj: a #DbIterator + * @column: the column name + * + * Creates a parameter for the specified column index and returns it. + * + * Return value: (transfer none): a #GvnParam + **/ +GvnParam * db_iterator_get_param (DbIterator * obj, const gchar * column) +{ + GList * n; + GvnParam * param; + + g_return_val_if_fail (DB_IS_ITERATOR (obj), NULL); + g_return_val_if_fail (column, NULL); + + for (n = obj->params; n; n = n->next) + if (!g_strcmp0 (db_param_get_column_name (n->data), column)) + break; + + if (!n) + { + param = db_param_new (column); + db_iterator_add_param (obj, DB_PARAM (param)); + } + else + param = n->data; + + return param; +} + +/** + * db_iterator_get_params: + * @obj: a #DbIterator + * + * Gets a list of params linked with the iterator. The returned list shoud be freed. + * + * Return value: (element-type Db.Param) (transfer container): the #GList + **/ +GList * db_iterator_get_params (DbIterator * obj) +{ + return g_list_copy (obj->params); +} + +/** + * db_iterator_bind_param: + * @obj: a #DbIterator + * @column: the column index. + * @param: the column index. + * + * Binds the param to the specified column index. If you want to link the same + * param several times you should use the db_iterator_get_param function. + **/ +void db_iterator_bind_param (DbIterator * obj, const gchar * column, GvnParam * param) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (GVN_IS_PARAM (obj)); + + gvn_param_set_master (param, + db_iterator_get_param (obj, column) + ); +} + +/** + * db_iterator_link: + * @obj: a #DbIterator + * @field: the field name in the iterator statement + * @src: the source #DbIterator + * @column: the column number of @src + * + * Links the iterator with another iterator parameter. + **/ +void db_iterator_link (DbIterator * obj, const gchar * field, DbIterator * src, const gchar * column) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (DB_IS_ITERATOR (src)); + g_return_if_fail (field); + + db_iterator_link_with_param (obj, field, + db_iterator_get_param (src, column)); +} + +/** + * db_iterator_link_with_param: + * @obj: a #DbIterator + * @field: the field name in the iterator statement + * @param: the #GvnParam + * + * Links the iterator with a parameter. + **/ +void db_iterator_link_with_param (DbIterator * obj, const gchar * field, GvnParam * param) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (GVN_IS_PARAM (param)); + g_return_if_fail (obj->model); + g_return_if_fail (field); + + db_model_set_default_value_from_param (obj->model, field, param); +} + +/** + * db_iterator_get_nrows: + * @obj: a #DbIterator + * + * Gets the number of rows in the model pointed by the iterator. + * + * Return value: the number of rows + **/ +gint db_iterator_get_nrows (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), 0); + + if (obj->model) + return db_model_get_nrows (obj->model); + + return 0; +} + +/** + * db_iterator_get_update_flags: + * @obj: a #DbIterator + * + * Gets the flags that indicate how a model can be modified. + * + * Return value: the flags + **/ +DbModelUpdateFlags db_iterator_get_update_flags (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), 0); + + if (obj->model) + return db_model_get_update_flags (obj->model); + + return 0; +} + +/** + * db_iterator_perform_operations: + * @obj: a #DbIterator + * + * Performs all pending operations on the model. + **/ +void db_iterator_perform_operations (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (obj->model) + db_model_perform_operations (obj->model, FALSE); +} + +/** + * db_iterator_reverse_operations: + * @obj: a #DbIterator + * + * Reverses all pending operations on the model. + **/ +void db_iterator_reverse_operations (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (obj->model) + db_model_reverse_operations (obj->model); +} + +/** + * db_iterator_get_pending_operations: + * @obj: a #DbIterator + * + * Gets pending operations to perform in the current row. + * + * Return value: the pending operations + **/ +DbModelRowOp db_iterator_get_pending_operations (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), 0); + + if (obj->model && obj->row_selected) + return db_model_get_row_operations (obj->model, &obj->iter); + + return 0; +} + +/** + * db_iterator_has_pending_operations: + * @obj: a #DbIterator + * + * Checks for pending operations to perform. + * + * Return value: %TRUE if there are pending operations, %FALSE otherwise + **/ +gboolean db_iterator_has_pending_operations (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), FALSE); + + if (obj->model) + return db_model_has_pending_operations (obj->model); + + return FALSE; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_MODEL = 1 + ,PROP_MODE + ,PROP_REMEMBER_SELECTION + ,PROP_CONN + ,PROP_STMT + ,PROP_SQL + ,PROP_USE_FILE +}; + +static void db_iterator_set_property (DbIterator * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_MODEL: + db_iterator_set_model (obj, g_value_get_object (value)); + break; + case PROP_MODE: + db_iterator_set_mode (obj, g_value_get_enum (value)); + break; + case PROP_REMEMBER_SELECTION: + obj->remember_selection = g_value_get_boolean (value); + break; + case PROP_CONN: + g_clear_object (&obj->conn); + obj->conn = g_value_dup_object (value); + db_iterator_try_create_model (obj); + break; + case PROP_STMT: + g_clear_object (&obj->stmt); + obj->stmt = g_value_dup_object (value); + db_iterator_try_create_model (obj); + break; + case PROP_SQL: + g_free (obj->sql); + obj->sql = g_value_dup_string (value); + db_iterator_try_create_model (obj); + break; + case PROP_USE_FILE: + obj->use_file = g_value_get_boolean (value); + db_iterator_try_create_model (obj); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void db_iterator_get_property (DbIterator * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_MODEL: + g_value_set_object (value, obj->model); + break; + case PROP_MODE: + g_value_set_enum (value, obj->mode); + break; + case PROP_REMEMBER_SELECTION: + g_value_set_boolean (value, obj->remember_selection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_iterator_init (DbIterator * obj) +{ + obj->model = NULL; + obj->params = NULL; + obj->row = 0; + obj->row_selected = FALSE; + obj->conn = NULL; + obj->stmt = NULL; + obj->sql = NULL; + obj->use_file = FALSE; +} + +static void db_iterator_finalize (DbIterator * obj) +{ + GList * n; + + for (n = obj->params; n; n = n->next) + g_object_weak_unref (n->data, + (GWeakNotify) db_iterator_unref_param, obj); + + g_list_free (obj->params); + + if (obj->model) + { + g_object_disconnect (obj->model + ,"any_signal", db_iterator_on_model_line_inserted, obj + ,"any_signal", db_iterator_on_model_line_updated_after, obj + ,"any_signal", db_iterator_on_model_line_deleted, obj + ,"any_signal", db_iterator_on_model_line_deleted_after, obj + ,"any_signal", db_iterator_on_model_lines_reordered, obj + ,"any_signal", db_iterator_on_model_status_changed, obj + ,"any_signal", db_iterator_on_model_operations_done, obj + ,NULL + ); + g_object_unref (obj->model); + } + + g_clear_object (&obj->conn); + g_clear_object (&obj->stmt); + g_free (obj->sql); + + G_OBJECT_CLASS (db_iterator_parent_class)->finalize (G_OBJECT (obj)); +} + +static void db_iterator_class_init (DbIteratorClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->set_property = (GObjectSetPropertyFunc) db_iterator_set_property; + k->get_property = (GObjectGetPropertyFunc) db_iterator_get_property; + k->finalize = (GObjectFinalizeFunc) db_iterator_finalize; + + signals[ITER_CHANGED] = g_signal_new ("iter-changed", + DB_TYPE_ITERATOR, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + signals[DATA_CHANGED] = g_signal_new ("data-changed", + DB_TYPE_ITERATOR, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + signals[ROW_NUM_CHANGED] = g_signal_new ("row-num-changed", + DB_TYPE_ITERATOR, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + signals[STATUS_CHANGED] = g_signal_new ("status-changed", + DB_TYPE_ITERATOR, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN + ); + signals[OPERATIONS_DONE] = g_signal_new ("operations-done", + DB_TYPE_ITERATOR, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + + g_object_class_install_property (k, PROP_MODEL, + g_param_spec_object ("model" + ,_("Model") + ,_("The DbModel handled by the iterator") + ,DB_TYPE_MODEL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_MODE, + g_param_spec_enum ("mode" + ,_("Mode") + ,_("The mode in which the iterator is working") + ,DB_TYPE_ITERATOR_MODE + ,DB_ITERATOR_MODE_ON_CHANGE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_REMEMBER_SELECTION, + g_param_spec_boolean ("remember-selection" + ,_("Remember selection") + ,_("Wether to rememeber the selection when model is refreshed") + ,TRUE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by the model") + ,DB_TYPE_CONN + ,G_PARAM_WRITABLE + )); + g_object_class_install_property (k, PROP_STMT, + g_param_spec_object ("stmt" + ,_("Statement") + ,_("The statement used to create the model") + ,SQL_TYPE_STMT + ,G_PARAM_WRITABLE + )); + g_object_class_install_property (k, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("The SQL query used to create the model") + , NULL + ,G_PARAM_WRITABLE + )); + g_object_class_install_property (k, PROP_USE_FILE, + g_param_spec_boolean ("use-file" + ,_("Use file") + ,_("Wether to interpret the sql property as query file") + ,FALSE + ,G_PARAM_CONSTRUCT | G_PARAM_WRITABLE + )); +} + +GType db_iterator_mode_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GEnumValue values[] = + { + {DB_ITERATOR_MODE_ON_CHANGE, "DB_ITERATOR_MODE_ON_CHANGE", "on-change"} + ,{DB_ITERATOR_MODE_ON_ITER, "DB_ITERATOR_MODE_ON_ITER", "on-iter"} + ,{DB_ITERATOR_MODE_ON_DEMAND, "DB_ITERATOR_MODE_ON_DEMAND", "on-demand"} + ,{0, NULL, NULL} + }; + + type = g_enum_register_static + (g_intern_static_string ("DbIteratorMode"), values); + } + + return type; +} diff --git a/db/db-iterator.h b/db/db-iterator.h new file mode 100644 index 0000000..d17a0ce --- /dev/null +++ b/db/db-iterator.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_ITERATOR_H +#define DB_ITERATOR_H + +#include "db-model.h" + +#define DB_TYPE_ITERATOR (db_iterator_get_type ()) +#define DB_ITERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, DB_TYPE_ITERATOR, DbIterator)) +#define DB_IS_ITERATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, DB_TYPE_ITERATOR)) +#define DB_ITERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, DB_TYPE_ITERATOR, DbIteratorClass)) +#define DB_IS_ITERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, DB_TYPE_ITERATOR)) +#define DB_ITERATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, DB_TYPE_ITERATOR, DbIteratorClass)) + +#define DB_TYPE_ITERATOR_MODE (db_iterator_mode_get_type ()) + +typedef struct _DbIterator DbIterator; +typedef struct _DbIteratorClass DbIteratorClass; + +#include "db-param.h" + +/** + * DbIteratorMode: + * @DB_ITERATOR_MODE_ON_CHANGE: every change made in a cell will be sent to the + * database instantly. + * @DB_ITERATOR_MODE_ON_ITER: every change made in a row will be sent to the + * database when iter changes. + * @DB_ITERATOR_MODE_ON_DEMAND: nothing will be sent to the database since it's + * demanded. + * + * The working mode of a Iterator. + **/ +typedef enum +{ + DB_ITERATOR_MODE_ON_CHANGE + ,DB_ITERATOR_MODE_ON_ITER + ,DB_ITERATOR_MODE_ON_DEMAND +} +DbIteratorMode; + +/** + * DbIteratorMove: + * @DB_ITERATOR_MOVE_FIRST: Moves to the first position. + * @DB_ITERATOR_MOVE_PREVIOUS: Moves previous. + * @DB_ITERATOR_MOVE_NEXT: Moves next. + * @DB_ITERATOR_MOVE_LAST: Moves to the last position. + * + * Indicates the movement. + **/ +typedef enum +{ + DB_ITERATOR_MOVE_FIRST + ,DB_ITERATOR_MOVE_PREVIOUS + ,DB_ITERATOR_MOVE_NEXT + ,DB_ITERATOR_MOVE_LAST +} +DbIteratorMove; + +struct _DbIterator +{ + GObject parent; + DbModel * model; + GList * params; + DbIteratorMode mode; + DbIter iter; + gint row; + gboolean row_selected; + gboolean remember_selection; + + DbConn * conn; + SqlStmt * stmt; + gchar * sql; + gboolean use_file; +}; + +struct _DbIteratorClass +{ + /* */ + GObjectClass parent; +}; + +GType db_iterator_get_type (); +GType db_iterator_mode_get_type () G_GNUC_CONST; + +DbIterator * db_iterator_new (DbModel * model); +DbIterator * db_iterator_new_with_stmt (DbConn * conn, SqlStmt * stmt); +DbIterator * db_iterator_new_with_sql (DbConn * conn, const gchar * sql); +DbModel * db_iterator_get_model (DbIterator * obj); +DbConn * db_iterator_get_conn (DbIterator * obj); +void db_iterator_set_conn (DbIterator * obj, DbConn * conn); +gboolean db_iterator_is_ready (DbIterator * obj); +DbIteratorMode db_iterator_get_mode (DbIterator * obj); +void db_iterator_set_mode (DbIterator * obj, DbIteratorMode mode); +gint db_iterator_get_row (DbIterator * obj); +gboolean db_iterator_get_iter (DbIterator * obj, DbIter * iter); +void db_iterator_move_iter (DbIterator * obj, DbIter * iter); +void db_iterator_move_first (DbIterator * obj); +void db_iterator_move_last (DbIterator * obj); +void db_iterator_move_previous (DbIterator * obj); +void db_iterator_move_next (DbIterator * obj); +void db_iterator_move_to (DbIterator * obj, DbIteratorMove move); +void db_iterator_refresh (DbIterator * obj); +const GvnParamSpec * db_iterator_get_spec (DbIterator * obj, gint column); +const GValue * db_iterator_get_value (DbIterator * obj, gint column); +gint db_iterator_get_column_index (DbIterator * obj, const gchar * name); +void db_iterator_set_value (DbIterator * obj, gint column, const GValue * value, GError ** err); +void db_iterator_delete (DbIterator * obj); +void db_iterator_insert (DbIterator * obj); +void db_iterator_add_param (DbIterator * obj, DbParam * param); +GvnParam * db_iterator_get_param (DbIterator * obj, const gchar * column); +GList * db_iterator_get_params (DbIterator * obj); +void db_iterator_bind_param (DbIterator * obj, const gchar * column, GvnParam * param); +void db_iterator_link (DbIterator * obj, const gchar * field, DbIterator * src, const gchar * column); +void db_iterator_link_with_param (DbIterator * obj, const gchar * field, GvnParam * param); +gint db_iterator_get_nrows (DbIterator * obj); +DbModelUpdateFlags db_iterator_get_update_flags (DbIterator * obj); +void db_iterator_reverse_operations (DbIterator * obj); +void db_iterator_perform_operations (DbIterator * obj); +DbModelRowOp db_iterator_get_pending_operations (DbIterator * obj); +gboolean db_iterator_has_pending_operations (DbIterator * obj); + +#endif \ No newline at end of file diff --git a/db/db-model-holder.c b/db/db-model-holder.c new file mode 100644 index 0000000..ef7db6e --- /dev/null +++ b/db/db-model-holder.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-model-holder.h" + +/** + * SECTION: db-model-holder + * @Short_description: + * @Title: DbModelHolder + * @See_also: #DbModel + * + * This interface should be implemented from any class that uses a #DbModel as + * datasource. + **/ + +G_DEFINE_INTERFACE (DbModelHolder, db_model_holder, G_TYPE_INTERFACE); + +/** + * db_model_holder_get_model: + * @obj: a #DbModelHolder + * + * Gets the model used by holder. + * + * Return value: the #DbModel + **/ +DbModel * db_model_holder_get_model (DbModelHolder * obj) +{ + g_return_val_if_fail (DB_IS_MODEL_HOLDER (obj), NULL); + + return DB_MODEL_HOLDER_GET_INTERFACE (obj)->get_model (obj); +} + +/** + * db_model_holder_get_model: + * @obj: a #DbModelHolder + * @model: the #DbModel + * + * Sets the model used by holder. + **/ +void db_model_holder_set_model (DbModelHolder * obj, DbModel * model) +{ + g_return_if_fail (DB_IS_MODEL_HOLDER (obj)); + + DB_MODEL_HOLDER_GET_INTERFACE (obj)->set_model (obj, model); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_model_holder_default_init (DbModelHolderInterface * klass) +{ + g_object_interface_install_property (klass, + g_param_spec_object ("model" + ,"Model" + ,"The model used by the holder" + ,DB_TYPE_MODEL + ,G_PARAM_READWRITE + )); +} diff --git a/db/db-model-holder.h b/db/db-model-holder.h new file mode 100644 index 0000000..1a475ea --- /dev/null +++ b/db/db-model-holder.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_MODEL_HOLDER_H +#define DB_MODEL_HOLDER_H + +#include "db-model.h" + +#define DB_TYPE_MODEL_HOLDER (db_model_holder_get_type ()) +#define DB_MODEL_HOLDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_MODEL_HOLDER, DbModelHolder)) +#define DB_IS_MODEL_HOLDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_MODEL_HOLDER)) +#define DB_MODEL_HOLDER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), DB_TYPE_MODEL_HOLDER, DbModelHolderInterface)) + +typedef struct _DbModelHolder DbModelHolder; +typedef struct _DbModelHolderInterface DbModelHolderInterface; + +typedef DbModel * (* DbModelHolderGetModelFunc) (DbModelHolder * obj); +typedef void (* DbModelHolderSetModelFunc) (DbModelHolder * obj, DbModel * sql); + +struct _DbModelHolder {}; + +struct _DbModelHolderInterface +{ + /* */ + GTypeInterface parent; + DbModelHolderGetModelFunc get_model; + DbModelHolderSetModelFunc set_model; +}; + +GType db_model_holder_get_type (); +DbModel * db_model_holder_get_model (DbModelHolder * obj); +void db_model_holder_set_model (DbModelHolder * obj, DbModel * model); + +#endif \ No newline at end of file diff --git a/db/db-model.c b/db/db-model.c new file mode 100644 index 0000000..912eb4d --- /dev/null +++ b/db/db-model.c @@ -0,0 +1,3444 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This file is part of Hedera. + * + * Hedera 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 . + */ + +#include +#include "db-model.h" +#include "db-row.h" + +/** + * SECTION: db-model + * @Short_description: data vinculed to a SELECT query sent to the database + * @Title: DbModel + * @See_also: #DbIterator, #DbConn + * + * The #DbModel class gets an SQL query statement to retrieve the data from the + * database connected by a #DbConn. It is normally used undirectly, using instead + * #DbForm. + **/ + +/* + * DbModel: + * @data: (element-type Db.Row): + * @operation: (element-type Db.Operation): + * @row_ops: (element-type Db.Row Db.Operation): + **/ +struct _DbModelPrivate +{ + DbConn * conn; + SqlStmt * stmt; + gchar * sql; + gboolean use_file; + gchar * main_table; + DbModelUpdateFlags update_flags; + + GPtrArray * data; + DbColumn * column; + DbResult * result; + DbRequest * request; + DbModelStatus status; + DbModelMode mode; + gchar * user_main_table; + DbModelUpdateFlags user_update_flags; + guint result_pos; + GQueue * operation; + GHashTable * row_ops; + gint updated_col; + GValue * updated_value; + + GHashTable * column_index; + GSList * pending_request; + GSList * join; + GSList * column_default; + GSList * param_default; + + gint stamp; + + gboolean fresh; + gint sort_column_id; + gint old_sort_column_id; + DbSortType order; + DbSortType old_order; + DbIterCompareFunc default_sort_func; + gpointer default_sort_data; + GDestroyNotify default_sort_destroy; + DbIterCompareFunc sort_func; + gpointer sort_data; + GDestroyNotify sort_destroy; +}; + +G_DEFINE_TYPE (DbModel, db_model, G_TYPE_OBJECT) + +#define MODEL_NOT_READY(obj) (obj->priv->status != DB_MODEL_STATUS_READY) +#define VALID_ITER(iter, model) (iter->data && iter->stamp == model->priv->stamp) +#define DB_ROW_FIELD(row, field) (&((DbRow *) row)->value[field]) +#define DB_ROW_POSITION(row) (((DbRow *) row)->position) + +// Constructors + +/** + * db_model_new: + * @conn: a #DbConn + * @stmt: an #SqlStmt + * + * Returns the newly created #DbModel, filled with the data retrieved from the + * database with @stmt and through @conn. + * + * Return value: (transfer full): a new #DbModel + **/ +DbModel * db_model_new (DbConn * conn, SqlStmt * stmt) +{ + return g_object_new (DB_TYPE_MODEL, "conn", conn, "stmt", stmt, NULL); +} + +/** + * db_model_new_with_sql: + * @conn: a #DbConn + * @sql: a string containing an SQL statement + * + * Returns the newly created #DbModel, filled with the data retrieved from the + * database with @stmt and through @conn. + * + * Return value: (transfer full): a new #DbModel + **/ +DbModel * db_model_new_with_sql (DbConn * conn, const gchar * sql) +{ + return g_object_new (DB_TYPE_MODEL, + "conn", conn, "use-file", FALSE, "sql", sql, NULL); +} + +/** + * db_model_new_with_file: + * @conn: a #DbConn + * @file: the path to the file containing the SQL query to fill the model + * + * Returns a newly created #DbModel, filled with the data retrieved from @file. + * + * Return value: (transfer full): a new #DbModel + **/ +DbModel * db_model_new_with_file (DbConn * conn, const gchar * file) +{ + return g_object_new (DB_TYPE_MODEL, + "conn", conn, "use-file", TRUE, "sql", file, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +// Helper structures and methods + +// Structures + +/* + * DbUpdatedField: + * @column: the position of the field in the row + * @value: the old value of the updated field + * + * Previous value of an updated field. + **/ +typedef struct +{ + gint column; + GValue * value; +} +DbUpdatedField; + +/* + * DbOperation: + * @type: #DbModelRowOp flags + * @locked: %TRUE while the operation is being performed + * @row: the #DbRow over which the operation has been performed + * @updated: (element-type Db.UpdatedField): old values for the updated fields + * in @row + * @request: #DbRequest associated to the operation, once performed + * + * A structure explaining the operations performed over each #DbRow. + **/ +typedef struct +{ + DbModelRowOp type; + gboolean locked; + DbRow * row; + GSList * updated; +} +DbOperation; + +/* + * DbModelRequest: + * @request: a DbRequest being performed + * @operations: a GQueue of operations being performed + * @model: the DbModel over which the operations are being performed + * + * This struct holds the information of a request performed but not yet + * finalized. + **/ +typedef struct +{ + DbModel * obj; + GQueue * operations; +} +DbModelRequest; + +typedef struct +{ + DbModel * obj; + DbIter * iter; + gint col; +} +JoinData; + +typedef struct +{ + gchar * schema; + gchar * table; + GPtrArray * name; + gboolean main; +} +DbModelField; + +typedef struct +{ + DbModelField * left; + DbModelField * right; +} +DbJoin; + +typedef struct +{ + gchar * dst; + gint col; +} +DbColDef; + +typedef struct +{ + gchar * dst; + GvnParam * param; +} +DbParamDef; + +typedef struct +{ + gint count; + gint * index; +} +DbModelPKey; + +enum +{ + DB_MODEL_UNSORTED_SORT_COLUMN_ID = -2, + DB_MODEL_DEFAULT_SORT_COLUMN_ID +}; + +// Prototypes + +static void db_model_set_status (DbModel * obj + ,DbModelStatus status); +static void db_model_clear (DbModel * obj); +static void db_model_free_operation (DbModel * obj + ,DbOperation * op); +static void db_model_manage_join (DbModel * obj + ,DbIter * iter + ,gint col); +static void db_model_post_process_query (DbModel * obj); +static void db_model_finish_insert (DbModel * obj + ,DbRow * req_row, gint i + ,DbRow * row, gint j + ,DbIter * iter); +static gboolean db_model_table_row_all_null (DbModel * model + ,DbRow * row + ,gint col); + +// Signal Handlers + +enum +{ + STATUS_CHANGED + ,LINE_INSERTED + ,LINE_DELETED + ,LINE_TOGGLED + ,LINE_UPDATED + ,LINES_REORDERED + ,SORT_CHANGED + ,OPERATIONS_DONE + + ,LAST_SIGNAL +}; + +static guint db_model_signal[LAST_SIGNAL] = {0}; + +// Default signal handlers + +static void db_model_on_line_updated (DbModel * obj, DbIter * iter) +{ + GValue * updated_value = obj->priv->updated_value; + gint col; + + if (!updated_value || !G_IS_VALUE (updated_value)) return; + + col = obj->priv->updated_col; + gvn_value_ccopy (updated_value, DB_ROW_FIELD (iter->data, col)); + + db_model_manage_join (obj, iter, col); + + g_value_unset (updated_value); + g_free (updated_value); + obj->priv->updated_value = NULL; +} + +static void db_model_on_line_deleted (DbModel * obj, gint position) +{ + gint r_ind; + + for (r_ind = position + 1; r_ind < obj->priv->result->nrows; r_ind++) + DB_ROW_POSITION (g_ptr_array_index (obj->priv->data, (guint) r_ind))--; + + g_ptr_array_remove_index (obj->priv->data, (guint) position); + + obj->priv->result->nrows--; +} + +// External signal handlers + +static void db_model_calculate_update_flags (DbModel * obj) +{ + gint i; + gchar * main_table = NULL; + DbModelPrivate * priv = obj->priv; + + if (priv->result) + for (i = 0; i < priv->result->ncols && !main_table; i++) + if (priv->column[i].info & DB_COLUMN_PRI_KEY) + { + if (!priv->user_main_table) + main_table = priv->column[i].table; + else if (!g_strcmp0 (priv->user_main_table, priv->column[i].table)) + main_table = priv->user_main_table; + } + + g_free (priv->main_table); + + if (main_table) + { + priv->update_flags = DB_MODEL_ALL & priv->user_update_flags; + priv->main_table = g_strdup (main_table); + } + else + { + if (priv->user_main_table && priv->result) + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN), G_LOG_LEVEL_WARNING, + "The requested table can't be set as main table"); + + priv->update_flags = 0; + priv->main_table = NULL; + } +} + +static void db_model_on_data_ready (DbRequest * request, DbModel * obj) +{ + gint i; + DbResult * r; + DbModelPrivate * priv = obj->priv; + + if (priv->request != request) + { + g_object_unref (request); + return; + } + + for (i = 0; i < priv->result_pos; i++) + db_request_fetch_non_select (request, NULL); + + if ((r = db_request_fetch_result (request, NULL))) + { + gint i, j; + + priv->column = r->column; + priv->data = r->data; + + if (!r->data && !r->column) + { + db_result_free (r); + db_model_set_status (obj, DB_MODEL_STATUS_CLEAN); + } + else + { + GSList * t = NULL; + + priv->result = r; + + if (priv->fresh) + priv->column_index = g_hash_table_new_full (g_str_hash, + g_str_equal, (GDestroyNotify) g_free, NULL); + + for (i = 0; i < priv->result->ncols; i++) + { + // Set fields editable if *all* primary keys are selected FIXME + if (priv->column[i].info & DB_COLUMN_PRI_KEY + && !g_slist_find_custom (t, priv->column[i].table, (GCompareFunc) g_strcmp0)) + for (j = 0; j < priv->result->ncols; j++) + if (!g_strcmp0 (priv->column[i].table, priv->column[j].table)) + { + gvn_param_spec_set_editable (priv->column[j].spec, TRUE); + t = g_slist_prepend (t, priv->column[j].table); + } + + if (priv->fresh && priv->column_index) + g_hash_table_insert (priv->column_index, + g_strdup (priv->column[i].display), GINT_TO_POINTER (i)); + } + + g_slist_free (t); + + db_model_calculate_update_flags (obj); + + if (!priv->fresh) + db_model_set_sort_column_id (obj, + priv->old_sort_column_id, priv->old_order); + else + db_model_post_process_query (obj); + + db_model_set_status (obj, DB_MODEL_STATUS_READY); + } + } + else + db_model_set_status (obj, DB_MODEL_STATUS_ERROR); + + g_clear_object (&priv->request); +} + +static void db_model_on_join_query_done (DbRequest * request, JoinData * join_data) +{ + gint i, j; + DbResult * r; + DbIter * iter = join_data->iter; + GError * err = NULL; + DbModel * obj = join_data->obj; + DbModelPrivate * priv = obj->priv; + + while ((r = db_request_fetch_result (request, &err))) + { + for (i = 0; i < priv->result->ncols; i++) + for (j = 0; j < r->ncols; j++) + if (!g_strcmp0 (r->column[j].table, priv->column[i].table) + && !g_strcmp0 (r->column[j].name, priv->column[i].name)) + { + if (r->nrows > 0) + { + GValue * new_value = + DB_ROW_FIELD (g_ptr_array_index (r->data, 0), j); + + priv->updated_value = g_new0 (GValue, 1); + g_value_init (priv->updated_value, G_VALUE_TYPE (new_value)); + gvn_value_copy (new_value, priv->updated_value); + } + else + priv->updated_value = + g_value_init (g_new0 (GValue, 1), GVN_TYPE_NULL); + + priv->updated_col = i; + g_signal_emit (obj, db_model_signal[LINE_UPDATED], 0, iter); + } + + db_result_free (r); + } + + if (err) + { + g_message ("%s", err->message); + g_error_free (err); + } + + priv->pending_request = + g_slist_remove (obj->priv->pending_request, request); + g_object_unref (request); +} + +static void db_model_on_stmt_changed (SqlStmt * stmt, DbModel * obj) +{ + if (obj->priv->conn && stmt) + { + if (sql_object_is_ready (SQL_OBJECT (stmt))) + db_model_refresh (obj); + else + { + db_model_clear (obj); + db_model_set_status (obj, DB_MODEL_STATUS_CLEAN); + } + } +} + +static void db_model_on_operations_done (DbRequest * request, DbModelRequest * data) +{ + DbOperation * op; + GError * err = NULL; + DbModel * obj = data->obj; + DbModelPrivate * priv = obj->priv; + + obj->priv->pending_request = + g_slist_remove (priv->pending_request, request); + + while (db_request_fetch_non_select (request, &err) != -1) + { + gint i, j; + DbResult * result; + DbRow * req_row, * row; + + op = g_queue_pop_head (data->operations); + row = op->row; + + g_hash_table_remove (priv->row_ops, op->row); + + if (op->type & DB_MODEL_ROW_OP_DELETE) // DELETE + { + g_signal_emit + (obj, db_model_signal[LINE_DELETED], 0, DB_ROW_POSITION (row)); + } + else if (op->type & DB_MODEL_ROW_OP_INSERT) // INSERT + { // Catch the SELECT associated with each INSERT + + result = db_request_fetch_result (request, &err); + + if (result) + { + if (result->data && result->nrows > 0) + { + req_row = g_ptr_array_index (result->data, 0); + + for (i = 0; i < result->ncols; i++) + for (j = 0; j < priv->result->ncols; j++) + if (!g_strcmp0 (priv->column[j].name, result->column[i].name) + && !g_strcmp0 (priv->column[j].table, priv->main_table)) + { + DbIter iter; + db_model_finish_insert (obj, req_row, i, row, j, &iter); + } + } + + db_result_free (result); + } + } + else if (op->type & DB_MODEL_ROW_OP_UPDATE)// UPDATE + if (priv->join) + { + GSList * n; + DbUpdatedField * u; + DbIter iter; + iter.stamp = priv->stamp; + iter.data = row; + + for (n = op->updated; n; n = n->next) + { + u = n->data; + +/* if (r->next && r->next->data + && ((DbResult *) r->next->data)->column)*/ + if (priv->column_default + && gvn_value_is_null (u->value) + && g_strcmp0 (priv->column[u->column].table, priv->main_table) + && db_model_table_row_all_null (obj, row, u->column)) + { // INSERT + SELECT "update" + + result = db_request_fetch_result (request, &err); + + if (result) + { + if (result->data && result->nrows > 0) + { + req_row = g_ptr_array_index (result->data, 0); + + for (i = 0; i < result->ncols; i++) + for (j = 0; j < priv->result->ncols; j++) + if (!g_strcmp0 (priv->column[j].name, result->column[i].name) + && (!g_strcmp0 (priv->column[j].table, result->column[i].table) + || !result->column[i].table)) + db_model_finish_insert (obj, req_row, i, row, j, &iter); + } + + db_result_free (result); + } + } +/* + if (n->next && (u = ((DbUpdatedField *) n->next->data)) + && priv->column_default + && gvn_value_is_null (u->value) + && g_strcmp0 (priv->column[u->column].table, priv->main_table) + && db_model_table_row_all_null (obj, row, u->column)) + r = r->next; +*/ + } + } + + db_model_free_operation (obj, op); + } + + //XXX Iterate both the result list and the queue at the same time + // when the Request has its Results set on errors(future?). + // Currently, if something fails in the plugin's code, it returns NULL + + if (err) + { + while ((op = g_queue_pop_head (data->operations))) + { + op->locked = FALSE; + g_queue_push_tail (priv->operation, op); + } + } + else + g_signal_emit (obj, db_model_signal[OPERATIONS_DONE], 0); + + g_object_unref (request); +} + +// Private helper methods and functions + +static void join_data_free (JoinData * join_data) +{ + g_object_unref (join_data->obj); + db_iter_free (join_data->iter); + g_free (join_data); +} + +static void db_updated_field_free (DbUpdatedField * u) +{ + if (u && u->value) + { + g_value_unset (u->value); + g_free (u->value); + } + + g_free (u); + u = NULL; +} + +static void db_model_free_operation (DbModel * obj, DbOperation * op) +{ + if (op->updated) + g_slist_free_full (op->updated, (GDestroyNotify) db_updated_field_free); + + g_hash_table_remove (obj->priv->row_ops, op->row); + + g_free (op); +} + +static void db_model_clean_operations (DbModel * obj) +{ + DbOperation * op; + DbModelPrivate * priv = obj->priv; + + while ((op = g_queue_pop_head (priv->operation))) + db_model_free_operation (obj, op); +} + +static void db_model_request_free (DbModelRequest * req) +{ + if (req) + { + g_queue_free (req->operations); + g_object_unref (req->obj); + } + + g_free (req); +} + +static void db_operation_add_updated (DbOperation * op, gint col) +{ + GSList * n; + DbUpdatedField * u; + + for (n = op->updated; n; n = n->next) + if (((DbUpdatedField *) n->data)->column == col) + return; + + u = g_new (DbUpdatedField, 1); + gvn_value_copy (DB_ROW_FIELD (op->row, col) + ,g_value_init (u->value = g_new0 (GValue, 1) + ,G_VALUE_TYPE (DB_ROW_FIELD (op->row, col)))); + u->column = col; + op->type |= DB_MODEL_ROW_OP_UPDATE; + op->updated = g_slist_prepend (op->updated, u); + + return; +} + +static gboolean db_model_set_row_operation (DbModel * obj, + DbRow * row, DbModelRowOp type, gint col) +{ + DbOperation * op = g_hash_table_lookup (obj->priv->row_ops, row); + + if (!op) + { + DbOperation * new_op = g_new (DbOperation, 1); + new_op->locked = FALSE; + new_op->row = row; + new_op->updated = NULL; + new_op->type = type; + + if (type & DB_MODEL_ROW_OP_UPDATE) + db_operation_add_updated (new_op, col); +/* if (!db_operation_add_updated (new_op, col)) + { + g_free (new_op); + return FALSE; + } +*/ + g_hash_table_insert (obj->priv->row_ops, row, new_op); + g_queue_push_tail (obj->priv->operation, new_op); + } + else if (!op->locked) + { + if (type & DB_MODEL_ROW_OP_DELETE) + op->type ^= DB_MODEL_ROW_OP_DELETE; + + if (type & DB_MODEL_ROW_OP_UPDATE) + db_operation_add_updated (op, col); +// if (!db_operation_add_updated (op, col)) +// return FALSE; + } + else + return FALSE; + + return TRUE; +} + +void db_model_reverse_operations (DbModel * obj) +{ + DbModelPrivate * priv = obj->priv; + DbOperation * op; + + g_return_if_fail (DB_IS_MODEL (obj)); + + while ((op = g_queue_pop_tail (priv->operation))) + { + g_hash_table_remove (priv->row_ops, op->row); + + if (op->type & DB_MODEL_ROW_OP_DELETE) + { + if (op->type & DB_MODEL_ROW_OP_INSERT) + { + g_signal_emit (obj, + db_model_signal[LINE_DELETED], 0, DB_ROW_POSITION (op->row)); + } + else + { + DbIter iter; + iter.data = op->row; + iter.stamp = priv->stamp; + + g_signal_emit (obj, db_model_signal[LINE_TOGGLED], 0, &iter); + } + } + else if (op->type & DB_MODEL_ROW_OP_INSERT) + { + g_signal_emit (obj, + db_model_signal[LINE_DELETED], 0, DB_ROW_POSITION (op->row)); + } + else if (op->type & DB_MODEL_ROW_OP_UPDATE) + { + GSList * n; + DbUpdatedField * u; + + for (n = op->updated; n; n = n->next) + { + DbIter iter; + iter.stamp = priv->stamp; + iter.data = op->row; + u = n->data; + + priv->updated_value = g_new0 (GValue, 1); + g_value_init (priv->updated_value, G_VALUE_TYPE (u->value)); + gvn_value_copy (u->value, priv->updated_value); + priv->updated_col = u->column; + + g_signal_emit (obj, db_model_signal[LINE_UPDATED], 0, &iter); + } + } + + db_model_free_operation (obj, op); + } +} + +static DbModelField * db_model_field_new (const gchar * table, const gchar * schema) +{ + DbModelField * field = g_new (DbModelField, 1); + field->schema = g_strdup (schema); + field->table = g_strdup (table); + field->name = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); + field->main = FALSE; + return field; +} + +static DbModelField * db_model_field_new_from_string (const gchar * str) +{ + DbModelField * field = NULL; + gchar * new_str = g_strdup (str), ** aux, ** f; + gint i, f_len; + g_strstrip (new_str); + f = g_strsplit (new_str, ".", G_MAXINT); + g_free (new_str); + f_len = g_strv_length (f); + + for (i = 0; i < f_len; i++) + { + aux = g_strsplit (f[i], "\"", G_MAXINT); + + if (g_strcmp0 (aux[0], "")) + { + g_strfreev (aux); + break; + } + + g_free (f[i]); + f[i] = g_strdup (aux[1]); + g_strfreev (aux); + } + + switch (f_len) + { + case 3: + { + field = db_model_field_new (f[1], f[0]); + g_ptr_array_add (field->name, g_strdup (f[2])); + break; + } + case 2: + { + field = db_model_field_new (f[0], NULL); + g_ptr_array_add (field->name, g_strdup (f[1])); + break; + } + case 1: + { + field = db_model_field_new (NULL, NULL); + g_ptr_array_add (field->name, g_strdup (f[0])); + } + } + + g_strfreev (f); + + return field; +} + +static void db_model_field_free (DbModelField * field) +{ + if (field) + { + if (field->schema) + g_free (field->schema); + if (field->table) + g_free (field->table); + if (field->name) + g_ptr_array_free (field->name, TRUE); + } + + g_free (field); +} + +static void db_join_free (DbJoin * join) +{ + if (join) + { + if (join->left) + db_model_field_free (join->left); + if (join->right) + db_model_field_free (join->right); + } + + g_free (join); +} + +static void db_model_calculate_col_def (DbModel * obj, SqlJoin * join, + SqlField * l_field, SqlField * r_field) +{ + gint i, col = -1; + gchar * dst = NULL; + DbModelPrivate * priv = obj->priv; + SqlField * f = NULL; + SqlTarget * l_table = join->target_left, * r_table = join->target_right; + + for (i = 0; i < priv->result->ncols; i++) + { + f = NULL; + + if (!g_strcmp0 (priv->column[i].name, l_field->name)) + { + f = l_field; + dst = (join->type == SQL_JOIN_TYPE_RIGHT) ? + l_field->name : r_field->name; + } + else if (!g_strcmp0 (priv->column[i].name, r_field->name)) + { + f = r_field; + dst = (join->type == SQL_JOIN_TYPE_LEFT) ? + l_field->name : r_field->name; + } + + if (f) + {//TODO add schema checks + if (f->target) + { + if (!g_strcmp0 (priv->column[i].table, f->target) + || (!g_strcmp0 (priv->column[i].table, SQL_TABLE (l_table)->name) + && !g_strcmp0 (f->target, l_table->alias))) + { + col = i; + break; + } + else if (!g_strcmp0 (priv->column[i].table, SQL_TABLE (r_table)->name) + && !g_strcmp0 (f->target, r_table->alias)) + { + col = i; + break; + } + } + else + { + col = i; + break; + } + } + } + + if (f) + db_model_set_default_value_from_column (obj, dst, col); +} + +static void db_model_set_join_fields (DbModel * obj, SqlJoin * join, + SqlField * lsql_field, SqlField * rsql_field, + DbModelField * lfield, DbModelField * rfield) +{ + gboolean check; + SqlTarget * ltarget = join->target_left, * rtarget = join->target_right; + + check = !g_strcmp0 (lfield->schema, lsql_field->schema) + || !g_strcmp0 (lfield->table, lsql_field->target) + || !g_strcmp0 (ltarget->alias, lsql_field->target) + || !g_strcmp0 (rfield->schema, rfield->schema) + || !g_strcmp0 (rfield->table, rsql_field->target) + || !g_strcmp0 (rtarget->alias, rsql_field->target); + + g_ptr_array_add (lfield->name, + g_strdup (check ? lsql_field->name : rsql_field->name)); + g_ptr_array_add (rfield->name, + g_strdup (check ? rsql_field->name : lsql_field->name)); +} + +static void db_model_post_process_query (DbModel * obj) +{ +// TODO When parser gets fully functional, these 3 lines won't be needed, +// because obj->stmt will be a parsed stmt: + gchar * rend = db_conn_render (obj->priv->conn, obj->priv->stmt, NULL); + SqlObject * stmt = sql_parser_parse (rend); +/*gchar * rend2 = NULL; +if (stmt && (rend2 = db_conn_render (obj->priv->conn, stmt, NULL))) +g_message ("SQL + Parser + Render =\n %s", rend2); +g_free (rend2);*/ + g_free (rend); + + if (stmt && SQL_IS_SELECT (stmt)) + { + DbModelField * lfield, * rfield; + SqlJoin * join; + GList * n; + SqlSelect * select = SQL_SELECT (stmt); + gboolean calculate_join = FALSE; + + for (n = sql_list_get_items (SQL_DML (select)->targets); n; n = n->next) + if ((join = n->data) + && SQL_IS_JOIN (join) + && SQL_IS_TABLE (join->target_left) + && SQL_IS_TABLE (join->target_right) + && SQL_IS_OPERATION (join->condition)) + { +// DbJoin and ColDef creation + GList * operators; + SqlOperation * op = SQL_OPERATION (join->condition); + SqlField * lsql_field = NULL, * rsql_field = NULL; + + lfield = db_model_field_new (SQL_TABLE (join->target_left)->name, NULL); + rfield = db_model_field_new (SQL_TABLE (join->target_right)->name, NULL); + + if (join->type == SQL_JOIN_TYPE_RIGHT) + rfield->main = TRUE; + else + lfield->main = TRUE; + + if (op->type == SQL_OPERATION_TYPE_AND) + { + GList * l; + + for (l = sql_list_get_items (op->operators); l; l = l->next) + { + SqlOperation * subop; + operators = sql_list_get_items (subop->operators); + + if (SQL_IS_OPERATION (subop = l->data) + && subop->type == SQL_OPERATION_TYPE_EQUAL + && operators->data // Left Field + && operators->next && operators->next->data) // Right Field + { + lsql_field = SQL_FIELD (operators->data); + rsql_field = SQL_FIELD (operators->next->data); + + db_model_set_join_fields (obj, join, + lsql_field, rsql_field, lfield, rfield); + + calculate_join = TRUE; + + if (join->type != SQL_JOIN_TYPE_INNER) + db_model_calculate_col_def + (obj, join, lsql_field, rsql_field); + } + else + { + calculate_join = FALSE; + break; + } + } + } + else + { + operators = sql_list_get_items (op->operators); + + if (op->type == SQL_OPERATION_TYPE_EQUAL && operators->data + && operators->next && operators->next->data) + { + lsql_field = SQL_FIELD (operators->data); + rsql_field = SQL_FIELD (operators->next->data); + + db_model_set_join_fields (obj, join, + lsql_field, rsql_field, lfield, rfield); + + calculate_join = TRUE; + + if (join->type != SQL_JOIN_TYPE_INNER) + db_model_calculate_col_def + (obj, join ,lsql_field, rsql_field); + } + } + + if (calculate_join) + { + DbJoin * join_res = g_new (DbJoin, 1); + join_res->left = lfield; + join_res->right = rfield; + + obj->priv->join = g_slist_prepend (obj->priv->join, join_res); + } + else + { + db_model_field_free (lfield); + db_model_field_free (rfield); + } + } + } + + if (G_IS_OBJECT (stmt)) + g_object_unref (stmt); +} + +static inline gboolean stored (const gint * v, const gint length, const gint target) +{ + gint i; + + for (i = 0; i < length; i++) + if (v[i] == target) + return TRUE; + return FALSE; +} + +static DbModelPKey * db_model_get_primary_key (DbModel *obj, gchar * table) +{ + gint i, j; + DbModelPKey * pkey = g_new (DbModelPKey, 1); + pkey->count = 0; +// Get number of pkeys + for (i = 0; i < obj->priv->result->ncols; i++) + if ((obj->priv->column[i].info & DB_COLUMN_PRI_KEY) + && !g_strcmp0 (obj->priv->column[i].table, table)) + pkey->count++; + + pkey->index = g_new (gint, pkey->count); +// Get pkey/s + for (i = 0; i < pkey->count; i++) + for (j = 0; j < obj->priv->result->ncols; j++) + if (!g_strcmp0 (obj->priv->column[j].table, table) + && (obj->priv->column[j].info & DB_COLUMN_PRI_KEY) + && !stored (pkey->index, i+1, j)) + { + pkey->index[i] = j; + break; + } + + return pkey; +} + +static void db_model_pkey_free (DbModelPKey * pkey) +{ + g_free (pkey->index); + g_free (pkey); +} + + +/* + * Comparison between values, using case-insensitive and UTF-8 strings + */ +static gint db_model_value_compare0 (const GValue * a, const GValue * b) +{ + GType a_type = G_VALUE_TYPE (a); + gboolean a_is_val = G_IS_VALUE (a); + gboolean b_is_val = G_IS_VALUE (b); + + if (!(a_is_val && b_is_val)) + { + if (a_is_val) + return 1; + if (b_is_val) + return -1; + } + else if (a_type == G_VALUE_TYPE (b)) + { + switch (a_type) + { + case G_TYPE_FLOAT: + { + gfloat aux = g_value_get_float (a) - g_value_get_float (b); + return (aux > 0.0) ? 1 : (aux < 0.0) ? -1 : 0; + } + case G_TYPE_DOUBLE: + { + gdouble aux = g_value_get_double (a) - g_value_get_double (b); + return (aux > 0.0) ? 1 : (aux < 0.0) ? -1 : 0; + } + case G_TYPE_INT: + return g_value_get_int (a) - g_value_get_int (b); + case G_TYPE_UINT: + return (gint) (g_value_get_uint (a) - g_value_get_uint (b)); + case G_TYPE_LONG: + return (gint) (g_value_get_long (a) - g_value_get_long (b)); + case G_TYPE_ULONG: + return (gint) (g_value_get_ulong (a) - g_value_get_ulong (b)); + case G_TYPE_BOOLEAN: + return (gint) (g_value_get_boolean (a) - g_value_get_boolean (b)); + case G_TYPE_CHAR: + return (gint) (g_value_get_schar (a) - g_value_get_schar (b)); + case G_TYPE_STRING: + { + gchar * a_str = g_utf8_casefold (g_value_get_string (a), -1); + gchar * b_str = g_utf8_casefold (g_value_get_string (b), -1); + return g_utf8_collate (a_str, b_str); + } + default: + if (a_type == G_TYPE_DATE) + return g_date_compare (g_value_get_boxed (a), g_value_get_boxed (b)); + if (a_type == G_TYPE_DATE_TIME) + return g_date_time_compare (g_value_get_boxed (a), g_value_get_boxed (b)); + else if (a_type == G_TYPE_BYTES) + return (gint) (g_value_get_boxed (a) - g_value_get_boxed (b)); + else if (a_type == GVN_TYPE_NULL) + return 0; + else + g_warning ("Attempting to compare invalid types: %s\n", + g_type_name (a_type)); + } + } + else if (gvn_value_is_null (a)) + return -1; + else if (gvn_value_is_null (b)) + return 1; + + return 1; +} + +static gint db_model_valcmp_asc (gpointer * a, gpointer * b, gpointer col) +{ + DbRow * first = *a; + DbRow * second = *b; + return db_model_value_compare0 (&first->value[GPOINTER_TO_INT (col)] + ,&second->value[GPOINTER_TO_INT (col)]); +} + +static gint db_model_valcmp_desc (gpointer * a, gpointer * b, gpointer col) +{ + DbRow * first = *a; + DbRow * second = *b; + return -db_model_value_compare0 (&(first->value)[GPOINTER_TO_INT (col)] + ,&(second->value)[GPOINTER_TO_INT (col)]); + +} + +static void db_model_set_status (DbModel * obj, DbModelStatus status) +{ + obj->priv->status = status; + g_signal_emit (obj, db_model_signal[STATUS_CHANGED], 0, status); +} + +static void db_model_cancel_pending_requests (DbModel * obj) +{ + GSList * n; + + for (n = obj->priv->pending_request; n; n = n->next) + db_request_cancel (n->data); +} + +static void db_model_add_pending_request (DbModel * obj, DbRequest * request) +{ + obj->priv->pending_request = g_slist_prepend (obj->priv->pending_request, + request); +} + +static void db_model_manage_join (DbModel * obj, DbIter * iter, gint col) +{ + DbModelPrivate * priv = obj->priv; + + if (priv->join + && gvn_param_spec_get_editable (priv->column[col].spec) + && !gvn_value_is_null (DB_ROW_FIELD (iter->data, col))) + { + gint i; + GSList * n; + gboolean send_request = FALSE, end = FALSE; + SqlList * stmts = g_object_ref_sink (sql_list_new (SQL_TYPE_MULTI_STMT)); + +/*FIXME + for (i = 0; i < obj->ncols; i++) + // Check for multi-field pkeys to be fully set + // need to know the total number of pkey fields + if (i != col && obj->column[i].info & DB_COLUMN_PRI_KEY + && gvn_value_is_null (DB_ROW_FIELD (iter->data, i)) + && !g_strcmp0 (obj->column[i].table, obj->column[col].table)) + { + end = TRUE; + break; + } +*/ + if (!end) + for (n = priv->join; n; n = n->next) + { + gint j; + SqlObject * where, * select; + DbModelField * main_field = NULL, * other_field = NULL; + DbJoin * join = n->data; + + if (join->left->main) + { + main_field = join->left; + other_field = join->right; + } + else if (join->right->main) + { + main_field = join->right; + other_field = join->left; + } + + for (i = 0; i < main_field->name->len; i++) + if (!g_strcmp0 (priv->column[col].table, main_field->table) + && !g_strcmp0 (priv->column[col].name, + g_ptr_array_index (main_field->name, i))) + { + send_request = TRUE; + break; + } + + if (!send_request) + continue; // Continue to the next DbJoin in the list + + select = sql_select_new (); + + sql_object_add_child (select, "targets", sql_table_new (other_field->table)); + + where = sql_operation_new (SQL_OPERATION_TYPE_AND); + + for (i = 0; i < priv->result->ncols; i++) + if (!g_strcmp0 (priv->column[i].table, other_field->table)) + { + sql_object_add_child (select, "exprs", + sql_field_new (priv->column[i].name, other_field->table, NULL)); + } + else if (!g_strcmp0 (priv->column[i].table, main_field->table)) + { + for (j = 0; j < main_field->name->len; j++) + if (!g_strcmp0 (priv->column[i].name, + g_ptr_array_index (main_field->name, j))) + { + SqlObject * equal = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + + sql_object_add_child (equal, "operators", + sql_field_new (g_ptr_array_index (other_field->name, j) + ,other_field->table, NULL)); + + sql_object_add_child (equal, "operators", + sql_value_new_with_value (DB_ROW_FIELD (iter->data, i))); + + sql_object_add_child (where, "operators", equal); + } + } + + sql_object_set (select, "where", where); + sql_list_add (stmts, select); + } + + if (send_request) + { + JoinData * join_data; + DbRequest * request; + + join_data = g_new (JoinData, 1); + join_data->obj = g_object_ref (obj); + join_data->iter = db_iter_copy (iter); + join_data->col = col; + + request = db_conn_query_with_stmt_async (priv->conn + ,g_object_new (SQL_TYPE_MULTI_STMT, "stmts", stmts, NULL) + ,(DbRequestDoneCallback) db_model_on_join_query_done + ,join_data + ,(GDestroyNotify) join_data_free + ); + db_model_add_pending_request (obj, request); + } + + g_object_unref (stmts); + } +} + +static void db_model_finish_insert (DbModel * obj, + DbRow * req_row, gint i, DbRow * row, gint j, DbIter * iter) +{ + GValue * v; + gboolean emit = TRUE; + iter->stamp = obj->priv->stamp; + iter->data = row; + + obj->priv->updated_value = g_new0 (GValue, 1); + + if ((v = &req_row->value[i]) && G_IS_VALUE (v) + && !gvn_value_is_null (DB_ROW_FIELD (req_row, i)) + && gvn_value_is_null (DB_ROW_FIELD (row, j))) + { + g_value_init (obj->priv->updated_value, G_VALUE_TYPE (v)); + gvn_value_copy (v, obj->priv->updated_value); + } + else if (gvn_value_is_null (DB_ROW_FIELD (row, j))) + { + g_value_init (obj->priv->updated_value, GVN_TYPE_NULL); + } + else + { + emit = FALSE; + g_free (obj->priv->updated_value); + } + + if (emit) + { + obj->priv->updated_col = j; + g_signal_emit (obj, db_model_signal[LINE_UPDATED], 0, iter); + } +} + +static void db_model_clear (DbModel * obj) +{ + DbModelPrivate * priv = obj->priv; + + if (priv->request) + { + db_request_cancel (priv->request); + priv->request = NULL; + } + else if (priv->result) + { + db_model_clean_operations (obj); + db_model_cancel_pending_requests (obj); + + db_result_free (priv->result); + priv->result = NULL; + priv->column = NULL; + priv->data = NULL; + + g_free (priv->main_table); + priv->main_table = NULL; + priv->update_flags = 0; + + priv->fresh = FALSE; + priv->old_order = priv->order; + priv->old_sort_column_id = priv->sort_column_id; + priv->sort_column_id = DB_MODEL_UNSORTED_SORT_COLUMN_ID; + priv->stamp = g_random_int (); + } +} + +static gboolean db_model_table_row_all_null (DbModel * obj, DbRow * row, gint col) +{ + DbUpdatedField * u; + GSList * n; + gboolean updated; + gint i; + + for (i = 0; i < row->len; i++) + { + updated = FALSE; + + if (!g_strcmp0 (obj->priv->column[col].table, obj->priv->column[i].table) + && i != col) + { + DbOperation * operation = g_hash_table_lookup (obj->priv->row_ops, row); + + if (operation) + for (n = operation->updated; n; n = n->next) + if ((u = (DbUpdatedField *) n->data) && u->column == i) + { + updated = TRUE; + + if (!gvn_value_is_null (u->value)) + return FALSE; + } + + if (!updated && !gvn_value_is_null (DB_ROW_FIELD (row, i))) + return FALSE; + } + } + + return TRUE; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +// Set&Get methods + +/** + * db_model_set_conn: + * @obj: a #DbModel + * @conn: (allow-none): a #DbConn + * + * Sets the connection through which the communication is established with + * the database. + **/ +void db_model_set_conn (DbModel * obj, DbConn * conn) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (DB_IS_CONN (conn) || !conn); + + if (conn) + { + if (!obj->priv->conn) + { + obj->priv->conn = g_object_ref (conn); + db_model_on_stmt_changed (obj->priv->stmt, obj); + } + else + g_warning ("DbModel: The connection can only be set once"); + } +} + +/** + * db_model_get_conn: + * @obj: a #DbModel + * + * Returns the connection through which the communication is established with + * the database. + * + * Return value: (transfer none): the #DbConn of the model + **/ +DbConn * db_model_get_conn (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + + return obj->priv->conn; +} + +/** + * db_model_get_spec: + * @obj: a #DbModel + * @col: the number of a column of @obj + * + * Returns the #GvnParamSpec of a field in the position @col of @obj. + * + * Return value: (transfer none): the #GvnParamSpec of the column with number @col + **/ +const GvnParamSpec * db_model_get_spec (DbModel * obj, gint col) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + + if ((obj->priv->result && 0 <= col + && col < obj->priv->result->ncols) + && obj->priv->column) + return obj->priv->column[col].spec; + return NULL; +} + +/** + * db_model_get_column_name: + * @obj: a #DbModel + * @col: the number of a column of @obj + * + * Retrieves the name of a field in the position @col of @obj. + * + * Return value: the name of the column with number @col + **/ +const gchar * db_model_get_column_name (DbModel * obj, gint col) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + + if ((obj->priv->result + && 0 <= col + && col < obj->priv->result->ncols) + && obj->priv->column) + return obj->priv->column[col].display; + return NULL; +} + +/** + * db_model_get_column_index: + * @obj: a #DbModel + * @name: the name of a column of @obj + * + * Retrieves the position in the query of the column called @name. + * + * Return value: the index of the column with name @name or -1 if there isn't + * a column called @name or if @name is NULL + **/ +gint db_model_get_column_index (DbModel * obj, const gchar * name) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), -1); + + if (!name || !obj->priv->column_index) + return -1; + + return GPOINTER_TO_INT (g_hash_table_lookup (obj->priv->column_index, name)); +} + +/** + * db_model_get_status: + * @obj: a #DbModel + * + * Returns the current #DbModelStatus of @obj. + * + * Return value: the status of @obj + **/ +DbModelStatus db_model_get_status (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), -1); + return obj->priv->status; +} + +/** + * db_model_get_mode: + * @obj: a #DbModel + * + * Retrieves the current working mode of @obj. See #DbModelMode. + * + * Return value: the current mode + **/ +DbModelMode db_model_get_mode (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), -1); + return (obj->priv->mode); +} + +/** + * db_model_set_mode: + * @obj: a #DbModel + * @mode: a #DbModelMode to set the model on + * + * Sets the working mode of @obj to @mode. See #DbModelMode. + **/ +void db_model_set_mode (DbModel * obj, DbModelMode mode) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + obj->priv->mode = mode; +} + +/** + * db_model_toggle_mode: + * @obj: a #DbModel + * + * Toogles the working mode of @obj. See #DbModelMode to see the two possible + * modes. + **/ +void db_model_toggle_mode (DbModel * obj) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + obj->priv->mode = (obj->priv->mode == DB_MODEL_MODE_ON_DEMAND)? + DB_MODEL_MODE_ON_CHANGE: + DB_MODEL_MODE_ON_DEMAND; +} + +/** + * db_model_set_result_pos: + * @obj: a #DbModel + * @pos: position of the query + * + * Sets the position where the query that will fill @model with data will be + * placed in a multi-query statement. + **/ +void db_model_set_result_pos (DbModel * obj, guint pos) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + obj->priv->result_pos = pos; +} + +/** + * db_model_get_update_flags: + * @obj: a #DbModel + * + * Retrieves the update flags of @obj. See #DbModelUpdateFlags for the details + * of those flags. See also db_model_request_update_flags(). + * + * Return value: the #DbModelUpdateFlags of @obj + **/ +DbModelUpdateFlags db_model_get_update_flags (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + return obj->priv->update_flags; +} + +/** + * db_model_request_update_flags: + * @obj: a #DbModel + * @flags: the set of #DbModelUpdateFlags to be set to @obj + * + * Requests the update flags of @obj. See #DbModelUpdateFlags for the details + * of those flags. If @obj does not allow an operation by itself, it will + * log a warning when trying to set this operation. If the flag set is + * #DB_MODEL_UPDATE it may not log until trying to update a non-updatable + * field, see db_model_set_value(). See also db_model_get_update_flags() and + * db_model_unset_update_flags(). + **/ +void db_model_request_update_flags (DbModel * obj, DbModelUpdateFlags flags) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + obj->priv->user_update_flags = flags; + db_model_calculate_update_flags (obj); +} + +/** + * db_model_unset_update_flags: + * @obj: a #DbModel + * @flags: the set of #DbModelUpdateFlags to be unset in @obj + * + * Unsets the update flags of @obj. See #DbModelUpdateFlags for the details + * of those flags. See also db_model_get_update_flags() and + * db_model_request_update_flags(). + **/ +void db_model_unset_update_flags (DbModel * obj, DbModelUpdateFlags flags) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + obj->priv->user_update_flags &= ~flags; + db_model_calculate_update_flags (obj); +} + +/** + * db_model_get_last: + * @obj: a @DbModel + * @iter: (out): an unitialized #DbIter + * + * Points @iter to the last row of @obj. + * + * Return value: %FALSE if @obj is a valid #DbModel, %TRUE otherwise + **/ +gboolean db_model_get_last (DbModel * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + iter->data = g_ptr_array_index + (obj->priv->data, (guint) obj->priv->data->len - 1); + iter->stamp = obj->priv->stamp; + return TRUE; +} + +/** + * db_model_add_join_columns: + * @obj: a #DbModel + * @left: the field on the left of the join + * @right: the field on the right of the join + * + * Sets the binding betwen two joined fields. This will generate a SELECT query + * to the database to change the corresponding values in the model. Currently + * it's used by the internal parser. + **/ +void db_model_add_join_columns (DbModel * obj, const gchar * left, const gchar * right) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + DbJoin * join = g_new (DbJoin, 1); + join->left = db_model_field_new_from_string (left); + join->right = db_model_field_new_from_string (right); + + obj->priv->join = g_slist_prepend (obj->priv->join, join); +} + +/** + * db_model_set_default_value_from_column: + * @obj: a #DbModel + * @dst_field: the field to be set + * @src_column: field from wich the value is picked + * + * TODO: cambiar src_column a string y almacenarlo, cuando se vaya a usar, cambiarlo + por el entero correspondiente + **/ +void db_model_set_default_value_from_column (DbModel * obj, + const gchar * dst_field, gint src_column) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + if (!obj->priv->result || 0 > src_column || src_column >= obj->priv->result->ncols) + return; + + DbColDef * def = g_new (DbColDef, 1); + def->dst = g_strdup (dst_field); + def->col = src_column; + + obj->priv->column_default = g_slist_prepend (obj->priv->column_default, def); +} + +/** + * db_model_set_default_value_from_param: + * @obj: a #DbModel + * @dst_field: the field to be set + * @param: a #GvnParam + * @id: an identifier string for @param + * + * Get the default value for @dst_field from @param. + **/ +void db_model_set_default_value_from_param (DbModel * obj, + const gchar * dst_field, GvnParam * param, const gchar * id) +{ + SqlObject * op, * value; + + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (GVN_IS_PARAM (param)); + g_return_if_fail (dst_field); + + op = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + sql_object_add_child (op, "operators", sql_field_new (dst_field, NULL, NULL)); + + value = sql_value_new (); + sql_value_set_param (SQL_VALUE (value), param); + sql_object_add_child (op, "operators", value); + + sql_object_add_held_object (SQL_OBJECT (obj->priv->stmt), id, op); + + DbParamDef * def = g_new (DbParamDef, 1); + def->dst = g_strdup (dst_field); + def->param = g_object_ref_sink (param); + + obj->priv->param_default = g_slist_prepend (obj->priv->param_default, def); +} + +/** + * db_model_add_param: + * @obj: a #DbModel + * @id: an identifier string for @param + * @param: the #GvnParam to add + * + * Links the statement in the model to @param. + **/ +void db_model_add_param (DbModel * obj, const gchar * id, GvnParam * param) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (GVN_IS_PARAM (param)); + + sql_string_add_param (SQL_STRING (obj->priv->stmt), id, param); +} + +/** + * db_model_request_main_table: + * @obj: a #DbModel + * @table: the name of the new main table of @model + * + * Requests the main table of @model. The main table is the only table on a + * #DbModel whose rows can be deleted or inserted. + **/ +void db_model_request_main_table (DbModel * obj, const gchar * table) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + g_free (obj->priv->user_main_table); + obj->priv->user_main_table = g_strdup (table); + db_model_calculate_update_flags (obj); +} + +/** + * db_model_get_main_table: + * @obj: a #DbModel + * + * Returns the string with the name of the main table of @obj. The main table + * is the only table on a #DbModel whose rows can be deleted or inserted. By + * default the main table is set to the table the first column field belongs. + * To override this behaviour, use @db_model_request_main_table. + * + * Note that the requested main table may be different fron the actual main + * table used by the model + * + * Return value: the name of the main table of @obj + **/ +const gchar * db_model_get_main_table (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + return obj->priv->main_table; +} + +/** + * db_model_get_stmt: + * @obj: a #DbModel + * + * Returns the #SqlStmt which queries to the database about the data of @obj. + * + * Return value: (transfer none): the #SqlStmt property of @obj + **/ +const SqlStmt * db_model_get_stmt (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + + return obj->priv->stmt; +} + +/** + * db_model_set_stmt: + * @obj: a #DbModel + * @stmt: the #SqlStmt + * + * Sets the "stmt" property of the model. + **/ +void db_model_set_stmt (DbModel * obj, SqlStmt * stmt) +{ + if (!stmt) + return; + + g_return_if_fail (!obj->priv->stmt); + g_return_if_fail (SQL_IS_STRING (stmt) || SQL_IS_SELECT (stmt)); + + obj->priv->stmt = g_object_ref_sink (stmt); + g_signal_connect (stmt, "changed", G_CALLBACK (db_model_on_stmt_changed), obj); + db_model_on_stmt_changed (stmt, obj); +} + +/** + * db_model_set_sql: + * @obj: a #DbModel + * @sql: a value for the "sql property + * + * Sets the "sql" property to @sql. + **/ +void db_model_set_sql (DbModel * obj, const gchar * sql) +{ + SqlString * string; + + if (!sql) + return; + + g_return_if_fail (DB_IS_MODEL (obj)); + + g_free (obj->priv->sql); + obj->priv->sql = g_strdup (sql); + + if (obj->priv->use_file) + string = db_conn_create_stmt_from_file (obj->priv->conn, sql); + else + string = sql_string_new (sql); + + db_model_set_stmt (obj, SQL_STMT (string)); +} + +/** + * db_model_get: + * @obj: a #DbModel + * @iter: a #DbIter + * @...: (out callee-allocates): pairs of column number and value return + * locations, terminated by -1 + * + * Gets the values on the specified columns and sets the return locations + * pointing to these values. The column numbers must be integers, while the + * return locations must be of the same type of the value being returned. + * + * Returned values of type G_TYPE_OBJECT have to be unreferenced, values of + * type G_TYPE_STRING or G_TYPE_BOXED have to be freed. Other values are passed + * by value. + **/ +void db_model_get (DbModel * obj, DbIter * iter, ...) +{ + va_list va; + gint column; + GValue * val; + + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (obj->priv->result); + g_return_if_fail (iter != NULL); + + va_start (va, iter); + + while ((column = (va_arg (va, gint))) >= 0 && column < obj->priv->result->ncols) + { + val = DB_ROW_FIELD (iter->data, column); + + if (gvn_value_is_null (val)) + switch (gvn_param_spec_get_gtype (obj->priv->column[column].spec)) + { + case G_TYPE_CHAR: + *va_arg (va, gchar*) = 0; + break; + case G_TYPE_INT: + *va_arg (va, gint*) = 0; + break; + case G_TYPE_LONG: + *va_arg (va, glong*) = 0; + break; + case G_TYPE_FLOAT: + *va_arg (va, gfloat*) = 0; + break; + case G_TYPE_DOUBLE: + *va_arg (va, gdouble*) = 0; + break; + default: // Objects or chararrays. + *va_arg (va, gpointer*) = NULL; + } + else + gvn_value_get_valist (val, va); + } + + va_end (va); + g_return_if_fail (column == -1); +} + +/** + * db_model_set: + * @obj: a #DbModel + * @iter: a #DbIter + * @...: pairs of column number and value, terminated by -1 + * + * Sets the values on the specified columns. The column numbers must be + * integers, while the values must be of the same type of the value being set. + * + * The value will be referenced by the model if it is a G_TYPE_OBJECT, and it + * will be copied if it is a G_TYPE_STRING or G_TYPE_BOXED. + **/ +void db_model_set (DbModel * obj, DbIter * iter, ...) +{ + gint column; + gpointer content; + GValue val = {0}; + va_list va; + + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (obj->priv->result); + g_return_if_fail (iter != NULL); + + va_start (va, iter); + + while ((column = (va_arg (va, gint))) >= 0 && column < obj->priv->result->ncols) + { + content = va_arg (va, gpointer); + gvn_value_new_with_content (&val, + gvn_param_spec_get_gtype (obj->priv->column[column].spec), content); + gvn_value_copy (&val, DB_ROW_FIELD (iter->data, column)); + g_value_unset (&val); + } + + va_end (va); + g_return_if_fail (column == -1); +} + +/** + * db_model_get_value: + * @obj: a #DbModel + * @iter: a #DbIter pointing to a row of @obj + * @col: the number of the field to get the value + * @err: (out) (allow-none): a #GError or %NULL to ignore errors + * + * Gets a value from @obj pointed to by @iter and @col and puts it in @err. + * + * Return value: the value pointed to by @iter and @col or %NULL in case of error + **/ +const GValue * db_model_get_value (DbModel * obj, DbIter * iter, gint col, GError ** err) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + g_return_val_if_fail (obj->priv->data && obj->priv->result, NULL); + g_return_val_if_fail (VALID_ITER (iter, obj), NULL); + + if (MODEL_NOT_READY (obj)) + { + g_set_error (err + ,DB_MODEL_LOG_DOMAIN + ,DB_MODEL_ERROR_NOT_READY + ,"The model is not ready"); + return NULL; + } + + if (0 > col || col >= obj->priv->result->ncols) + { + g_set_error (err + ,DB_MODEL_LOG_DOMAIN + ,DB_MODEL_ERROR_OUT_OF_RANGE + ,"Column out of range"); + return NULL; + } + + return DB_ROW_FIELD (iter->data, col); +} + +/** + * db_model_set_value: + * @obj: a #DbModel + * @iter: a #DbIter pointing to the row to be set + * @col: the column of the field to be set + * @value: new value for the field pointed to by @iter and @col + * @err: (out) (allow-none): a #GError or %NULL to ignore errors + * + * Sets the value of a single field to @value on @obj as well as on the database. + * If the database update fails, the model is not updated and err is set + * if it's not %NULL. + * If @iter is pointing a new row (inserted by db_model_insert() but not yet + * commited with db_model_perform_operations()) db_model_set_value() will only set + * the value on the model. + * + * Return value: %TRUE on success, %FALSE otherwise + **/ +gboolean db_model_set_value (DbModel * obj, DbIter * iter, gint col, const GValue * value, GError ** err) +{ + DbModelPrivate * priv = obj->priv; + gboolean ret = FALSE; + + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + g_return_val_if_fail (VALID_ITER (iter, obj), FALSE); + + if (MODEL_NOT_READY (obj) || !priv->result) + { + g_set_error (err, DB_MODEL_LOG_DOMAIN + ,DB_MODEL_ERROR_NOT_READY, "Model not ready"); + } + else + { + g_return_val_if_fail (0 <= col && col < priv->result->ncols, FALSE); + + GValue new_value = {0}; + DbRow * row = iter->data; + DbOperation * operation = g_hash_table_lookup (priv->row_ops, row); + DbModelRowOp row_op = operation ? operation->type : 0; + GvnParamSpec * spec = priv->column[col].spec; + + if (!gvn_param_spec_validate (spec, value, err)) + { + return FALSE; + } + + if (!(priv->update_flags & DB_MODEL_UPDATE) + && !(row_op & DB_MODEL_ROW_OP_INSERT)) + { + g_set_error (err, DB_MODEL_LOG_DOMAIN + ,DB_MODEL_ERROR_NOT_UPDATABLE, "Model not updatable"); + return FALSE; + } + + if (!gvn_value_is_null (value)) + { + g_value_init (&new_value, gvn_param_spec_get_gtype (spec)); + g_value_transform (value, &new_value); + } + else + g_value_init (&new_value, GVN_TYPE_NULL); + + if (gvn_value_compare (&new_value, DB_ROW_FIELD (iter->data, col))) + { + ret = TRUE; + } + else if (!db_model_set_row_operation (obj, row, DB_MODEL_ROW_OP_UPDATE, col)) + { + g_set_error (err, DB_MODEL_LOG_DOMAIN + ,DB_MODEL_ERROR_NOT_UPDATABLE, "Row locked. " + "There are one or more operations being applied over this row."); + } + else + { + priv->updated_col = col; + priv->updated_value = g_new0 (GValue, 1); + gvn_value_copy (&new_value + ,g_value_init (priv->updated_value, G_VALUE_TYPE (&new_value))); + + g_signal_emit (obj, db_model_signal[LINE_UPDATED], 0, iter); + + if (priv->mode == DB_MODEL_MODE_ON_CHANGE + && !(row_op & DB_MODEL_ROW_OP_INSERT)) + db_model_perform_operations (obj, FALSE); + + ret = TRUE; + } + + g_value_unset (&new_value); + } + + return ret; +} + +/** + * db_model_insert: + * @obj: a #DbModel + * @iter: (out): a #DbIter that will point to the new row + * + * Inserts an empty row at the end of @obj. The values for this row must be + * set one by one using db_model_set_value() or all at once with db_model_set(). + * And then committed with db_model_perform_operations(). + **/ +gboolean db_model_insert (DbModel * obj, DbIter * iter) +{ + gint i; + DbRow * row; + DbModelPrivate * priv; + + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + priv = obj->priv; + + if (MODEL_NOT_READY (obj) || !priv->result) + { + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, "Model not ready"); + return FALSE; + } + + if (!(priv->update_flags & DB_MODEL_INSERT)) + { + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, "Can't insert into this Model"); + return FALSE; + } + + row = db_row_new (priv->result->ncols, priv->result->nrows); + + for (i = 0; i < row->len; i++) + { + GSList * p; + const GValue * def = NULL; + + for (p = priv->param_default; p; p = p->next) + { + DbParamDef * param_def = p->data; + + if (!g_strcmp0 (param_def->dst, priv->column[i].name)) + { + def = gvn_param_get_value (param_def->param); + g_value_init (&row->value[i], G_VALUE_TYPE (def)); + gvn_value_copy (def, &row->value[i]); + } + } + + if (!def) + g_value_init (&row->value[i], GVN_TYPE_NULL); + } + + g_ptr_array_add (priv->data, row); + iter->data = g_ptr_array_index (priv->data, (guint) priv->data->len - 1); + iter->stamp = priv->stamp; + priv->result->nrows++; + + db_model_set_row_operation (obj, iter->data, DB_MODEL_ROW_OP_INSERT, 0); + + g_signal_emit (obj, db_model_signal[LINE_INSERTED], 0, iter); + + return TRUE; +} + +/** + * db_model_delete: + * @obj: a #DbModel + * @iter: a #DbIter pointing to the row to be deleted + * + * Deletes the row pointed to by @iter on @obj, as well as on the database (if + * it was already there, i.e. it's not a new row). + * If the deletion on the database fails, then the row on @obj is not deleted + * and a log message is emitted. + **/ +void db_model_delete (DbModel * obj, DbIter * iter) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (VALID_ITER (iter, obj)); + + if (MODEL_NOT_READY (obj)) + { + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, "Model not ready"); + return; + } + + if (!(obj->priv->update_flags & DB_MODEL_DELETE)) + { + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, "Can't delete from this Model"); + return; + } + + if (!db_model_set_row_operation + (obj, g_ptr_array_index (obj->priv->data, DB_ROW_POSITION (iter->data)), + DB_MODEL_ROW_OP_DELETE, 0)) + return; + + g_signal_emit (obj, db_model_signal[LINE_TOGGLED], 0, iter); + + if (obj->priv->mode == DB_MODEL_MODE_ON_CHANGE) + db_model_perform_operations (obj, FALSE); +} + +/** + * db_model_order_by: + * @obj: a #DbModel + * @col: the number of the column that will be the sort criteria + * @order: the order to sort in + * + * Sorts @obj in the order indicated by @order and using the data + * on the field @col. + **/ +void db_model_order_by (DbModel * obj, gint col, DbSortType order) +{ + DbModelPrivate * priv; + g_return_if_fail (DB_IS_MODEL (obj)); + priv = obj->priv; + g_return_if_fail (priv->result && col >= -2 && col < priv->result->ncols); + g_return_if_fail (order == DB_SORT_ASCENDING || order == DB_SORT_DESCENDING); + + gint new_order[priv->result->nrows]; + gint old_col = priv->sort_column_id; + DbRow * row_iter; + gint r_ind, i = 0; + + if ((priv->order == order && priv->sort_column_id == col) + || !priv->data + || col == DB_MODEL_UNSORTED_SORT_COLUMN_ID) + return; + + if (col == DB_MODEL_DEFAULT_SORT_COLUMN_ID + && old_col != DB_MODEL_DEFAULT_SORT_COLUMN_ID) + col = old_col; + + priv->order = order; + priv->sort_column_id = col; + g_signal_emit (obj, db_model_signal[SORT_CHANGED], 0, NULL); + + if (order == DB_SORT_DESCENDING) + g_ptr_array_sort_with_data (priv->data + ,(GCompareDataFunc) db_model_valcmp_desc + ,GINT_TO_POINTER (col)); + else + g_ptr_array_sort_with_data (priv->data + ,(GCompareDataFunc) db_model_valcmp_asc + ,GINT_TO_POINTER (col)); + + for (r_ind = 0; r_ind < priv->result->nrows; r_ind++) + { + row_iter = g_ptr_array_index (priv->data, r_ind); + new_order[i] = DB_ROW_POSITION (row_iter); + DB_ROW_POSITION (row_iter) = i++; + } + + g_signal_emit (obj, db_model_signal[LINES_REORDERED], 0, col, new_order); +} + +/** + * db_model_search: + * @obj: a #DbModel + * @col: the field to search in + * @iter: a #DbIter that will point the first found element + * @content: the value looked for or %NULL + * + * Looks for a the passed value in the field specified by @col. + * + * Return value: Returns %TRUE if the value is found and %FALSE otherwise + **/ +gboolean db_model_search (DbModel * obj, gint col, DbIter * iter, gpointer content) +{ + gboolean ret; + GValue value = {0}; + + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + g_return_val_if_fail (obj->priv->result + && 0 <= obj->priv->result->ncols + && col < obj->priv->result->ncols , FALSE); + + gvn_value_new_with_content (&value, + gvn_param_spec_get_gtype (obj->priv->column[col].spec), content); + + ret = db_model_search_value (obj, col, iter, &value); + + g_value_unset (&value); + return ret; +} + +/** + * db_model_search_value: + * @obj: a #DbModel + * @col: the field to search in + * @iter: a #DbIter that will point the first found element + * @value: the value to search for + * + * Looks for a the value pointed to by @value in the field specified by @col. + * + * Return value: Returns %TRUE if the value is found and %FALSE otherwise + **/ +gboolean db_model_search_value + (DbModel * obj, gint col, DbIter * iter, const GValue * value) +{ + gint i; + GType type; + DbRow * row; + + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + g_return_val_if_fail (G_IS_VALUE (value), FALSE); + g_return_val_if_fail (obj->priv->result + && 0 <= col + && col < obj->priv->result->ncols, FALSE); + + type = gvn_param_spec_get_gtype (obj->priv->column[col].spec); + + if (gvn_value_is_null (value) || G_VALUE_TYPE (value) == type) + for (i = 0; i < obj->priv->result->nrows; i++) + { + row = g_ptr_array_index (obj->priv->data, i); + + if (gvn_value_compare (DB_ROW_FIELD (row, col), value)) + { + iter->stamp = obj->priv->stamp; + iter->data = row; + return TRUE; + } + } + + return FALSE; +} + +/** + * db_model_get_row_operations: + * @obj: a #DbModel + * @iter: a #DbIter + * + * Returns #DbModelRowOp flags indicating the operations being applied to the + * row pointed to by @iter. If no operations are being applied on the row, it + * returns 0. + * + * Return value: the #DbModelRowOp of the row or 0 + **/ +DbModelRowOp db_model_get_row_operations (DbModel * obj, DbIter * iter) +{ + DbOperation * op; + + g_return_val_if_fail (DB_IS_MODEL (obj), 0); + g_return_val_if_fail (VALID_ITER (iter, obj), 0); + + op = g_hash_table_lookup (obj->priv->row_ops, (DbRow *) iter->data); + return op ? op->type : 0; +} + +/** + * db_model_has_pending_operations: + * @obj: a #DbModel + * + * Returns whether there are pending operations in the model. + * + * Return value: #TRUE if the model has pending operations, #FALSE otherwise + **/ +gboolean db_model_has_pending_operations (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + return g_hash_table_size (obj->priv->row_ops) > 0; +} + +/** + * db_model_perform_operations: + * @obj: a #DbModel with a new row, not yet inserted + * @retry: whether to retry the failed operations (not implemented) + * + * Commits the changes made by db_model_set_value() to a new row, inserted + * by db_model_insert(). + * Note that if this method is not called after db_model_insert(), all the + * changes made on the new row will be lost. + * If the @obj is working in the #DB_MODEL_MODE_ON_DEMAND, non-interactive, + * mode, this method will perform every actions taken and not yet submitted. + **/ +void db_model_perform_operations (DbModel * obj, gboolean retry) +{ + DbModelPrivate * priv; + gboolean render_ops = FALSE, transaction = FALSE; + gint i; + DbRow * row; + DbOperation * op_elem; + GList * l = NULL; + GSList * sl = NULL; + SqlObject * multi = NULL, + * equal_op, * val = NULL, + * insert, * set, + * select, * where = NULL; + SqlList * stmts; + DbRequest * request; + GQueue * req_ops = NULL; + + g_return_if_fail (DB_IS_MODEL (obj)); + + if (MODEL_NOT_READY (obj)) + { + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN), G_LOG_LEVEL_WARNING, + "Model not ready"); + return; + } + + priv = obj->priv; + + if (!priv->operation->length) + return; + + if (priv->operation->length > 1) + transaction = TRUE; + + stmts = sql_list_new (SQL_TYPE_STMT); + + while ((op_elem = g_queue_pop_head (priv->operation))) + { + const GValue * def; + + op_elem->locked = TRUE; + row = op_elem->row; + + if (op_elem->type & DB_MODEL_ROW_OP_DELETE) // DELETE + { + if (op_elem->type & DB_MODEL_ROW_OP_INSERT) + { + db_model_free_operation (obj, op_elem); + op_elem = NULL; + g_signal_emit (obj, + db_model_signal[LINE_DELETED], 0, DB_ROW_POSITION (row)); + + if (!transaction) + { + g_object_unref (stmts); + stmts = NULL; + } + } + else + { + SqlObject * delete = sql_delete_new (); + sql_object_add_child (delete, "targets", + sql_table_new (priv->main_table, NULL)); + + where = sql_operation_new (SQL_OPERATION_TYPE_AND); + + DbModelPKey * pkey = + db_model_get_primary_key (obj, priv->main_table); + + for (i = 0; i < pkey->count; i++) + { + equal_op = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + + sql_object_add_child (equal_op, "operators", + sql_field_new (priv->column[pkey->index[i]].name, NULL, NULL)); + sql_object_add_child (equal_op, "operators", + sql_value_new_with_value (&row->value[pkey->index[i]])); + + sql_object_add_child (where, "operators", equal_op); + } + + db_model_pkey_free (pkey); + + sql_object_set (delete, "where", where); + + sql_list_add (stmts, delete); + render_ops = TRUE; + } + } + else if (op_elem->type & DB_MODEL_ROW_OP_INSERT) // INSERT + { + SqlObject * target = sql_table_new (priv->main_table); + + insert = g_object_new (SQL_TYPE_INSERT, "table", target, NULL); + set = g_object_new (SQL_TYPE_SET, NULL); + sql_object_add_child (insert, "values", set); + + select = sql_select_new (); + sql_object_add_child (select, "targets", target); + where = sql_operation_new (SQL_OPERATION_TYPE_AND); + + for (i = 0; i < row->len; i++) + if (!g_strcmp0 (priv->column[i].table, priv->main_table)) + { + gboolean cont = FALSE; + SqlObject * field = NULL; + + l = sql_list_get_items (SQL_INSERT (insert)->fields); + + for (; l; l = l->next) + if (!g_strcmp0 (((SqlField*) l->data)->name, priv->column[i].name) + && ((!((SqlField*) l->data)->target) + || !g_strcmp0 (((SqlField*) l->data)->target, priv->column[i].table))) + { + cont = TRUE; + break; + } + + if (cont) + continue; + + val = sql_value_new_with_value (&row->value[i]); + + sql_object_add_child (insert, "fields", + sql_field_new (priv->column[i].name, NULL, NULL)); + sql_object_add_child (set, + "exprs", gvn_value_is_null (&row->value[i]) ? NULL : val); + + sql_object_add_child (select, "exprs", + sql_field_new (priv->column[i].name, + priv->column[i].table, NULL)); + + // Set the filter conditions + if (priv->column[i].info & DB_COLUMN_PRI_KEY) + { + gboolean unset = FALSE; + GValue value = {0}; + SqlObject * eq_value; + + equal_op = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + sql_object_add_child (equal_op, "operators", field); + + def = gvn_param_spec_get_default (priv->column[i].spec); + + if (!gvn_value_is_null (&row->value[i])) + { + eq_value = val; + } + else if (def && G_IS_VALUE (def)) + { + if (G_VALUE_TYPE (def) == SQL_TYPE_FUNCTION) + eq_value = g_value_get_object (def); + else + eq_value = sql_value_new_with_value (def); + } + else + { + gvn_value_set_null (&value); + eq_value = sql_value_new_with_value (&value); + unset = TRUE; + } + + sql_object_add_child (equal_op, "operators", eq_value); + + if (unset) + g_value_unset (&value); + + sql_object_add_child (where, "operators", equal_op); + } + } + + sql_object_set (select, "where", where); + + for (sl = priv->param_default; sl; sl = sl->next) + { + gboolean cont = FALSE; + DbParamDef * param_def = sl->data; + + l = sql_list_get_items (SQL_INSERT (insert)->fields); + + for (; l; l = l->next) + if (!g_strcmp0 (((SqlField*) l->data)->name, param_def->dst)) + { + cont = TRUE; + break; + } + + if (cont) continue; + + val = sql_value_new_with_value + (gvn_param_get_value (param_def->param)); + + sql_object_add_child (insert, "fields", + sql_field_new (param_def->dst, NULL, NULL)); + sql_object_add_child (set, "exprs", val); + } + + sql_list_add (stmts, insert); + sql_list_add (stmts, select); + render_ops = TRUE; + } + else if (op_elem->type & DB_MODEL_ROW_OP_UPDATE) // UPDATE + { + // Depending on the field, generate an UPDATE or INSERT+SELECT + GSList * m; + GValue * new_value; + SqlObject * update = NULL; + GSList * prev_tables = NULL; + GSList * prev_updates = NULL; + gboolean insert_set = FALSE; + select = NULL; + + for (m = op_elem->updated; m; m = m->next) + { + DbUpdatedField * u = m->data; + i = u->column; + + new_value = DB_ROW_FIELD (row, i); + + if (!priv->column_default + || !gvn_value_is_null (u->value) + || !g_strcmp0 (priv->column[i].table, priv->main_table) + || !db_model_table_row_all_null (obj, row, i)) + { + gboolean is_added = NULL != g_slist_find_custom (prev_tables, + priv->column[i].table, (GCompareFunc) g_strcmp0); + + if (!is_added) + { + where = sql_operation_new (SQL_OPERATION_TYPE_AND); + update = g_object_new (SQL_TYPE_UPDATE, + "where", where, NULL); + } + else + { + GSList * l; + SqlTable * t; + + for (l = prev_updates; l; l = l->next) + if (SQL_IS_UPDATE (l->data) + && SQL_IS_TABLE (t = sql_list_get_items (SQL_DML (l->data)->targets)->data) + && !g_strcmp0 (SQL_TABLE (t)->name, priv->column[i].table)) + { + update = l->data; + break; + } + } + + sql_object_add_child (update, "sets", + g_object_new (SQL_TYPE_UPDATE_SET, + "field", sql_field_new (priv->column[i].name, NULL, NULL), + "expr", sql_value_new_with_value (new_value), + NULL)); + + if (!is_added) + { + guint j; + DbModelPKey * pkey = + db_model_get_primary_key (obj, priv->column[i].table); + + sql_object_add_child (update, "targets", + sql_table_new (priv->column[i].table)); + + for (j = 0; j < pkey->count; j++) + { + gint key_col = pkey->index[j]; + GValue * primary = NULL; + GSList * n; + + if (key_col == i) + primary = u->value; + else + { + for (n = op_elem->updated; n; n = n->next) + if (((DbUpdatedField *)n->data)->column == key_col) + break; + + if (n) + primary = ((DbUpdatedField *) n->data)->value; + else + primary = DB_ROW_FIELD (row, key_col); + } + + equal_op = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + + sql_object_add_child (equal_op, "operators", + sql_field_new (priv->column[key_col].name, + priv->column[key_col].table, NULL)); + + val = sql_value_new_with_value (primary); + sql_object_add_child (equal_op, "operators", val); + + if (where) + sql_object_add_child (where, "operators", equal_op); + } + + sql_object_set (update, "where", where); + + sql_list_add (stmts, update); + db_model_pkey_free (pkey); + } + + prev_tables = g_slist_prepend (prev_tables, priv->column[i].table); + prev_updates = g_slist_prepend (prev_updates, update); + } + else + { + GSList * n; + + if (!insert) + { + insert = sql_insert_new (); + select = sql_select_new (); + } + + if (!SQL_INSERT (insert)->table) + { + SqlObject * table = sql_table_new (priv->column[i].table); + sql_object_set (insert, "table", table); + sql_object_add_child (select, "targets", table); + + set = g_object_new (SQL_TYPE_SET, NULL); + sql_object_add_child (insert, "values", set); + + insert_set = TRUE; + + for (n = priv->column_default; n; n = n->next) + { + gchar * dst = ((DbColDef *) n->data)->dst; + gint col_def = ((DbColDef *) n->data)->col; + + if (!g_strcmp0 (priv->column[i].table, + priv->column[col_def].table)) + continue; + + sql_object_add_child (insert, "fields", + sql_field_new (dst, NULL, NULL)); + + sql_object_add_child (set, "exprs" + ,sql_value_new_with_value (DB_ROW_FIELD (row, col_def))); + } + } + + if (insert_set) + { + val = sql_value_new_with_value (new_value); + + sql_object_add_child (insert, "fields", + sql_field_new (priv->column[i].name, NULL, NULL)); + sql_object_add_child (set, "exprs", val); + } + } + } + + g_slist_free (prev_tables); + g_slist_free (prev_updates); + + if (insert) + { + where = sql_operation_new (SQL_OPERATION_TYPE_AND); + + for (i = 0; i < row->len; i++) + if (!g_strcmp0 (SQL_TABLE (sql_list_get_items (SQL_DML (select)->targets)->data)->name + ,priv->column[i].table)) + { + SqlObject * field = SQL_OBJECT + (sql_field_new (priv->column[i].name, NULL, NULL)); + sql_object_add_child (select, "fields", field); + + if (priv->column[i].info & DB_COLUMN_PRI_KEY) + { + def = gvn_param_spec_get_default (priv->column[i].spec); + + if (!gvn_value_is_null (&row->value[i]) + || (def && G_VALUE_TYPE (def) == SQL_TYPE_FUNCTION)) + { + equal_op = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + + sql_object_add_child (equal_op, "operators", field); + + if (gvn_value_is_null (&row->value[i])) + { + if (def && G_VALUE_TYPE (def) == SQL_TYPE_FUNCTION) + sql_object_add_child (equal_op, "operators", + g_value_get_object (def)); + } + else + sql_object_add_child (equal_op, "operators", val); + + sql_object_add_child (where, "operators", equal_op); + } + } + } + + sql_object_set (select, "where", where); + + sql_list_add (stmts, insert); + sql_list_add (stmts, select); + } + + render_ops = TRUE; + } + + if (op_elem) + { + if (!req_ops) + req_ops = g_queue_new (); + + g_queue_push_tail (req_ops, op_elem); + } + } + + if (render_ops) + { + if (multi) + { + DbModelRequest * data = g_new (DbModelRequest, 1); + data->obj = g_object_ref (obj); + data->operations = req_ops; + multi = g_object_new (SQL_TYPE_MULTI_STMT, "stmts", stmts, NULL); + + request = db_conn_query_with_stmt_async (priv->conn + ,SQL_STMT (multi) + ,(DbRequestDoneCallback) db_model_on_operations_done + ,data + ,(GDestroyNotify) db_model_request_free + ); + db_model_add_pending_request (obj, request); + } + else + { + g_queue_free (req_ops); + db_model_clean_operations (obj); + } + } +} + +/** + * db_model_refresh: + * @obj: a #DbModel + * + * Executes the SELECT query and fills @obj with the data returned by it. + **/ +void db_model_refresh (DbModel * obj) +{ + DbModelPrivate * priv; + + g_return_if_fail (DB_IS_MODEL (obj)); + priv = obj->priv; + g_return_if_fail (priv->stmt); + + db_model_clear (obj); + db_model_set_status (obj, DB_MODEL_STATUS_LOADING); + + priv->request = db_conn_query_with_stmt_async (priv->conn + ,priv->stmt + ,(DbRequestDoneCallback) db_model_on_data_ready + ,g_object_ref (obj) + ,(GDestroyNotify) g_object_unref + ); +} + +/** + * db_model_get_nrows: + * @obj: a #DbModel + * + * Returns the current number of rows on #obj. + * + * Return value: the number of rows + **/ +gint db_model_get_nrows (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), 0); + + if (obj->priv->result) + return obj->priv->result->nrows; + else + return 0; +} + +/** + * db_model_iter_is_valid: + * @iter: a #DbIter + * @model: a #DbModel + * + * Checks if @iter is a valid #DbIter pointing inside @model. + **/ +gboolean db_model_iter_is_valid (DbIter * iter, DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + return (iter && iter->data && obj->priv->stamp == iter->stamp); +} + +// GtkTreeModel implementation methods. + +/** + * db_model_get_ncols: + * @obj: a #DbModel + * + * Returns the number of columns supported by @obj. + * + * Return value: the number of columns + **/ +gint db_model_get_ncols (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), -1); + + if (obj->priv->result) + return obj->priv->result->ncols; + else + return 0; +} + +/** + * db_model_get_column_type: + * @obj: a #DbModel + * @index: the number of the column + * + * Retrieves the type of the column specified by @index. + * + * Return value: the type of the column + **/ +GType db_model_get_column_type (DbModel * obj, gint index) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), G_TYPE_INVALID); + g_return_val_if_fail (obj->priv->result + && 0 <= index && index < obj->priv->result->ncols, G_TYPE_INVALID); + + if (obj->priv->column) + if (obj->priv->column[index].spec) + return gvn_param_spec_get_gtype (obj->priv->column[index].spec); + return G_TYPE_INVALID; +} + +/** + * db_model_get_path: + * @obj: a #DbModel + * @iter: a #DbIter + * + * Returns the number of the row pointed to by @iter. + * + * Return value: the number of the row pointed to by @iter + **/ +gint db_model_get_path (DbModel * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), 0); + if (MODEL_NOT_READY (obj)) return 0; + g_return_val_if_fail (VALID_ITER (iter, obj), 0); + + return DB_ROW_POSITION (iter->data); +} + +/** + * db_model_get_iter: + * @obj: a #DbModel + * @iter: (out): an unitialized #DbIter + * @path: the number of the row being accessed + * + * Sets @iter pointing to the row of @obj specified by @path. + * + * Return value: %TRUE if the position is found, %FALSE otherwise + **/ +gboolean db_model_get_iter (DbModel * obj, DbIter * iter, gint path) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + if ((0 > path + || (obj->priv->result && path >= obj->priv->result->nrows)) + || MODEL_NOT_READY (obj)) + return FALSE; + + iter->stamp = obj->priv->stamp; + iter->data = g_ptr_array_index (obj->priv->data, (guint) path); + + if (iter->data) + return TRUE; + return FALSE; +} + +/** + * db_model_get_iter_first: + * @obj: a #DbModel + * @iter: (out): an unitialized #DbIter + * + * Sets @iter pointing to the first row of @obj. + * + * Return value: %TRUE if @iter is set right + **/ +gboolean db_model_get_iter_first (DbModel * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + if (MODEL_NOT_READY(obj) + || !obj->priv->result || !obj->priv->result->nrows) + return FALSE; + + if (obj->priv->data) + { + iter->stamp = obj->priv->stamp; + iter->data = g_ptr_array_index (obj->priv->data, 0); + } + else + return FALSE; + + return TRUE; +} + +/** + * db_model_iter_prev: + * @obj: a #DbModel + * @iter: (inout): a valid #DbIter + * + * Sets @iter pointing to the previous row of @obj. + * + * Return value: %TRUE if the iter has been changed to the previous row + **/ +gboolean db_model_iter_prev (DbModel * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + if (MODEL_NOT_READY(obj) + || !obj->priv->result || !obj->priv->result->nrows) + return FALSE; + + g_return_val_if_fail (VALID_ITER (iter, obj), FALSE); + + if ((iter->data = g_ptr_array_index + (obj->priv->data, (guint) DB_ROW_POSITION (iter->data) - 1))) + return TRUE; + return FALSE; +} + +/** + * db_model_iter_next: + * @obj: a #DbModel + * @iter: (inout): a valid #DbIter + * + * Sets @iter pointing to the next row of @obj. + * + * Return value: %TRUE if the iter has been changed to the next row + **/ +gboolean db_model_iter_next (DbModel * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + if (MODEL_NOT_READY(obj) + || !obj->priv->result || !obj->priv->result->nrows) + return FALSE; + + g_return_val_if_fail (VALID_ITER (iter, obj), FALSE); + + gint pos = DB_ROW_POSITION (iter->data); + + if (pos < obj->priv->result->nrows-1) + { + iter->data = g_ptr_array_index (obj->priv->data, (guint) pos + 1); + return TRUE; + } + + iter->stamp = (iter->stamp)? 0 : 1; + iter->data = NULL; + return FALSE; +} + +// GtkTreeSortable implementation methods + +/** + * db_model_get_sort_column_id: + * @obj: a #DbModel + * @sort_column_id: (out): an integer + * @order: (out): a @DbSortType + * + * Fills in @sort_column_id and @order with the current sort + * column and the order. See #GtkTreeSortable. + * + * Return value: %TRUE if the sort column is not one of the special sort column + * ids + **/ +gboolean db_model_get_sort_column_id (DbModel * obj, gint * sort_column_id, + DbSortType * order) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + if (sort_column_id) + * sort_column_id = obj->priv->sort_column_id; + + if (order) + * order = obj->priv->order; + + if (obj->priv->sort_column_id == DB_MODEL_DEFAULT_SORT_COLUMN_ID + || obj->priv->sort_column_id == DB_MODEL_UNSORTED_SORT_COLUMN_ID) + return FALSE; + else + return TRUE; +} + +/** + * db_model_set_sort_column_id: + * @obj: a #DbModel + * @sort_column_id: the column to sort by + * @order: the order in which the sort will be done + * + * Sets @sort_column_id to be the current sort column. @obj will resort itself + * to reflect this change. See #GtkTreeSortable. + **/ +void db_model_set_sort_column_id (DbModel * obj, gint sort_column_id, + DbSortType order) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + db_model_order_by (obj, sort_column_id, order); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +typedef enum +{ + PROP_CONN = 1 + ,PROP_STMT + ,PROP_SQL + ,PROP_USE_FILE + ,PROP_MAIN_TABLE + ,PROP_UPDATE_FLAGS + ,PROP_RESULT_POS +} +DbModelProp; + +static void db_model_set_property (DbModel * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_CONN: + db_model_set_conn (obj, g_value_get_object (value)); + break; + case PROP_STMT: + db_model_set_stmt (obj, g_value_get_object (value)); + break; + case PROP_SQL: + db_model_set_sql (obj, g_value_get_string (value)); + break; + case PROP_USE_FILE: + obj->priv->use_file = g_value_get_boolean (value); + break; + case PROP_MAIN_TABLE: + db_model_request_main_table (obj, g_value_get_string (value)); + break; + case PROP_UPDATE_FLAGS: + db_model_request_update_flags (obj, g_value_get_flags (value)); + break; + case PROP_RESULT_POS: + obj->priv->result_pos = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void db_model_get_property (DbModel * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_CONN: + g_value_set_object (value, obj->priv->conn); + break; + case PROP_STMT: + g_value_set_object (value, obj->priv->stmt); + break; + case PROP_SQL: + g_value_set_string (value, obj->priv->sql); + break; + case PROP_USE_FILE: + g_value_set_boolean (value, obj->priv->use_file); + break; + case PROP_MAIN_TABLE: + g_value_set_string (value, obj->priv->main_table); + break; + case PROP_UPDATE_FLAGS: + g_value_set_flags (value, obj->priv->update_flags); + break; + case PROP_RESULT_POS: + g_value_set_uint (value, obj->priv->result_pos); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void db_model_init (DbModel *obj) +{ + obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, DB_TYPE_MODEL, DbModelPrivate); + + obj->priv->conn = NULL; + obj->priv->stmt = NULL; + obj->priv->use_file = FALSE; + obj->priv->sql = NULL; + obj->priv->main_table = NULL; + obj->priv->update_flags = 0; + obj->priv->user_main_table = NULL; + obj->priv->user_update_flags = DB_MODEL_ALL; + obj->priv->request = NULL; + obj->priv->status = DB_MODEL_STATUS_CLEAN; + obj->priv->mode = DB_MODEL_MODE_ON_CHANGE; + obj->priv->result = NULL; + obj->priv->result_pos = 0; + obj->priv->data = NULL; + obj->priv->column = NULL; + obj->priv->operation = g_queue_new (); + obj->priv->row_ops = g_hash_table_new (g_direct_hash, g_direct_equal); + obj->priv->column_index = NULL; + obj->priv->join = NULL; + obj->priv->column_default = NULL; + obj->priv->param_default = NULL; + obj->priv->pending_request = NULL; + + obj->priv->stamp = g_random_int (); + + obj->priv->fresh = TRUE; + obj->priv->sort_column_id = DB_MODEL_UNSORTED_SORT_COLUMN_ID; + obj->priv->default_sort_data = NULL; + obj->priv->default_sort_func = NULL; + obj->priv->default_sort_destroy = NULL; +} + +static void db_model_finalize (DbModel * obj) +{ + GSList * n; + GObjectClass * parent; + parent = g_type_class_peek_parent (DB_MODEL_GET_CLASS (obj)); + + db_model_clear (obj); + + g_clear_object (&obj->priv->conn); + g_clear_object (&obj->priv->stmt); + + if (obj->priv->join) + g_slist_free_full (obj->priv->join, (GDestroyNotify) db_join_free); + + g_hash_table_destroy (obj->priv->row_ops); + g_queue_free (obj->priv->operation); + + g_free (obj->priv->sql); + g_free (obj->priv->main_table); + g_free (obj->priv->user_main_table); + + if (obj->priv->column_index) + g_hash_table_destroy (obj->priv->column_index); + + for (n = obj->priv->column_default; n; n = n->next) + { + g_free (((DbColDef *) n->data)->dst); + g_free (n->data); + } + + g_slist_free (obj->priv->column_default); + + for (n = obj->priv->param_default; n; n = n->next) + { + g_object_unref (((DbParamDef *) n->data)->param); + g_free (((DbParamDef *) n->data)->dst); + g_free (n->data); + } + + g_slist_free (obj->priv->param_default); + + parent->finalize (G_OBJECT (obj)); +} + +static void db_model_class_init (DbModelClass *k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->set_property = (GObjectSetPropertyFunc) db_model_set_property; + klass->get_property = (GObjectGetPropertyFunc) db_model_get_property; + klass->finalize = (GObjectFinalizeFunc) db_model_finalize; + + g_type_class_add_private (klass, sizeof (DbModelPrivate)); + + /** + * DbModel::status-changed: + * @model: the object which received the signal + * @status: the current status of @model + * + * This signal is emitted every time the status of @model changes. + */ + db_model_signal[STATUS_CHANGED] = g_signal_new ("status-changed" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0, NULL, NULL + ,g_cclosure_marshal_VOID__INT + ,G_TYPE_NONE, 1, G_TYPE_INT + ); + + /** + * DbModel::line-inserted: + * @model: the object which received the signal + * @iter: a #DbIter + * + * This signal is emitted when a new row is inserted into @model. The inserted + * row is pointed to by @iter. + */ + db_model_signal[LINE_INSERTED] = g_signal_new ("line-inserted" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0, NULL, NULL + ,g_cclosure_marshal_VOID__BOXED + ,G_TYPE_NONE, 1, DB_TYPE_ITER + ); + + /** + * DbModel::line-deleted: + * @model: the object which received the signal + * @position: the position of the deleted line + * + * Every time a row of @model is deleted, this signal is emitted. + */ + db_model_signal[LINE_DELETED] = g_signal_new_class_handler ("line-deleted" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_LAST, (GCallback) db_model_on_line_deleted + ,NULL, NULL, g_cclosure_marshal_VOID__INT + ,G_TYPE_NONE, 1, G_TYPE_INT + ); + + /** + * DbModel::line-toggled: + * @model: the instance that received the signal + * @iter: a #DbIter + * + * Emitted to tell that the line is going to be deleted, + * but the operation is still. + */ + db_model_signal[LINE_TOGGLED] = g_signal_new ("line-toggled" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0 ,NULL, NULL + ,g_cclosure_marshal_VOID__BOXED + ,G_TYPE_NONE, 1, DB_TYPE_ITER + ); + + /** + * DbModel::line-updated: + * @model: the object which received the signal + * @iter: a #DbIter + * + * This signal is emitted when any value in a row of @model is set. + */ + db_model_signal[LINE_UPDATED] = g_signal_new_class_handler ("line-updated" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_LAST, (GCallback) db_model_on_line_updated + ,NULL, NULL, g_cclosure_marshal_VOID__BOXED + ,G_TYPE_NONE, 1, DB_TYPE_ITER + ); + + /** + * DbModel::lines-reordered: + * @model: the object which received the signal + * @col: the sort column + * @new_order: (array) (element-type gint): an array mapped with the new + * positions over the old ones + * + * This signal is emitted by @model every time its rows are reordered. + * + * The @new_order array is an array of which the positions indexed after the + * old order of the elements of @model, contain the new position of these + * elements in the reordered @model. + */ + db_model_signal[LINES_REORDERED] = g_signal_new ("lines-reordered" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0, NULL, NULL + ,g_cclosure_marshal_VOID__UINT_POINTER + ,G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER + ); + + /** + * DbModel::sort-changed: + * @model: the object which received the signal + * + * This signal is emitted when a new column is selected as sort criteria for + * @model. + */ + db_model_signal[SORT_CHANGED] = g_signal_new ("sort-changed" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0, NULL, NULL + ,g_cclosure_marshal_VOID__VOID + ,G_TYPE_NONE, 0 + ); + + /** + * DbModel::operations-done: + * @model: the object which received the signal + * @success: whether the operation has failed or succeded + * + * When an operation on the model is performed on the DB, this signal + * is emitted. + */ + db_model_signal[OPERATIONS_DONE] = g_signal_new ("operations-done" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0, NULL, NULL + ,g_cclosure_marshal_VOID__VOID + ,G_TYPE_NONE, 0 + ); + + g_object_class_install_property (klass, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The DbConn that manages the connection to the database") + ,DB_TYPE_CONN + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_STMT, + g_param_spec_object ("stmt" + ,_("Statement") + ,_("The statement which retrieves the data") + ,SQL_TYPE_STMT + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("Depending on the \"use-file\" property this will " + "be the path to a file with queries for the " + "model or a SQL string") + ,NULL + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_USE_FILE, + g_param_spec_boolean ("use-file" + ,_("Use file") + ,_("If this is set to TRUE, the \"sql\" property will " + "hold the name of a file containing a query, if " + "set to FALSE, \"sql\" is used as an SQL string") + ,FALSE + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_MAIN_TABLE, + g_param_spec_string ("main-table" + ,_("Main Table") + ,_("The main table of the model") + ,NULL + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_UPDATE_FLAGS, + g_param_spec_flags ("update-flags" + ,_("Update flags") + ,_("The flags that indicate how a model can be modified") + ,DB_TYPE_MODEL_UPDATE_FLAGS + ,DB_MODEL_ALL + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_RESULT_POS, + g_param_spec_uint ("result-pos" + ,_("Result position") + ,_("The position where the query that will fill the " + "model will be placed in a multi-query") + ,0 + ,G_MAXUINT32 + ,0 + ,G_PARAM_READWRITE + )); +} + +GType db_model_update_flags_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GFlagsValue values[] = + { + {DB_MODEL_INSERT, "DB_MODEL_INSERT", "insert"}, + {DB_MODEL_DELETE, "DB_MODEL_DELETE", "delete"}, + {DB_MODEL_UPDATE, "DB_MODEL_UPDATE", "update"}, + {0, NULL, NULL} + }; + + type = g_flags_register_static + (g_intern_static_string ("DbModelUpdateFlags"), values); + } + + return type; +} diff --git a/db/db-model.h b/db/db-model.h new file mode 100644 index 0000000..26385ee --- /dev/null +++ b/db/db-model.h @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This file is part of Hedera. + * + * Hedera 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 . + */ + +#ifndef DB_MODEL_H +#define DB_MODEL_H + +#include +#include "db-conn.h" +#include "db-iter.h" + +#define DB_MODEL_LOG_DOMAIN (g_quark_from_string ("DbModel")) + +#define DB_TYPE_MODEL (db_model_get_type()) +#define DB_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_MODEL, DbModel)) +#define DB_IS_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_MODEL)) +#define DB_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_MODEL, DbModelClass)) +#define DB_IS_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_MODEL)) +#define DB_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_MODEL, DbModelClass)) + +#define DB_TYPE_MODEL_UPDATE_FLAGS (db_model_update_flags_get_type()) + +typedef struct _DbModel DbModel; +typedef struct _DbModelClass DbModelClass; +typedef struct _DbModelPrivate DbModelPrivate; + +/** + * DbModelRowOp: + * @DB_MODEL_ROW_OP_INSERT: an INSERT operation + * @DB_MODEL_ROW_OP_DELETE: a DELETE operation + * @DB_MODEL_ROW_OP_UPDATE: an UPDATE operation + * + * Flags stating the changes made over a row. + * + * If a DELETE is taking place on a row at the same time of another operation, + * the changes won't be performed and the row will be deleted. Every INSERT + * operation must have an UPDATE operation to add data, otherwise all data will + * get filled with the values set by default by the DB. Each UPDATE operation + * will merge with the previous ones and substitute the values already set by + * these. + **/ +typedef enum +{ + DB_MODEL_ROW_OP_INSERT = 1 << 0 + ,DB_MODEL_ROW_OP_DELETE = 1 << 1 + ,DB_MODEL_ROW_OP_UPDATE = 1 << 2 +} +DbModelRowOp; + +/** + * DbModelError: + * @DB_MODEL_ERROR_NOT_READY: the model data is not completely loaded yet + * @DB_MODEL_ERROR_NOT_UPDATABLE: a changed model column cannot be updated + * @DB_MODEL_ERROR_OUT_OF_RANGE: an index is out of the column range + * + * Error codes of a #DbModel. + **/ +typedef enum +{ + DB_MODEL_ERROR_NOT_READY + ,DB_MODEL_ERROR_NOT_UPDATABLE + ,DB_MODEL_ERROR_OUT_OF_RANGE +} +DbModelError; + +/** + * DbModelUpdateFlags: + * @DB_MODEL_INSERT: rows can be inserted into the model + * @DB_MODEL_DELETE: it's possible to delete rows from the model + * @DB_MODEL_UPDATE: the values of the model can be changed + * + * Indicates which operations are allowed for the user in a #DbModel. + **/ +typedef enum +{ + DB_MODEL_INSERT = 1 << 0 + ,DB_MODEL_DELETE = 1 << 1 + ,DB_MODEL_UPDATE = 1 << 2 + /* */ + ,DB_MODEL_ALL = DB_MODEL_INSERT | DB_MODEL_DELETE | DB_MODEL_UPDATE +} +DbModelUpdateFlags; + +/** + * DbModelStatus: + * @DB_MODEL_STATUS_ERROR: there has been an error while executing the main + * statement of a #DbModel + * @DB_MODEL_STATUS_LOADING: the statement is being executed + * @DB_MODEL_STATUS_READY: the statement execution is over and the data is + * available on the #DbModel + * @DB_MODEL_STATUS_CLEAN: the #DbModel is created but the statement has not + * been executed yet + * + * These constants indicate the status of the model. This status depends on the + * execution of the stamtement (@stmt property)of a #DbModel. They are returned + * by db_model_get_status(). + **/ +typedef enum +{ + DB_MODEL_STATUS_ERROR + ,DB_MODEL_STATUS_LOADING + ,DB_MODEL_STATUS_READY + ,DB_MODEL_STATUS_CLEAN +} +DbModelStatus; + +/** + * DbModelMode: + * @DB_MODEL_MODE_ON_CHANGE: every change made in the model will be inmediatelly + * sent to the database + * @DB_MODEL_MODE_ON_DEMAND: nothing will be sent to the database since demanded + * by calling db_model_perform_operations() + * + * The working mode of a #DbModel, depending on which the changes made on it + * will be sent to the database or will be made only locally. + * The same methods are used to modify the model in both modes + * (db_model_set_value(), db_model_insert() and db_model_delete()). + **/ +typedef enum +{ + DB_MODEL_MODE_ON_CHANGE + ,DB_MODEL_MODE_ON_DEMAND +} +DbModelMode; + +/** + * DbSortType: + * @DB_SORT_ASCENDING: Ascending sort order + * @DB_SORT_DESCENDING: Descending sort order + * + * Determines the direction of a sort. + **/ +typedef enum +{ + DB_SORT_ASCENDING + ,DB_SORT_DESCENDING +} +DbSortType; + +/** + * DbIterCompareFunc: + * @model: a #DbModel + * @a: a #DbIter + * @b: a #DbIter + * @user_data: (closure): user-provided data to use while comparing two #DbIter + * + * Prototype of a function used to search some in a model using + * #DbIter to iterate through it. + **/ +typedef gint (*DbIterCompareFunc) (DbModel * model + ,DbIter * a + ,DbIter * b + ,gpointer user_data); + +struct _DbModel +{ + GObject parent; + DbModelPrivate * priv; +}; + +struct _DbModelClass +{ + /* */ + GObjectClass parent; +}; + +GType db_model_get_type (); +GType db_model_update_flags_get_type () G_GNUC_CONST; + +DbModel * db_model_new (DbConn * conn, SqlStmt * stmt); +DbModel * db_model_new_with_sql (DbConn * conn, const gchar * sql); +DbModel * db_model_new_with_file (DbConn * conn, const gchar * file); +DbConn * db_model_get_conn (DbModel * obj); +void db_model_set_conn (DbModel * obj, DbConn * conn); +const GvnParamSpec * db_model_get_spec (DbModel * obj, gint col); +const gchar * db_model_get_column_name (DbModel * obj, gint col); +gint db_model_get_column_index (DbModel * obj, const gchar * name); +void db_model_refresh (DbModel * obj); +const SqlStmt * db_model_get_stmt (DbModel * obj); +void db_model_set_stmt (DbModel * obj, SqlStmt * stmt); +void db_model_add_pre_stmt (DbModel * obj, SqlStmt * stmt); +void db_model_add_post_stmt (DbModel * obj, SqlStmt * stmt); +DbModelStatus db_model_get_status (DbModel * obj); +const gchar * db_model_get_main_table (DbModel * obj); +void db_model_request_main_table (DbModel * obj, const gchar * table); +DbModelUpdateFlags db_model_get_update_flags (DbModel * obj); +void db_model_request_update_flags (DbModel * obj + ,DbModelUpdateFlags flags); +void db_model_unset_update_flags (DbModel * obj + ,DbModelUpdateFlags flags); +void db_model_set_mode (DbModel * obj, DbModelMode mode); +DbModelMode db_model_get_mode (DbModel * obj); +void db_model_toggle_mode (DbModel * obj); +gboolean db_model_get_last (DbModel * obj, DbIter * iter); +void db_model_get (DbModel * obj + ,DbIter * iter + ,...); +void db_model_set (DbModel * obj + ,DbIter * iter + ,...); +const GValue * db_model_get_value (DbModel * obj, + DbIter * iter, + gint col, + GError ** err); +gboolean db_model_set_value (DbModel * obj, + DbIter * iter, + gint col, + const GValue * value, + GError ** err); +gboolean db_model_insert (DbModel * obj, DbIter * iter); +void db_model_delete (DbModel * obj, DbIter * iter); +DbModelRowOp db_model_get_row_operations (DbModel * obj, DbIter * iter); +gboolean db_model_has_pending_operations (DbModel * obj); +void db_model_perform_operations (DbModel * obj, gboolean retry); +void db_model_reverse_operations (DbModel * obj); +void db_model_order_by (DbModel * obj, + gint col, + DbSortType order); +gboolean db_model_search (DbModel * obj, + gint col, + DbIter * iter, + gpointer content); +gboolean db_model_search_value (DbModel * obj, + gint col, + DbIter * iter, + const GValue * value); +gint db_model_get_nrows (DbModel * obj); +gboolean db_model_iter_is_valid (DbIter * iter + ,DbModel * model); +void db_model_add_join_columns (DbModel * obj + ,const gchar * left + ,const gchar * right); +void db_model_set_default_value_from_column (DbModel * obj + ,const gchar * dst_field + ,gint src_column); +void db_model_set_default_value_from_param (DbModel * obj + ,const gchar * dst_field + ,GvnParam * param + ,const gchar * id); +void db_model_add_param (DbModel * obj + ,const gchar * id + ,GvnParam * param); + +//GtkTreeModel-like methods + +gint db_model_get_ncols (DbModel * obj); +GType db_model_get_column_type (DbModel * obj, gint index); +gboolean db_model_get_iter_first (DbModel * obj, DbIter * iter); +gboolean db_model_get_iter (DbModel * obj, + DbIter * iter, + gint path); +gint db_model_get_path (DbModel * obj, DbIter * iter); +gboolean db_model_iter_next (DbModel * obj, DbIter * iter); +gboolean db_model_iter_prev (DbModel * obj, DbIter * iter); +void db_model_ref_node (DbModel * obj, DbIter * iter); +void db_model_unref_node (DbModel * obj, DbIter * iter); + +//GtkTreeSortable-like methods + +gboolean db_model_get_sort_column_id (DbModel * obj, + gint * sort_column_id, + DbSortType * order); +void db_model_set_sort_column_id (DbModel * obj, + gint sort_column_id, + DbSortType order); +void db_model_set_sort_func (DbModel * obj, + gint sort_column_id, + DbIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +void db_model_set_default_sort_func (DbModel * obj, + DbIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +gboolean db_model_has_default_sort_func (DbModel * obj); + +#endif diff --git a/db/db-param.c b/db/db-param.c new file mode 100644 index 0000000..4f2230a --- /dev/null +++ b/db/db-param.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-param.h" + +G_DEFINE_TYPE (DbParam, db_param, GVN_TYPE_PARAM); + +/** + * db_param_new: + * @column_name: col name of #DbModel associated to param. + * + * Creates a new #DbParam. + * + * Return value: the new #DbParam + **/ +GvnParam * db_param_new (const gchar * column_name) +{ + return g_object_new (DB_TYPE_PARAM, "column-name", column_name, NULL); +} + +/** + * db_param_new_with_index: + * @column_index: col index of #DbModel associated to param. + * + * Creates a new #DbParam. + * + * Return value: the new #DbParam + **/ +GvnParam * db_param_new_with_index (guint column_index) +{ + return g_object_new (DB_TYPE_PARAM, "column-index", column_index, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void db_param_on_value_changed (DbParam * obj, const GValue * value, gpointer data) +{ + db_iterator_set_value (obj->iterator, obj->column_index, value, NULL); +} + +static void db_param_on_iterator_status_changed (DbIterator * iterator, gboolean ready, DbParam * obj) +{ + GvnParamClass * klass = GVN_PARAM_GET_CLASS (obj); + + if (ready) + { + if (obj->column_name) + obj->column_index = db_iterator_get_column_index (obj->iterator, obj->column_name); + + klass->set_spec (GVN_PARAM (obj), + db_iterator_get_spec (obj->iterator, obj->column_index)); + } +} + +static void db_param_on_iterator_iter_changed (DbIterator * iterator, DbParam * obj) +{ + const GValue * value; + GvnParamClass * klass = GVN_PARAM_GET_CLASS (obj); + + if (db_iterator_get_row (obj->iterator) != -1 && obj->column_index != -1) + { + klass->set_status (GVN_PARAM (obj), GVN_PARAM_STATUS_OK); + value = db_iterator_get_value (obj->iterator, obj->column_index); + } + else + { + klass->set_status (GVN_PARAM (obj), GVN_PARAM_STATUS_BUSY); + value = NULL; + } + + g_signal_handlers_block_by_func (obj, db_param_on_value_changed, NULL); + + if (!value) + { + GValue tmp_value = {0}; + g_value_init (&tmp_value, GVN_TYPE_NULL); + klass->put_value (GVN_PARAM (obj), &tmp_value); + g_value_unset (&tmp_value); + } + else + klass->put_value (GVN_PARAM (obj), value); + + g_signal_handlers_unblock_by_func (obj, db_param_on_value_changed, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * db_param_get_column_index: + * @obj: a #DbParam + * + * Gets the iterator column referenced by the param. + * + * Return value: the column index. + **/ +guint db_param_get_column_index (DbParam * obj) +{ + g_return_val_if_fail (DB_IS_PARAM (obj), 0); + + return obj->column_index; +} + +/** + * db_param_get_column_name: + * @obj: a #DbParam + * + * Gets the iterator column referenced by the param. + * + * Return value: the column name. + **/ +const gchar * db_param_get_column_name (DbParam * obj) +{ + g_return_val_if_fail (DB_IS_PARAM (obj), 0); + + return obj->column_name; +} + +/** + * db_param_set_iterator: + * @obj: a #DbParam + * @iterator: the #DbIterator + * + * Sets the iterator that updates the param value, this property can be set if + * the parameter does not have a iterator associated yet. + **/ +void db_param_set_iterator (DbParam * obj, DbIterator * iterator) +{ + g_return_if_fail (DB_IS_PARAM (obj)); + g_return_if_fail (DB_IS_ITERATOR (iterator)); + g_return_if_fail (!obj->iterator); + + obj->iterator = g_object_ref (iterator); + g_object_connect (iterator + ,"signal::iter-changed", db_param_on_iterator_iter_changed, obj + ,"signal::status-changed", db_param_on_iterator_status_changed, obj + ,NULL + ); + db_param_on_iterator_status_changed (obj->iterator, + db_iterator_is_ready (iterator), obj); + db_param_on_iterator_iter_changed (obj->iterator, obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_ITERATOR = 1 + ,PROP_COLUMN_INDEX + ,PROP_COLUMN_NAME +}; + +static void db_param_set_property (DbParam * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_ITERATOR: + db_param_set_iterator (obj, g_value_get_object (value)); + break; + case PROP_COLUMN_INDEX: + obj->column_index = g_value_get_int (value); + break; + case PROP_COLUMN_NAME: + g_free (obj->column_name); + obj->column_name = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void db_param_get_property (DbParam * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_ITERATOR: + g_value_set_object (value, obj->iterator); + break; + case PROP_COLUMN_INDEX: + g_value_set_int (value, obj->column_index); + break; + case PROP_COLUMN_NAME: + g_value_set_string (value, obj->column_name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_param_init (DbParam * obj) +{ + obj->column_index = -1; + obj->column_name = NULL; + obj->iterator = NULL; + g_signal_connect (obj, "value-changed", + G_CALLBACK (db_param_on_value_changed), NULL); +} + +static void db_param_finalize (DbParam * obj) +{ + if (obj->iterator) + { + g_object_disconnect (obj->iterator + ,"any_signal", db_param_on_iterator_iter_changed, obj + ,"any_signal", db_param_on_iterator_status_changed, obj + ,NULL + ); + g_clear_object (&obj->iterator); + } + + g_signal_handlers_disconnect_by_func (obj, + db_param_on_value_changed, NULL); + g_free (obj->column_name); + G_OBJECT_CLASS (db_param_parent_class)->finalize (G_OBJECT (obj)); +} + +static void db_param_class_init (DbParamClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) db_param_finalize; + klass->set_property = (GObjectSetPropertyFunc) db_param_set_property; + klass->get_property = (GObjectGetPropertyFunc) db_param_get_property; + + g_object_class_install_property (klass, PROP_ITERATOR, + g_param_spec_object ("iterator" + ,_("Iterator") + ,_("The iterator owner of param") + ,DB_TYPE_ITERATOR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_COLUMN_INDEX, + g_param_spec_int ("column-index" + ,_("Column index") + ,_("The referenced column index") + ,-1 ,1024 ,-1 + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_COLUMN_NAME, + g_param_spec_string ("column-name" + ,_("Column name") + ,_("The referenced column name") + ,NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); +} diff --git a/db/db-param.h b/db/db-param.h new file mode 100644 index 0000000..a3cb575 --- /dev/null +++ b/db/db-param.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_PARAM_H +#define DB_PARAM_H + +#include + +#define DB_TYPE_PARAM (db_param_get_type ()) +#define DB_PARAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_PARAM, DbParam)) +#define DB_IS_PARAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_PARAM)) +#define DB_PARAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_PARAM, DbParamClass)) +#define DB_IS_PARAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_PARAM)) +#define DB_PARAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_PARAM, DbParamClass)) + +typedef struct _DbParam DbParam; +typedef struct _DbParamClass DbParamClass; + +#include "db-iterator.h" + +struct _DbParam +{ + GvnParam parent; + DbIterator * iterator; + gint column_index; + gchar * column_name; +}; + +struct _DbParamClass +{ + /* */ + GvnParamClass parent; +}; + +GType db_param_get_type (); +GvnParam * db_param_new (const gchar * column_name); +GvnParam * db_param_new_with_index (guint column_index); +guint db_param_get_column_index (DbParam * obj); +const gchar * db_param_get_column_name (DbParam * obj); +void db_param_set_iterator (DbParam * obj, DbIterator * iterator); + +#endif \ No newline at end of file diff --git a/db/db-plugin.c b/db/db-plugin.c new file mode 100644 index 0000000..1666cb0 --- /dev/null +++ b/db/db-plugin.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-plugin.h" + +/** + * SECTION: db-plugin + * @Short_description: manages a connection to a database. + * @Title: DbPlugin + * @See_also: #DbConn + * + * This class manages a connection to a database internally. This + * is accessed through the #DbConn class to internally connect, query and + * disconnect the database. + **/ +G_DEFINE_ABSTRACT_TYPE (DbPlugin, db_plugin, G_TYPE_OBJECT); + +/** + * db_plugin_close: + * @obj: a #DbPlugin + * + * Closes de current connection to the database. + **/ +void db_plugin_close (DbPlugin * obj) +{ + g_return_if_fail (DB_IS_PLUGIN (obj)); + + g_mutex_lock (&obj->mutex); + g_mutex_lock (&obj->kill_mutex); + + DB_PLUGIN_GET_CLASS (obj)->close (obj); + + g_mutex_unlock (&obj->kill_mutex); + g_mutex_unlock (&obj->mutex); +} + +/** + * db_plugin_open: + * @obj: a #DbPlugin + * @host: the hostname + * @schema: the database schema + * @user: the user name + * @pass: the password + * @err: (out) (allow-none): destination of #GError, if you want to handle it. + * + * Opens a new connection to the database. + * + * Return value: Returns %TRUE if connection was made, %FALSE otherwise. + **/ +gboolean db_plugin_open (DbPlugin * obj, const gchar * host, + const gchar * schema, const gchar * user, const gchar * pass, GError ** err) +{ + gboolean opened; + + g_return_val_if_fail (DB_IS_PLUGIN (obj), FALSE); + + g_mutex_lock (&obj->mutex); + g_mutex_lock (&obj->kill_mutex); + + DB_PLUGIN_GET_CLASS (obj)->close (obj); + + if (obj->ca) + DB_PLUGIN_GET_CLASS (obj)->set_ssl (obj, obj->ca); + + opened = DB_PLUGIN_GET_CLASS (obj)->open (obj, + host, schema, user, pass, err); + + g_mutex_unlock (&obj->kill_mutex); + g_mutex_unlock (&obj->mutex); + + return opened; +} + +/** + * db_plugin_set_ssl: + * @obj: a #DbPlugin + * @ca: path to the certificate authority file + * + * Sets the certificate authority file needed to use SSL. If the secure + * connection through SSL is not properly set, @db_plugin_open will fail. + */ +void db_plugin_set_ssl (DbPlugin * obj, const gchar * ca) +{ + g_return_if_fail (DB_IS_PLUGIN (obj)); + + g_mutex_lock (&obj->mutex); + g_free (obj->ca); + obj->ca = strdup (ca); + g_mutex_unlock (&obj->mutex); +} + +/** + * db_plugin_query: + * @obj: a #DbPlugin + * @sql: a #gchar string containing an sql query + * @err: (out) (allow-none): destination of #GError, if you want to handle it. + * + * If everything was ok (i.e. the sql query wasn't empty, it was completed + * successfully, etc.), it will return a #GList of #DbResult with the + * database response, depending on the query or queries passed. Note that if + * the input string @sql is a multi-query and any of the queries inside of it + * fails the rest of the queries will fail. + * + * Refer to #DbError for the possible errors. + * + * Return value: (transfer full) (allow-none): a #DbResultSet containing the + * results or %NULL if error + **/ +DbResultSet * db_plugin_query (DbPlugin * obj, const gchar * sql, GError ** err) +{ + DbResultSet * set; + + g_return_val_if_fail (DB_IS_PLUGIN (obj), NULL); + + g_mutex_lock (&obj->mutex); + set = DB_PLUGIN_GET_CLASS (obj)->query (obj, sql, err); + g_mutex_unlock (&obj->mutex); + + return set; +} + +/** + * db_plugin_kill_query: + * @obj: a #DbPlugin + * + * Tryes to kill the current query. + **/ +void db_plugin_kill_query (DbPlugin * obj) +{ + DbPluginClass * klass; + + g_return_if_fail (DB_IS_PLUGIN (obj)); + + klass = DB_PLUGIN_GET_CLASS (obj); + + if (klass->kill_query) + { + g_mutex_lock (&obj->kill_mutex); + klass->kill_query (obj); + g_mutex_unlock (&obj->kill_mutex); + } +} + +/** + * db_plugin_parse: + * @obj: a #DbPlugin. + * @sql: (transfer none): an SQL string. + * + * Parses a string and makes an #SqlStmt from it. + * + * Return value: (transfer none): a new #SqlStmt parsed from @sql. + **/ +SqlStmt * db_plugin_parse (DbPlugin * obj, gchar * sql) +{ + g_return_val_if_fail (DB_IS_PLUGIN (obj), NULL); + + return NULL; +} + +/** + * db_plugin_render: + * @obj: a #DbPlugin + * @object: the #GObject to render + * @err: (out) (allow-none): the return location for #GError + * + * Renders a #GObject object as a SQL string to send it in a database + * query. It takes the plugin to know the codification in wich to escape + * the data. + * + * Return value: (transfer full): the rendered string, or %NULL if error. + **/ +gchar * db_plugin_render (DbPlugin * obj, gpointer object, GError ** err) +{ + g_return_val_if_fail (DB_IS_PLUGIN (obj), NULL); + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + + return sql_render_get_string (obj->render, object, obj, err); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_plugin_init (DbPlugin *obj) +{ + obj->render = NULL; + obj->ca = NULL; + g_mutex_init (&obj->mutex); + g_mutex_init (&obj->kill_mutex); +} + +static void db_plugin_finalize (DbPlugin * obj) +{ + g_clear_object (&obj->render); + g_free (obj->ca); + g_mutex_clear (&obj->mutex); + g_mutex_clear (&obj->kill_mutex); + G_OBJECT_CLASS (db_plugin_parent_class)->finalize (G_OBJECT (obj)); +} + +static void db_plugin_class_init (DbPluginClass * k) +{ + G_OBJECT_CLASS (k)->finalize = (GObjectFinalizeFunc) db_plugin_finalize; +} diff --git a/db/db-plugin.h b/db/db-plugin.h new file mode 100644 index 0000000..7d64217 --- /dev/null +++ b/db/db-plugin.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_PLUGIN_H +#define DB_PLUGIN_H + +#include +#include "db-result-set.h" + +#define DB_TYPE_PLUGIN (db_plugin_get_type ()) +#define DB_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_PLUGIN, DbPlugin)) +#define DB_IS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_PLUGIN)) +#define DB_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_PLUGIN, DbPluginClass)) +#define DB_IS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_PLUGIN)) +#define DB_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_PLUGIN, DbPluginClass)) + +typedef struct _DbPlugin DbPlugin; +typedef struct _DbPluginClass DbPluginClass; + +typedef GType (* DbPluginGetTypeFunc) (); +typedef gboolean (* DbPluginOpenFunc) (DbPlugin * obj + ,const gchar * host + ,const gchar * schema + ,const gchar * user + ,const gchar * pass + ,GError ** err); +typedef void (* DbPluginCloseFunc) (DbPlugin * obj); +typedef void (* DbPluginSetSSL) (DbPlugin * obj + ,const gchar * ca); +typedef DbResultSet * (* DbPluginQueryFunc) (DbPlugin * obj, const gchar * sql, GError ** err); +typedef void (* DbPluginKillQueryFunc) (DbPlugin * obj); +typedef SqlStmt * (* DbPluginParseFunc) (DbPlugin * obj, const gchar * sql); + +struct _DbPlugin +{ + GObject parent; + GMutex mutex; + GMutex kill_mutex; + SqlRender * render; + gchar * ca; +}; + +struct _DbPluginClass +{ + /* */ + GObjectClass parent; + DbPluginOpenFunc open; + DbPluginCloseFunc close; + DbPluginSetSSL set_ssl; + DbPluginQueryFunc query; + DbPluginKillQueryFunc kill_query; + DbPluginParseFunc parse; +}; + +GType db_plugin_get_type (); +gboolean db_plugin_open (DbPlugin * obj + ,const gchar * host + ,const gchar * schema + ,const gchar * user + ,const gchar * pass + ,GError ** err); +void db_plugin_close (DbPlugin * obj); +void db_plugin_set_ssl (DbPlugin * obj, const gchar * ca); +DbResultSet * db_plugin_query (DbPlugin * obj, const gchar * sql, GError ** err); +void db_plugin_kill_query (DbPlugin * obj); +SqlStmt * db_plugin_parse (DbPlugin * obj, gchar * sql); +gchar * db_plugin_render (DbPlugin * obj, gpointer object, GError ** err); + +#endif \ No newline at end of file diff --git a/db/db-request.c b/db/db-request.c new file mode 100644 index 0000000..9bf13e6 --- /dev/null +++ b/db/db-request.c @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-request.h" + +/** + * SECTION: db-request + * @Short_description: a request to the database sent by the model + * @Title: DbRequest + * @See_also: #DbConn, #DbModel + * + * This class is used to send an SQL query to the database through a #DbConn, + * and to retrieve any data or metadata obtained by it in the model. + **/ +G_DEFINE_TYPE (DbRequest, db_request, G_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static gboolean db_request_idle (DbRequest * obj) +{ + if (obj->callback) + obj->callback (obj, obj->user_data); + + return FALSE; +} + +static void db_request_idle_notify (DbRequest * obj) +{ + if (obj->notify) + obj->notify (obj->user_data); + + g_object_unref (obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +/** + * db_request_new: + * @conn: a #DbConn + * @sql: SQL statement to execute + * + * Creates a new request to execute the %sql query. + * + * Return value: a new #DbRequest + **/ +DbRequest * db_request_new (DbConn * conn, const gchar * sql) +{ + g_return_val_if_fail (DB_IS_CONN (conn), NULL); + + return g_object_new (DB_TYPE_REQUEST, "conn", conn, "sql", sql, NULL); +} + +/** + * db_request_new_with_stmt: + * @conn: a #DbConn + * @stmt: the #SqlStmt statement to execute. + * + * Creates a new request to execute the statement. + * + * Return value: a new #DbRequest + **/ +DbRequest * db_request_new_with_stmt (DbConn * conn, SqlStmt * stmt) +{ + g_return_val_if_fail (DB_IS_CONN (conn), NULL); + g_return_val_if_fail (SQL_IS_STMT (stmt), NULL); + + return g_object_new (DB_TYPE_REQUEST, "conn", conn, "stmt", stmt, NULL); +} + +/** + * db_request_fetch_result: + * @obj: a #DbRequest + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Obtains the first result of the query. The request must have been made to + * call this method. + * + * Return value: (transfer full) (allow-none): the #DbResult + **/ +DbResult * db_request_fetch_result (DbRequest * obj, GError ** err) +{ + DbResult * result = NULL; + + g_return_val_if_fail (DB_IS_REQUEST (obj), NULL); + + g_mutex_lock (&obj->mutex); + + if (obj->result_set) + { + result = db_result_set_take_next (obj->result_set); + } + else if (err) + { + g_propagate_error (err, obj->error); + obj->error = NULL; + } + + g_mutex_unlock (&obj->mutex); + return result; +} + +/** + * db_request_fetch_non_select: + * @obj: a #DbRequest + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Fetchs the result of a non select statement. + * + * Return value: returns 0 or the number of affected rows on success, -1 on failure + **/ +gint db_request_fetch_non_select (DbRequest * obj, GError ** err) +{ + DbResult * result; + + g_return_val_if_fail (DB_IS_REQUEST (obj), -1); + + result = db_request_fetch_result (obj, err); + + if (result) + { + gint affected_rows = result->nrows; + db_result_free (result); + return affected_rows; + } + + return -1; +} + +/** + * db_request_fetch_value: + * @obj: a #DbRequest + * @value: (out): return location for a #GValue + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Obtains the first result of the query. The request must have been made to + * call this method. + * + * Return value: %TRUE on success, %FALSE on failure + **/ +gboolean db_request_fetch_value (DbRequest * obj, GValue * value, GError ** err) +{ + DbResult * result; + + g_return_val_if_fail (DB_IS_REQUEST (obj), FALSE); + + result = db_request_fetch_result (obj, err); + + if (result) + { + DbRow * row = db_result_get_row (result, 0); + + if (row) + { + const GValue * row_value = db_row_get_value (row, 0); + + if (row_value) + { + g_value_init (value, G_VALUE_TYPE (row_value)); + g_value_copy (row_value, value); + return TRUE; + } + } + + db_result_free (result); + } + + return FALSE; +} + +/** + * db_request_exec: + * @obj: a #DbRequest + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Sends the request to the database. + **/ +gboolean db_request_exec (DbRequest * obj, GError ** err) +{ + gboolean ret; + + g_return_val_if_fail (DB_IS_REQUEST (obj), FALSE); + + ret = TRUE; + g_mutex_lock (&obj->mutex); + + switch ((gint) obj->status) + { + case DB_REQUEST_CLEAN: + case DB_REQUEST_ERROR: + { + DbResultSet * set; + GError * query_error = NULL; + + obj->status = DB_REQUEST_LOADING; + g_mutex_unlock (&obj->mutex); + + set = db_conn_exec (obj->conn, obj->sql, &query_error); + + g_mutex_lock (&obj->mutex); + + if (obj->status != DB_REQUEST_CANCELED) + { + if (set) + { + obj->result_set = set; + obj->status = DB_REQUEST_DONE; + } + else if (query_error) + { + g_clear_error (&obj->error); + obj->error = g_error_copy (query_error); + g_propagate_error (err, query_error); + obj->status = DB_REQUEST_ERROR; + ret = FALSE; + } + } + else + { + if (set) + db_result_set_free (set); + else + g_clear_error (&query_error); + } + } + } + + g_mutex_unlock (&obj->mutex); + + return ret; +} + +/** + * db_request_set_callback: + * @obj: a #DbRequest + * @callback: + * @user_data: + * @notify: + * + * Sends the request to the database. + **/ +void db_request_set_callback (DbRequest * obj, + DbRequestDoneCallback callback, gpointer user_data, GDestroyNotify notify) +{ + g_return_if_fail (DB_IS_REQUEST (obj)); + + g_mutex_lock (&obj->mutex); + + obj->callback = callback; + obj->user_data = user_data; + obj->notify = notify; + + g_mutex_unlock (&obj->mutex); +} + +/** + * db_request_complete: + * @obj: a #DbRequest + * + * Emits the ready or error signal of the object. + **/ +void db_request_complete (DbRequest * obj) +{ + g_return_if_fail (DB_IS_REQUEST (obj)); + + g_idle_add_full (G_PRIORITY_HIGH_IDLE, + (GSourceFunc) db_request_idle, g_object_ref (obj), (GDestroyNotify) db_request_idle_notify); +} + +/** + * db_request_cancel: + * @obj: a #DbRequest + * + * Attempts to cancel the request. + * + * Return value: %TRUE if request was canceled %FALSE otherwise. + **/ +void db_request_cancel (DbRequest * obj) +{ + g_return_if_fail (DB_IS_REQUEST (obj)); + + g_mutex_lock (&obj->mutex); + + if (obj->status != DB_REQUEST_CANCELED) + { + if (obj->result_set) + { + db_result_set_free (obj->result_set); + obj->result_set = NULL; + } + else + g_clear_error (&obj->error); + + obj->status = DB_REQUEST_CANCELED; + obj->error = g_error_new ( + DB_REQUEST_LOG_DOMAIN + ,DB_REQUEST_ERROR_CANCELED + ,_("The request was canceled") + ); + } + + g_mutex_unlock (&obj->mutex); +} + +/** + * db_request_get_conn: + * @obj: a #DbRequest + * + * Gets the connection used by request. + * + * Return value: (transfer full): the #DbConn + **/ +DbConn * db_request_get_conn (DbRequest * obj) +{ + g_return_val_if_fail (DB_IS_REQUEST (obj), NULL); + + return obj->conn; +} + +/** + * db_request_get_sql: + * @obj: a #DbRequest + * + * Gets the SQL statement used by #DbRequest. + * + * Return value: the sql string, must be freed with g_free() + **/ +gchar * db_request_get_sql (DbRequest * obj) +{ + g_return_val_if_fail (DB_IS_REQUEST (obj), NULL); + + return g_strdup (obj->sql); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +typedef enum +{ + PROP_CONN = 1 + ,PROP_SQL + ,PROP_STMT + ,PROP_RESULT_SET + ,PROP_ERROR +} +DbRequestProp; + +static void db_request_set_property (DbRequest * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + g_mutex_lock (&obj->mutex); + + switch (property_id) + { + case PROP_CONN: + obj->conn = g_value_dup_object (value); + break; + case PROP_SQL: + { + gchar * sql = g_value_dup_string (value); + + if (sql) + obj->sql = sql; + + break; + } + case PROP_STMT: + { + SqlStmt * stmt = g_value_get_object (value); + + if (obj->conn && stmt) + obj->sql = db_conn_render (obj->conn, stmt, NULL); + else if (!obj->conn) + g_warning ("DbRequest: Can't render stmt, conn property not set"); + + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } + + g_mutex_unlock (&obj->mutex); +} + +static void db_request_get_property (DbRequest * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_CONN: + g_value_set_object (value, obj->conn); + break; + case PROP_SQL: + g_value_set_string (value, obj->sql); + break; + case PROP_RESULT_SET: + g_value_set_boxed (value, obj->result_set); + break; + case PROP_ERROR: + g_value_set_boxed (value, obj->error); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_request_init (DbRequest * obj) +{ + obj->sql = NULL; + obj->error = NULL; + obj->result_set = NULL; + obj->conn = NULL; + obj->callback = NULL; + obj->notify = NULL; + obj->status = DB_REQUEST_CLEAN; + g_mutex_init (&obj->mutex); +} + +static void db_request_finalize (DbRequest * obj) +{ + if (obj->result_set) + db_result_set_free (obj->result_set); + + g_clear_error (&obj->error); + g_clear_object (&obj->conn); + g_free (obj->sql); + g_mutex_clear (&obj->mutex); + G_OBJECT_CLASS (db_request_parent_class)->finalize (G_OBJECT (obj)); +} + +static void db_request_class_init (DbRequestClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) db_request_finalize; + klass->set_property = (GObjectSetPropertyFunc) db_request_set_property; + klass->get_property = (GObjectGetPropertyFunc) db_request_get_property; + + g_object_class_install_property (klass, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used to render and execute the query") + ,DB_TYPE_CONN + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("The SQL query to execute") + ,NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_STMT, + g_param_spec_object ("stmt" + ,_("Statement") + ,_("The statement to execute") + ,SQL_TYPE_STMT + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE + )); + g_object_class_install_property (klass, PROP_RESULT_SET, + g_param_spec_boxed ("result-set" + ,_("Result") + ,_("The result data of the query") + ,DB_TYPE_RESULT_SET + ,G_PARAM_READABLE + )); + g_object_class_install_property (klass, PROP_ERROR, + g_param_spec_boxed ("error" + ,_("Error") + ,_("The GError, if an error ocurred") + ,G_TYPE_ERROR + ,G_PARAM_READABLE + )); +} diff --git a/db/db-request.h b/db/db-request.h new file mode 100644 index 0000000..4ccbbbc --- /dev/null +++ b/db/db-request.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_REQUEST_H +#define DB_REQUEST_H + +#include +#include "db-result-set.h" + +#define DB_REQUEST_LOG_DOMAIN (g_quark_from_string ("DbRequest")) + +#define DB_TYPE_REQUEST (db_request_get_type ()) +#define DB_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_REQUEST, DbRequest)) +#define DB_IS_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_REQUEST)) +#define DB_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_REQUEST, DbRequestClass)) +#define DB_IS_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_REQUEST)) +#define DB_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_REQUEST, DbRequestClass)) + +typedef struct _DbRequest DbRequest; +typedef struct _DbRequestClass DbRequestClass; + +typedef void (*DbRequestDoneCallback) (DbRequest * obj, gpointer user_data); + +#include "db-conn.h" + +typedef enum +{ + DB_REQUEST_CLEAN + ,DB_REQUEST_LOADING + ,DB_REQUEST_DONE + ,DB_REQUEST_CANCELED + ,DB_REQUEST_ERROR +} +DbRequestStatus; + +typedef enum +{ + DB_REQUEST_ERROR_CANCELED + ,DB_REQUEST_ERROR_NO_EXECUTED +} +DbRequestError; + +struct _DbRequest +{ + GObject parent; + + /* */ + DbConn * conn; + gchar * sql; + DbResultSet * result_set; + GError * error; + DbRequestStatus status; + GMutex mutex; + DbRequestDoneCallback callback; + gpointer user_data; + GDestroyNotify notify; +}; + +struct _DbRequestClass +{ + /* */ + GObjectClass parent; +}; + +GType db_request_get_type (); +DbRequest * db_request_new (DbConn * conn, const gchar * sql); +DbRequest * db_request_new_with_stmt (DbConn * conn, SqlStmt * stmt); +DbResult * db_request_fetch_result (DbRequest * obj, GError ** err); +gint db_request_fetch_non_select (DbRequest * obj, GError ** err); +gboolean db_request_fetch_value (DbRequest * obj, GValue * value, GError ** err); +gboolean db_request_exec (DbRequest * obj, GError ** err); +void db_request_set_callback (DbRequest * obj, DbRequestDoneCallback callback, gpointer user_data, GDestroyNotify notify); +void db_request_complete (DbRequest * obj); +void db_request_cancel (DbRequest * obj); +gchar * db_request_get_sql (DbRequest * obj); + +#endif diff --git a/db/db-result-set.c b/db/db-result-set.c new file mode 100644 index 0000000..2242701 --- /dev/null +++ b/db/db-result-set.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-result-set.h" + +/** + * SECTION: db-result-set + * @Short_description: + * @Title: DbResultSet + * @See_also: #DbResult + **/ +G_DEFINE_BOXED_TYPE (DbResultSet, db_result_set, db_result_set_copy, db_result_set_free); + +/** + * db_result_set_new: + * + * Return value: the new #DbResultSet + **/ +DbResultSet * db_result_set_new () +{ + DbResultSet * obj = g_new (DbResultSet, 1); + obj->results = NULL; + return obj; +} + +/** + * db_result_set_take_nth: + * @obj: a #DbResultSet + * @n: the position of the result, counting from 0 + * + * Gets the nth result and removes it from the resultset. The user is + * responsible for releasing the result with db_result_free(). + * + * Return value: (transfer full): the #DbResult or %NULL if nth result doesn't exist + **/ +DbResult * db_result_set_take_nth (DbResultSet * obj, guint n) +{ + GSList * list; + + g_return_val_if_fail (obj, NULL); + + list = g_slist_nth (obj->results, n); + + if (list) + { + DbResult * result = list->data; + obj->results = g_slist_delete_link (obj->results, list); + return result; + } + + return NULL; +} + +/** + * db_result_set_take_next: + * @obj: a #DbResultSet + * + * Gets the next result and removes it from the resultset. The user is + * responsible for releasing the result with db_result_free(). + * + * Return value: (transfer full): the #DbResult or %NULL if no more results + **/ +DbResult * db_result_set_take_next (DbResultSet * obj) +{ + g_return_val_if_fail (obj, NULL); + + return db_result_set_take_nth (obj, 0); +} + +/** + * db_result_set_copy: + * @obj: a #DbResultSet + * + * Makes a copy of a #DbResultSet. + * + * Return value: a newly allocated #DbResultSet, with the same contents + **/ +DbResultSet * db_result_set_copy (const DbResultSet * obj) +{ + GSList * n; + DbResultSet * set; + + g_return_val_if_fail (obj, NULL); + + set = db_result_set_new (); + + for (n = obj->results; n; n = n->next) + set->results = g_slist_append (set->results, db_result_copy (n->data)); + + return set; +} + +/** + * db_result_set_free: + * @obj: a #DbResultSet + * + * Frees a #DbResultSet. + **/ +void db_result_set_free (DbResultSet * obj) +{ + GSList * n; + + g_return_if_fail (obj); + + for (n = obj->results; n; n = n->next) + db_result_free (n->data); + + g_slist_free (obj->results); + g_free (obj); +} diff --git a/db/db-result-set.h b/db/db-result-set.h new file mode 100644 index 0000000..f6c9839 --- /dev/null +++ b/db/db-result-set.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_RESULT_SET_H +#define DB_RESULT_SET_H + +#include +#include "db-result.h" + +#define DB_TYPE_RESULT_SET (db_result_set_get_type()) + +typedef struct _DbResultSet DbResultSet; + +/** + * DbResultSet: (skip) + * @results: (element-type Db.Result): + * + * Has the information of a result. + **/ +struct _DbResultSet +{ + GSList * results; +}; + +GType db_result_set_get_type (); +DbResultSet * db_result_set_new (); +DbResult * db_result_set_take_nth (DbResultSet * obj, guint n); +DbResult * db_result_set_take_next (DbResultSet * obj); +DbResultSet * db_result_set_copy (const DbResultSet * obj); +void db_result_set_free (DbResultSet * obj); + +#endif \ No newline at end of file diff --git a/db/db-result.c b/db/db-result.c new file mode 100644 index 0000000..68ff801 --- /dev/null +++ b/db/db-result.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-result.h" + +/** + * SECTION: db-result + * @Short_description: + * @Title: DbResult + * @See_also: #DbResultSet + **/ +G_DEFINE_BOXED_TYPE (DbResult, db_result, db_result_copy, db_result_free); + +DbResult * db_result_new () +{ + DbResult * obj = g_new (DbResult, 1); + return obj; +} + +DbRow * db_result_get_row (const DbResult * obj, gint row_index) +{ + g_return_val_if_fail (obj, NULL); + g_return_val_if_fail (row_index >= 0 && row_index < obj->nrows, NULL); + + return g_ptr_array_index (obj->data, row_index); +} + +void db_result_remove_row (DbResult * obj, gint row_index) +{ + g_return_if_fail (obj); + g_return_if_fail (row_index >= 0 && row_index < obj->nrows); +} + +/** + * db_result_copy: + * @obj: a #DbResult + * + * Makes a copy of a #DbResult. + * + * Return value: a newly allocated #DbResult, with the same contents + **/ +DbResult * db_result_copy (const DbResult * obj) +{ + gint n; + DbResult * result; + + g_return_val_if_fail (obj, NULL); + + result = g_new (DbResult, 1); + result->nrows = obj->nrows; + result->ncols = obj->ncols; + result->column = g_new (DbColumn, obj->ncols); + result->data = g_ptr_array_new_full (obj->nrows + 15, + (GDestroyNotify) db_row_free); + + for (n = 0; n < obj->ncols; n++) + { + result->column[n].info = obj->column[n].info; + result->column[n].spec = gvn_param_spec_copy (obj->column[n].spec); + result->column[n].table = g_strdup (obj->column[n].table); + result->column[n].name = g_strdup (obj->column[n].name); + result->column[n].display = g_strdup (obj->column[n].display); + } + + for (n = 0; n < obj->nrows; n++) + g_ptr_array_add (result->data, + db_row_copy (g_ptr_array_index (obj->data, n))); + + return result; +} + +/** + * db_result_free: + * @obj: a #DbResult + * + * Frees a #DbResult. + **/ +void db_result_free (DbResult * obj) +{ + g_return_if_fail (obj); + + if (obj->data) + g_ptr_array_unref (obj->data); + + if (obj->column) + { + int i; + + for (i = 0; i < obj->ncols; i++) + { + DbColumn col = obj->column[i]; + gvn_param_spec_free (col.spec); + g_free (col.name); + g_free (col.display); + g_free (col.table); + } + + g_free (obj->column); + } + + g_free (obj); +} \ No newline at end of file diff --git a/db/db-result.h b/db/db-result.h new file mode 100644 index 0000000..08643d5 --- /dev/null +++ b/db/db-result.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_RESULT_H +#define DB_RESULT_H + +#include +#include "db-row.h" + +#define DB_TYPE_RESULT (db_result_get_type()) + +typedef struct _DbResult DbResult; +typedef struct _DbColumn DbColumn; + +/** + * DbResult: + * @nrows: Number of rows. + * @ncols: Number of columns. + * @data: Has a #GList. + * @column: Has a #DbColumn. + * + * Has the information of a row. + **/ +struct _DbResult +{ + gint nrows; + gint ncols; + GPtrArray * data; + DbColumn * column; +}; + +/** + * DbColumnInfo: + * @DB_COLUMN_PRI_KEY: Attribute primary Key. + * @DB_COLUMN_UNIQUE: Attribute unique. + * + * Saves the information about if it is primary key or unique. + **/ +typedef enum +{ + DB_COLUMN_PRI_KEY = 1 << 0 + ,DB_COLUMN_UNIQUE = 1 << 1 +} +DbColumnInfo; + +/** + * DbColumn: + * @info: A #DbColumnInfo + * @spec: A #GvnParamSpec + * @table: Name of the table + * @name: Name of the column + * @display: The name given to it in the query + * + * Has the information of a column. + **/ +struct _DbColumn +{ + DbColumnInfo info; + GvnParamSpec * spec; + gchar * table; + gchar * name; + gchar * display; +}; + +GType db_result_get_type (); +DbResult * db_result_new (); +DbRow * db_result_get_row (const DbResult * obj, gint row_index); +void db_result_remove_row (DbResult * obj, gint row_index); +DbResult * db_result_copy (const DbResult * obj); +void db_result_free (DbResult * obj); + +#endif \ No newline at end of file diff --git a/db/db-row.c b/db/db-row.c new file mode 100644 index 0000000..3ae722f --- /dev/null +++ b/db/db-row.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-row.h" + +/** + * SECTION: db-row + * @Short_description: a row from a #DbModel + * @Title: DbRow + * @See_also: #DbModel + * + * #DbRow represents a row in a #DbModel. + **/ +G_DEFINE_BOXED_TYPE (DbRow, db_row, db_row_copy, db_row_free); + +/** + * db_row_new: + * @len: the lenght of the #DbRow. + * @position: the position where the returned #DbRow will be placed. + * + * Sets a newly allocated #DbRow of lenght @len with position @postition. + * This new structure must be freed using db_row_free(). + * + * Return value: a new #DbRow + **/ +DbRow * db_row_new (gint len, gint position) +{ + DbRow * row = g_slice_new (DbRow); + row->value = g_new0 (GValue, len); + row->position = position; + row->len = len; + return row; +} + +/** + * db_row_free: + * @row: the row that you want to free. + * + * Frees a #DbRow of a #DbResult. + **/ +void db_row_free (DbRow * row) +{ + gint n; + + for (n = 0; n < row->len; n++) + g_value_unset (&row->value[n]); + + g_free (row->value); + g_slice_free (DbRow, row); +} + +/** + * db_row_copy: + * @row: a #DbRow + * + * Copies a #DbRow. + * + * Return value: a new copy of @row + **/ +DbRow * db_row_copy (const DbRow * row) +{ + gint n; + DbRow * new; + + g_return_val_if_fail (row, NULL); + + new = g_new (DbRow, 1); + new->len = row->len; + new->position = row->position; + new->value = g_new (GValue, new->len); + + for (n = 0; n < new->len; n++) + g_value_copy + (g_value_init (&new->value[n], G_VALUE_TYPE (&row->value)) + ,&row->value[n]); + + return new; +} + +/** + * db_row_get_value: + * @row: a #DbRow + * @index: the index of the value in @row + * + * Returns the value placed in the position @index of @row. + * + * Return value: a #GValue + **/ +const GValue * db_row_get_value (const DbRow * row, gint index) +{ + g_return_val_if_fail (row, NULL); + g_return_val_if_fail (index >= 0 && index < row->len, NULL); + + return &row->value[index]; +} + +/** + * db_row_get_position: + * @row: a #DbRow + * + * Returns the position where @row in the model it belongs to. + * + * Return value: the position of @row in its model or -1 if index is out of + **/ +gint db_row_get_position (const DbRow * row) +{ + g_return_val_if_fail (row, -1); + + return row->position; +} diff --git a/db/db-row.h b/db/db-row.h new file mode 100644 index 0000000..f63dc8a --- /dev/null +++ b/db/db-row.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_ROW_H +#define DB_ROW_H + +#include + +#define DB_TYPE_ROW (db_row_get_type()) + +typedef struct _DbRow DbRow; + +/** + * DbRow: + * @value: a #GValue + * @position: the position of the row in the DbModel + * @len: the number of fields + * + * Holds the data and information of a row. + **/ +struct _DbRow +{ + GValue * value; + gint position; + gint len; +}; + +GType db_row_get_type (); +DbRow * db_row_new (gint len, gint position); +void db_row_free (DbRow * row); +DbRow * db_row_copy (const DbRow * row); +const GValue * db_row_get_value (const DbRow * row, gint index); +gint db_row_get_position (const DbRow * row); + +#endif \ No newline at end of file diff --git a/db/db.h b/db/db.h new file mode 100644 index 0000000..4757877 --- /dev/null +++ b/db/db.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_H +#define DB_H + +#include +#include "db-iter.h" +#include "db-model.h" +#include "db-request.h" +#include "db-conn.h" +#include "db-row.h" +#include "db-iterator.h" +#include "db-param.h" +#include "db-plugin.h" +#include "db-calc.h" +#include "db-file-loader.h" + +#endif \ No newline at end of file diff --git a/db/db.pc.in b/db/db.pc.in new file mode 100644 index 0000000..e8081e3 --- /dev/null +++ b/db/db.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/hedera +includedir=@includedir@/hedera + +Name: Db +Description: Database Access Module for Hedera Library +Version: @VERSION@ +Requires: sql gvn glib-2.0 gobject-2.0 +Libs: -L${libdir} -ldb +Cflags: -I${includedir}/db diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..1cb12cd --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +libhedera (1.0-1) stable; urgency=low + + * Initial Release. + + -- Alejandro T. Colombini Gómez Wed, 02 May 2012 10:20:21 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..301160a --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +8 \ No newline at end of file diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..fb0d285 --- /dev/null +++ b/debian/control @@ -0,0 +1,73 @@ +Source: libhedera +Priority: extra +Maintainer: Alejandro T. Colombini Gómez +Build-Depends: debhelper (>= 8.0.0), autotools-dev, libgtk-3-dev, gtk-doc-tools, + valac, libgirepository1.0-dev, libgladeui-dev, libgdome2-dev, + libpq-dev, libmysqlclient-dev, ragel +Standards-Version: 3.9.3 +Section: libs +Homepage: http://www.verdnatura.es +Vcs-Svn: svn://www.verdnatura.es/hedera/trunk + +Package: hedera +Section: gnome +Architecture: amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, libhedera1 (= ${binary:Version}) +Suggests: vn-updater (>= 1.0) +Description: Database access and widget provider library (runtime) + The hedera library provides and lets you create widgets bound to SQL queries + and manages the different operations made over the data retrieved from these + queries. + . + The library also has a modular system to add and remove the different widgets + in run time. + . + This package contains the executable and data files. + +Package: libhedera-dev +Section: libdevel +Architecture: amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, libhedera1 (= ${binary:Version}) +Suggests: libgtk-3-doc, libgtk-3-dbg, devhelp, valac (>= 0.16) +Recommends: glade (>= 3.4), anjuta (>= 3.4) +Description: Database access and widget provider library (development) + The hedera library provides and lets you create widgets bound to SQL queries + and manages the different operations made over the data retrieved from these + queries. + . + The library also has a modular system to add and remove the different widgets + in run time. And it includes Glade integration, to design the programs in an + easiest, graphical way. + . + This package contains development files to compile C and Vala programs that + use the hedera library. It also contains the documentation and introspection + files. + +Package: libhedera1 +Section: libs +Architecture: amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, libgtk-3-0, libgtk-3-dev +Description: Database access and widget provider library (core) + The hedera library provides and lets you create widgets bound to SQL queries + and manages the different operations made over the data retrieved from these + queries. + . + The library also has a modular system to add and remove the different widgets + in run time. + . + This package contains the core of the library. It also provides the plugins to + connect a PostgreSQL or MySQL database. + +Package: libhedera-dbg +Section: debug +Architecture: amd64 +Depends: ${misc:Depends}, libhedera-dev (= ${binary:Version}) +Description: Database access and widget provider library (debug) + The hedera library provides and lets you create widgets bound to SQL queries + and manages the different operations made over the data retrieved from these + queries. + . + The library also has a modular system to add and remove the different widgets + in run time. + . + This package contains the debugging symbols for hedera. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..a416160 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,29 @@ +Format: http://dep.debian.net/deps/dep5 +Upstream-Name: libhedera +Source: svn://www.verdnatura.es/hedera + +Files: * +Copyright: 2011-2013 Juan Ferrer Toribio + 2013 The Hedera Team +License: GPL-3.0+ + +Files: debian/* +Copyright: 2013 Alejandro T. Colombini Gómez +License: GPL-3.0+ + +License: GPL-3.0+ + This package 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 . + . + On Debian systems, the complete text of the GNU General Public + License can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..50bd824 --- /dev/null +++ b/debian/docs @@ -0,0 +1,2 @@ +NEWS +README diff --git a/debian/hedera.dirs b/debian/hedera.dirs new file mode 100644 index 0000000..cd6f5f3 --- /dev/null +++ b/debian/hedera.dirs @@ -0,0 +1,8 @@ +usr/bin +usr/lib/hedera/module +usr/share/hedera/vn/image +usr/share/hedera/vn +usr/share/hedera/module +usr/share/hedera/module/sql +usr/share/applications +usr/share/ca-certificates/verdnatura.es \ No newline at end of file diff --git a/debian/hedera.install b/debian/hedera.install new file mode 100644 index 0000000..baa912c --- /dev/null +++ b/debian/hedera.install @@ -0,0 +1,7 @@ +usr/bin/* +usr/lib/hedera/module/*.so +usr/share/hedera/vn/image/* +usr/share/hedera/vn/* +usr/share/hedera/module/* +usr/share/applications/* +usr/share/ca-certificates/verdnatura.es/* \ No newline at end of file diff --git a/debian/hedera.links b/debian/hedera.links new file mode 100644 index 0000000..374fd1f --- /dev/null +++ b/debian/hedera.links @@ -0,0 +1 @@ +usr/share/ca-certificates/verdnatura.es/cacert.pem etc/ssl/certs/verdnatura.es.pem \ No newline at end of file diff --git a/debian/hedera.manpages b/debian/hedera.manpages new file mode 100644 index 0000000..a0ae287 --- /dev/null +++ b/debian/hedera.manpages @@ -0,0 +1 @@ +main/hedera.1 \ No newline at end of file diff --git a/debian/libhedera-dev.dirs b/debian/libhedera-dev.dirs new file mode 100644 index 0000000..8ee5a77 --- /dev/null +++ b/debian/libhedera-dev.dirs @@ -0,0 +1,15 @@ +usr/include/hedera +usr/include/hedera/gvn +usr/include/hedera/sql +usr/include/hedera/db +usr/include/hedera/vn +usr/include/hedera/vn/field +usr/include/hedera/vn/column +usr/lib/hedera +usr/lib/glade/modules +usr/lib/girepository-1.0 +usr/share/gir-1.0 +usr/share/glade/catalogs +usr/share/pkgconfig +usr/share/vala-0.20/vapi +usr/share/gtk-doc/html/hedera diff --git a/debian/libhedera-dev.install b/debian/libhedera-dev.install new file mode 100644 index 0000000..4938212 --- /dev/null +++ b/debian/libhedera-dev.install @@ -0,0 +1,25 @@ +usr/include/hedera/gvn/* +usr/include/hedera/sql/* +usr/include/hedera/db/* +usr/include/hedera/vn/* +usr/include/hedera/vn/field/* +usr/include/hedera/vn/column/* +usr/lib/hedera/libgvn.a +usr/lib/hedera/libgvn.so +usr/lib/hedera/libsql.a +usr/lib/hedera/libsql.so +usr/lib/hedera/libdb.a +usr/lib/hedera/libdb.so +usr/lib/hedera/libvn.a +usr/lib/hedera/libvn.so +usr/lib/hedera/libvnfield.a +usr/lib/hedera/libvnfield.so +usr/lib/hedera/libvncolumn.a +usr/lib/hedera/libvncolumn.so +usr/lib/glade/modules/libgladevn.so +usr/lib/girepository-1.0/* +usr/share/gir-1.0/* +usr/share/glade/catalogs/* +usr/share/pkgconfig/* +usr/share/vala-0.20/vapi/* +usr/share/gtk-doc/html/hedera/* diff --git a/debian/libhedera1.dirs b/debian/libhedera1.dirs new file mode 100644 index 0000000..6b32778 --- /dev/null +++ b/debian/libhedera1.dirs @@ -0,0 +1,6 @@ +usr/lib/hedera +usr/share/glib-2.0/schemas +usr/share/locale/es/LC_MESSAGES +usr/share/locale/ca/LC_MESSAGES +usr/share/locale/nl/LC_MESSAGES +usr/share/xml/hedera \ No newline at end of file diff --git a/debian/libhedera1.install b/debian/libhedera1.install new file mode 100644 index 0000000..201d4d2 --- /dev/null +++ b/debian/libhedera1.install @@ -0,0 +1,13 @@ +usr/lib/hedera/libgvn.so.* +usr/lib/hedera/libsql.so.* +usr/lib/hedera/libdb.so.* +usr/lib/hedera/plugin/pg/libdbpg.so +usr/lib/hedera/plugin/mysql/libdbmysql.so +usr/lib/hedera/libvn.so.* +usr/lib/hedera/libvnfield.so.* +usr/lib/hedera/libvncolumn.so.* +usr/share/glib-2.0/schemas/* +usr/share/locale/es/LC_MESSAGES/hedera.mo +usr/share/locale/ca/LC_MESSAGES/hedera.mo +usr/share/locale/nl/LC_MESSAGES/hedera.mo +usr/share/xml/hedera/* \ No newline at end of file diff --git a/debian/menu b/debian/menu new file mode 100644 index 0000000..44bbb2c --- /dev/null +++ b/debian/menu @@ -0,0 +1,5 @@ +?package(hedera):needs="X11" \ + section="Applications/Office" \ + title="Hedera" command="hedera" \ + icon16x16="/usr/share/hedera/vn/image/hedera16x16.xpm" \ + icon32x32="/usr/share/hedera/vn/image/hedera32x32.xpm" diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..ecf5253 --- /dev/null +++ b/debian/rules @@ -0,0 +1,21 @@ +#!/usr/bin/make -f + +#export DH_VERBOSE=1 +#export CFLAGS=" -O3 " + +%: + dh $@ --with autotools-dev --parallel --builddirectory=debian/build + +.PHONY: override_dh_auto_configure +override_dh_auto_configure: + dh_auto_configure -- \ + --enable-vala --enable-gtk-doc --enable-debug --enable-install + +.PHONY: override_dh_strip +override_dh_strip: + dh_strip --dbg-package=libhedera-dbg + +# Overriden to avoid lintian warnings about postinst/postrm +.PHONY: override_dh_makeshlibs +override_dh_makeshlibs: + dh_makeshlibs --noscripts diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/docs/Makefile.am b/docs/Makefile.am new file mode 100644 index 0000000..f948f36 --- /dev/null +++ b/docs/Makefile.am @@ -0,0 +1,3 @@ + +SUBDIRS = reference + diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am new file mode 100644 index 0000000..fc30d06 --- /dev/null +++ b/docs/reference/Makefile.am @@ -0,0 +1,3 @@ + +SUBDIRS = hedera + diff --git a/docs/reference/hedera/Makefile.am b/docs/reference/hedera/Makefile.am new file mode 100644 index 0000000..4b7b09c --- /dev/null +++ b/docs/reference/hedera/Makefile.am @@ -0,0 +1,99 @@ + +AUTOMAKE_OPTIONS = 1.6 + +DOC_MODULE = hedera + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.sgml + +# Directories containing the source code. +# gtk-doc will search all .c and .h files beneath these paths +# for inline comments documenting functions and macros. +DOC_SOURCE_DIR = \ + $(top_srcdir)/gvn \ + $(top_srcdir)/sql \ + $(top_srcdir)/db \ + $(top_srcdir)/vn \ + $(top_srcdir)/vn/field \ + $(top_srcdir)/vn/column + +MKDB_OPTIONS = --xml-mode --output-format=xml + +# Used for dependencies. The docs will be rebuilt if any of these change. +HFILE_GLOB = \ + $(top_srcdir)/gvn/*.h \ + $(top_srcdir)/sql/*.h \ + $(top_srcdir)/db/*.h \ + $(top_srcdir)/vn/*.h \ + $(top_srcdir)/vn/field/*.h \ + $(top_srcdir)/vn/column/*.h +CFILE_GLOB = \ + $(top_srcdir)/gvn/*.c \ + $(top_srcdir)/sql/*.c \ + $(top_srcdir)/db/*.c \ + $(top_srcdir)/vn/*.c \ + $(top_srcdir)/vn/field/*.c \ + $(top_srcdir)/vn/column/*.c + +# Header files or dirs to ignore when scanning. Use base file/dir names +IGNORE_HFILES = \ + plugin module parser + +# Images to copy into HTML directory. +HTML_IMAGES = \ + $(srcdir)/image/vn-calendar.png + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +content_files = \ + introduction.xml \ + configuring.xml \ + running.xml \ + adding-modules.xml \ + module-tutorial.xml \ + first-tutorial.xml + +# Extra options to supply to gtkdoc-fixref +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS = + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +expand_content_files = #first-tutorial.xml + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +GTKDOC_CFLAGS = -I$(top_srcdir) -I$(top_builddir) $(gtk_CFLAGS) +GTKDOC_LIBS = \ + $(gtk_LIBS) \ + $(top_builddir)/gvn/libgvn.la \ + $(top_builddir)/sql/libsql.la \ + $(top_builddir)/db/libdb.la \ + $(top_builddir)/vn/libvn.la \ + $(top_builddir)/vn/field/libvnfield.la \ + $(top_builddir)/vn/column/libvncolumn.la + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Comment this out if you want 'make check' to test you doc status +# and run some sanity checks +if ENABLE_GTK_DOC +TESTS_ENVIRONMENT = cd $(srcdir) && \ + DOC_MODULE = $(DOC_MODULE) DOC_MAIN_SGML_FILE = $(DOC_MAIN_SGML_FILE) \ + SRCDIR = $(abs_srcdir) BUILDDIR = $(abs_builddir) +#TESTS = $(GTKDOC_CHECK) +endif + +EXTRA_DIST += \ + introduction.xml \ + configuring.xml \ + running.xml \ + adding-modules.xml \ + module-tutorial.xml \ + first-tutorial.xml + +CLEANFILES += \ + hedera-scan.c \ + hedera.types \ + hedera-overrides.txt \ + hedera-sections.txt \ + hedera-decl-list.txt \ + hedera-decl.txt diff --git a/docs/reference/hedera/adding-modules.xml b/docs/reference/hedera/adding-modules.xml new file mode 100644 index 0000000..07b7d87 --- /dev/null +++ b/docs/reference/hedera/adding-modules.xml @@ -0,0 +1,69 @@ + + + + + Adding modules + 0 + Hedera Library + + + + Adding modules + + How to add your own modules + + + + + Previous stage + + Before you can add a module, you have to create it. To do so, + the Hedera Library ships with an Anjuta IDE project wizzard + that creates an empty module written in Vala, almost ready + to build . If you won't be using the Anjuta IDE, you'll + have to work your way to a built module. More on that will be + covered in the section about the module creation on the Tutorials. + + + + + Adding the module + + At this point, you should have a compiled module, this consisting + in a folder with the data (images, UI definitions, etc.) of the + module along with the dynamic library (a .so file) that the + Hedera execution environment (or Hedera application) will link + in runtime to load the module. It can also be the single + .so, due to that may be the module developer has coded + the UI creation or may be beacuse both the module and its data were + compiled in a single file. + + + To run the module in the hedera runtime you must set the environment + variable VN_MODULE_LIB_PATH to the full path to the module or + modules .so files (separated by :), and + VN_MODULE_DATA_PATH pointing to the .xml, + .glade, .ui and any + other data of the module, there is also an optional third variable, + VN_MODULE_QUERY_PATH that points to .sql + files. Once these variables are set you'll have to run + the Hedera application normally, and it will look for the + values on the variables and load the module. In the future there + will be another variable that when set to "yes" the module will + install and when set to "no" (or not set) the module will just be + used as now in the hedera runtime. Note that if you use the Anjuta + IDE with the project created by the wizzard, as you run in Anjuta + the variables will be automatically set pointing to your project + files. + + + If you want to install the module you have to install the + .so in /usr/lib/hedera/module and + the .xml and any other data to + /usr/share/hedera/module. + + + + diff --git a/docs/reference/hedera/configuring.xml b/docs/reference/hedera/configuring.xml new file mode 100644 index 0000000..80036a9 --- /dev/null +++ b/docs/reference/hedera/configuring.xml @@ -0,0 +1,87 @@ + + + + + Configuring + 0 + Hedera Library + + + + Configuring the Hedera Library + + How to configure the Hedera library before start using it + + + + + + This chapter covers how to configure the library from the tarball, + if you have received it packed in any other form, like a .deb + package, this section can be ignored, but you'll need to know how + to use the package you received. + + + + What do you need? + + The Hedera library has some dependencies, so to compile it you'll + need these to be satisfied. Depending on how many things you want + the library to do, you'll need more or less things to start using + it. + + + The main and unavoidable dependencies for the Hedera library + are GLib and GTK+. Also, depending of what type of database you will + be using, you will also need the public API of this database. The + goal of the library is to add more support for other free databases, + but right now, the only supported databases are PostgreSQL and + MySQL, so you will need to install the programming interface for at + least of of these. + + + If you want to generate this documentation with your own compilation + of the library, you'll also need to install GTK-Doc to do so. + + + + Compiling the library + + Supposing you've satisfied all of the existing dependencies, now + you'll need to compile the Hedera library. To do it you just have + to go to the root folder of the library and run the autogen.sh + script with the configure options of your choice (listed in the + README file of the distribution) e.g. './autogen.sh --enable-vala + --prefix=~/installdir' and then run 'make' to compile the library. + + + As we do, we highly recommend developers using the Hedera + library and also GTK+ or GLib, to use the Anjuta IDE, a very + useful and complete free developing environment with a great + support and integration for the GObject system and the GNU + build system. + + + + Vala + + The Hedera library supports Vala as programming language. If + you will be programming in Vala, you must install the Vala + compiler, valac. The only version of Vala supported right now is + the 0.16, plans are to be also compatible with newer versions + too. Note that to build the Vala bindings you'll also need the + vapigen tool, which normally installs with the Vala compiler. + + + + Installing + + When you have the library configured according to your needs, + you'll have to execute 'make install' with root privileges, and + the library directory tree will install in the folder specified + at configure time. + + + diff --git a/docs/reference/hedera/first-tutorial.xml b/docs/reference/hedera/first-tutorial.xml new file mode 100644 index 0000000..98beb26 --- /dev/null +++ b/docs/reference/hedera/first-tutorial.xml @@ -0,0 +1,27 @@ + + + + + First Tutorial + 0 + Hedera Library + + + + First simple tutorial + + A simple tutorial to get started + + + + + The first tutorial + + In this first tutorial you will learn how to create a simple + module with just one form using the Hedera library. + + + + diff --git a/docs/reference/hedera/hedera-docs.sgml b/docs/reference/hedera/hedera-docs.sgml new file mode 100644 index 0000000..7c596be --- /dev/null +++ b/docs/reference/hedera/hedera-docs.sgml @@ -0,0 +1,153 @@ + + +]> + + + Hedera Reference Manual + + This document is for the Hedera library, version 1.0. + The latest version of this documentation can be found on-line + here. + + + + + Overview + + + This part exposes the features of the Hedera library and + what you need to compile and use it the first time. + + + + + + + + + + Using Hedera library + + + Once you've installed it, you'll need to get the hang of it. + With this in mind, we've prepared some tutorials to do it. + + + + + + + + Hedera Reference + + + A quick reference guide of the classes and functions found + in each part of the Hedera library. + + + + Object Hierarchy + + + + GvnLib + + + + + + + + + SqlLib + + + + + + + + + + + + + + + + + + + + + + + DbLib + + + + + + + + + + + + + VnLib + + + + + + + + + + + + Fields + + + + + + + + + + + + Columns + + + + + + + + + + API Index + + + + + + diff --git a/docs/reference/hedera/image/vn-calendar.png b/docs/reference/hedera/image/vn-calendar.png new file mode 100644 index 0000000..60e8e23 Binary files /dev/null and b/docs/reference/hedera/image/vn-calendar.png differ diff --git a/docs/reference/hedera/introduction.xml b/docs/reference/hedera/introduction.xml new file mode 100644 index 0000000..6c70d8f --- /dev/null +++ b/docs/reference/hedera/introduction.xml @@ -0,0 +1,50 @@ + + + + + Introduction + 0 + Hedera Library + + + + Introduction to the Hedera library + + Presentation of the features of the Hedera Lib + + + + + The Hedera Library + + The Hedera library is a utility library that offers an easy + way to connect to a database and link the retrieved data to + GTK+ graphical interfaces. + + + It features a modular system to easily add new functionalities + to your applications with the minimum maintenance needed. This + system offers the developer the chance to use either GObject/C + or Vala to implement new modules and also comes with an easy + integration method to add them to the existing application in no + time. + + + Althought you can write your own applications, the library is + thought to use the shipped application and link the modules + to it, in order to ease the process. The linking of the + application and the modules will be described in the section + about adding new modules and an example of the process will be + shown in a tutorial. + + + The hedera library also comes with an Anjuta IDE project + wizzard to help on the creation of the Hedera Module project + and integrate it with the GNU Build System. As the motivation + of the Hedera Library Project was to use the library from Vala, + this project wizzard generates a Vala project. + + + \ No newline at end of file diff --git a/docs/reference/hedera/module-tutorial.xml b/docs/reference/hedera/module-tutorial.xml new file mode 100644 index 0000000..351c97d --- /dev/null +++ b/docs/reference/hedera/module-tutorial.xml @@ -0,0 +1,76 @@ + + + + + Creating a module + 0 + Hedera Library + + + + Creating a module + + Tutorial on how to create an Hedera Module + + + + + This section is a tutorial on how to create a new Hedera Module, + here you will learn about the basic programming to set up a + module, then you'll need to add it to the rest of the Hedera + execution environment, to know how to do it, read the chapter + "Adding modules" of the Hedera manual. + + + + The alternatives + + Before starting you have to decide if you're going to use the + Anjuta IDE or not. If you do so, you can use the project wizzard + to generate the tamplate of the project and the build system. If + you can't use the Anjuta IDE, you'll need to write your build + scripts in your preferred build environment. In this tutorial + you are supposed to use the Anjuta IDE, if you don't, the code + will be the same, so you can follow the next tutorials either way. + + + + Setting up the Anjuta project + + As said before, the Hedera Library comes with an Anjuta project + wizzard to help the easy creation of a new Hedera Module on Vala. + To include this wizzard to Anjuta 2.31 or grater you can run + Anjuta passing the path to the module file as an argument. + For previous versions of Anjuta you'll need to unzip the + hedera-mod.wiz.tgz file to the corresponding data directory named + anjuta/project, placed under ~/.local on most distributions. With + the former method Anjuta will do the same by itself. As the + dependancies of the hedera library go beyond these versions, it's + most likely that you have a version greater than the 2.31. + + + When Anjuta starts, it will show the wizzard dialog that will + guide you in the creation of the module. + + + After following the wizzard, you'll have a clean Hedera Module, + you can now verify you have everything you need to work by + building the module. As you try to build the project, Anjuta will + prompt you for the configuration options of the project, configure + the project without options (you can add options like --enable-debug + or others, that will be discussed later). If you can build you are + good to go with the next tutorials. Else you'll need to check the + errors prompted by the build system and fix them, probably + installing missing packages on your system. + + + + Setting up a GNU make project + + To set up a project using a simple GNU make script you'll need to + do some things that will be described here but are not written yet. + + + diff --git a/docs/reference/hedera/running.xml b/docs/reference/hedera/running.xml new file mode 100644 index 0000000..44de8cb --- /dev/null +++ b/docs/reference/hedera/running.xml @@ -0,0 +1,53 @@ + + + + + Running applications + 0 + Hedera Library + + + + Running Hedera applications + + How to run an Hedera application + + + + + Custom modules + + To run your own modules you need to install them in the + search directories of Hedera, these are ${libdir}/hedera/module + for the binary objects (.so files) and ${datadir}/hedera/module + for additional data, thus the GUI files and the configuration file + and also, optionally any additional data you want to add to your + module should be here. + + + It is also possible to put your files in any other path and tell + Hedera where to look for them. To do so, set the following + environment variables before executing Hedera: VN_MODULE_LIB_PATH, + the list of directories put in this variable will be used to look + for the binary files of the module; VN_MODULE_DATA_PATH, this list + of directories must hold the path to the data of the module. + + Example modules + + Once you've succesfully compiled the library you can try and + run some of the provided modules (some of wich you can write + by yourself following the tutorials included in this manual). + If the installation went as expected you'll just need to + run the Hedera executable. The executable will be in the + "bin" directory of the installation tree. If you installed the + library in a folder that is included in the PATH environment + variable, you'll just need to write 'hedera' in the command + line. The library will load the modules at runtime. The details + on how to tell the hedera execution environment where to look + for your modules are explained in + Adding Modules. + + + diff --git a/glade/Makefile.am b/glade/Makefile.am new file mode 100644 index 0000000..56b9ac0 --- /dev/null +++ b/glade/Makefile.am @@ -0,0 +1,23 @@ +include $(top_srcdir)/Makefile.decl + +gladevn_lib_LTLIBRARIES = libgladevn.la + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(gladeui_CFLAGS) +libgladevn_la_LIBADD = \ + $(top_builddir)/vn/libvn.la \ + $(gladeui_LIBS) +libgladevn_la_LDFLAGS = -avoid-version +libgladevn_la_SOURCES = \ + glade-vn.h \ + glade-vn.c \ + glade-vn-batch.c + +gladevn_data_DATA = vn.xml + +EXTRA_DIST = $(gladevn_data_DATA) + +install-data-hook: + rm -f $(DESTDIR)$(gladevn_libdir)/libgladevn.la + rm -f $(DESTDIR)$(gladevn_libdir)/libgladevn.a \ No newline at end of file diff --git a/glade/glade-vn-batch.c b/glade/glade-vn-batch.c new file mode 100644 index 0000000..409e8f3 --- /dev/null +++ b/glade/glade-vn-batch.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "glade-vn.h" + +gboolean glade_vn_batch_add_verify (GladeWidgetAdaptor * adaptor, + VnBatch * container, GObject * child, gboolean user_feedback) +{ + if (G_IS_OBJECT (child)) + return TRUE; + + if (user_feedback) + { + GladeWidgetAdaptor * object_adaptor = + glade_widget_adaptor_get_by_type (G_TYPE_OBJECT); + + glade_util_ui_message (glade_app_get_window () + ,GLADE_UI_INFO + ,NULL + ,"Only objects of type %s can be added to objects of type %s." + ,glade_widget_adaptor_get_title (object_adaptor) + ,glade_widget_adaptor_get_title (adaptor) + ); + } + + return FALSE; +} + +void glade_vn_batch_add_child (GladeWidgetAdaptor * adaptor, + VnBatch * group, GObject * child) +{ + if (G_IS_OBJECT (child)) + vn_batch_add (group, child); +} + +void glade_vn_batch_remove_child (GladeWidgetAdaptor * adaptor, + VnBatch * group, GObject * child) +{ + if (G_IS_OBJECT (child)) + vn_batch_remove (group, child); +} + +void glade_vn_batch_replace_child (GladeWidgetAdaptor * adaptor, + GObject * container, GObject * current, GObject * new) +{ + VnBatch * group = VN_BATCH (container); + glade_vn_batch_remove_child (adaptor, group, current); + glade_vn_batch_add_child (adaptor, group, new); +} + +GList * glade_vn_batch_get_children (GladeWidgetAdaptor * adaptor, + VnBatch * group) +{ + return vn_batch_get_objects (group); +} diff --git a/glade/glade-vn.c b/glade/glade-vn.c new file mode 100644 index 0000000..3f12661 --- /dev/null +++ b/glade/glade-vn.c @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "glade-vn.h" + +void glade_vn_init () +{ + g_message ("Hedera objects loaded!"); +} \ No newline at end of file diff --git a/glade/glade-vn.h b/glade/glade-vn.h new file mode 100644 index 0000000..0000d41 --- /dev/null +++ b/glade/glade-vn.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef GLADE_VN_H +#define GLADE_VN_H + +#include +#include + +void glade_vn_init (); + +#endif \ No newline at end of file diff --git a/glade/glade.sh b/glade/glade.sh new file mode 100755 index 0000000..ff01e02 --- /dev/null +++ b/glade/glade.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +MODULE_PATH=../build/glade/ + +export GLADE_MODULE_SEARCH_PATH="$MODULE_PATH/.libs" +export GLADE_CATALOG_SEARCH_PATH=. +glade diff --git a/glade/vn.xml b/glade/vn.xml new file mode 100644 index 0000000..cffdac7 --- /dev/null +++ b/glade/vn.xml @@ -0,0 +1,150 @@ + + + + + glade_vn_init + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + glade_vn_batch_add_verify + glade_vn_batch_add_child + glade_vn_batch_remove_child + glade_vn_batch_get_children + glade_vn_batch_replace_child + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gvn/Makefile.am b/gvn/Makefile.am new file mode 100644 index 0000000..719eba6 --- /dev/null +++ b/gvn/Makefile.am @@ -0,0 +1,63 @@ +include $(top_srcdir)/Makefile.decl + +gvn_lib_LTLIBRARIES = libgvn.la +gvn_include_HEADERS = \ + gvn.h \ + gvn-misc.h \ + gvn-null.h \ + gvn-time.h \ + gvn-param.h \ + gvn-param-spec.h \ + gvn-value.h + +AM_CPPFLAGS = $(glib_CFLAGS) +libgvn_la_LIBADD = $(glib_LIBS) +libgvn_la_SOURCES = \ + $(gvn_include_HEADERS) \ + gvn-misc.c \ + gvn-null.c \ + gvn-time.c \ + gvn-param.c \ + gvn-param-spec.c \ + gvn-value.c + +pkgconfig_DATA = gvn.pc + +EXTRA_DIST = gvn.pc.in + +DISTCLEANFILES = gvn.pc + +if ENABLE_VALA +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_SCANNER_ARGS = $(GIR_SCANNER_ARGS) + +introspection_sources = $(libgvn_la_SOURCES) + +Gvn-$(VERSION).gir: $(gvn_lib_LTLIBRARIES) + Gvn_@uVERSION@_gir_INCLUDES = GObject-2.0 + Gvn_@uVERSION@_gir_LIBS = $(gvn_lib_LTLIBRARIES) + Gvn_@uVERSION@_gir_FILES = $(introspection_sources) + Gvn_@uVERSION@_gir_EXPORT_PACKAGES = gvn + INTROSPECTION_GIRS = Gvn-$(VERSION).gir + +gir_DATA = $(INTROSPECTION_GIRS) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES = $(gir_DATA) $(typelib_DATA) + +$(vapis)/gvn.vapi: $(INTROSPECTION_GIRS) $(vapidata)/Gvn-$(VERSION).metadata + $(vapigen_v)$(VAPIGEN) -q \ + --directory $(vapis) \ + --metadatadir $(vapidata) \ + --library gvn \ + Gvn-$(VERSION).gir + +vapi_DATA = $(vapis)/gvn.vapi + +CLEANFILES += $(vapi_DATA) + +endif +endif diff --git a/gvn/gvn-misc.c b/gvn/gvn-misc.c new file mode 100644 index 0000000..4c65772 --- /dev/null +++ b/gvn/gvn-misc.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include +#include "gvn-misc.h" + +/** + * SECTION: gvn-misc + * @Short_description: Miscelaneous utility functions + * @Title: Miscelaneous utility functions + **/ + +const gchar * GVN_ABR_WDAY[] = {N_("Err"), + N_("Mon"), N_("Tue"), N_("Wed"), N_("Thu"), N_("Fri"), N_("Sat"), N_("Sun") +}; + +const gchar * GVN_ABR_MONTH[] = {N_("Err"), + N_("Jan"), N_("Feb"), N_("Mar"), N_("Apr"), N_("May"), N_("Jun"), + N_("Jul"), N_("Aug"), N_("Sep"), N_("Oct"), N_("Nov"), N_("Dec"), +}; + +/** + * GETTEXT_PACKAGE: (skip) + **/ + +/** + * gvn_object_warning: + * @instance: the object instance within which the warning was emitted + * @message: (allow-none): a string with the message to show or #NULL + * + * Prints a more verbose warning message than g_warning(), showing the type of + * the object @instance, the file and line from where it's been called and + * @message, if any. + **/ + +/** + * gvn_key_file_save: + * @obj: a #GKeyFile + * @file: the file name + * + * Creates an ini file with the contents of @obj. + **/ +void gvn_key_file_save (GKeyFile * obj, const gchar * file) +{ + gsize len; + gchar * aux; + + aux = g_key_file_to_data (obj, &len, NULL); + + if (len > 0) + g_file_set_contents (file, aux, len, NULL); + + g_free (aux); +} + +/** + * gvn_encode: + * @string: the string to be encoded. + * + * Encodes an string using base64. + * + * Return value: the encoded string. + **/ +gchar * gvn_encode (const gchar * string) +{ + if (string) + return g_base64_encode ((guchar *) string, strlen (string)); + else + return NULL; +} + +/** + * gvn_decode: + * @string: the string to be decoded. + * + * Decodes an string using base64. + * + * Return value: the decoded string. + **/ +gchar * gvn_decode (const gchar * string) +{ + if (string) + { + gsize len; + gchar * base64; + gchar * str; + + base64 = (gchar *) g_base64_decode (string, &len); + str = g_new (gchar, len + 1); + g_memmove (str, base64, len); + str[len] = '\0'; + g_free (base64); + + return str; + } + else + return NULL; +} diff --git a/gvn/gvn-misc.h b/gvn/gvn-misc.h new file mode 100644 index 0000000..a3dae15 --- /dev/null +++ b/gvn/gvn-misc.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef GVN_AUX_H +#define GVN_AUX_H + +#define GETTEXT_PACKAGE "hedera" +#include +#include + +void gvn_key_file_save (GKeyFile * obj, const gchar * file); +gchar * gvn_encode (const gchar * string); +gchar * gvn_decode (const gchar * string); + +extern const gchar * GVN_ABR_WDAY[]; +extern const gchar * GVN_ABR_MONTH[]; + +#endif diff --git a/gvn/gvn-null.c b/gvn/gvn-null.c new file mode 100644 index 0000000..df78bfe --- /dev/null +++ b/gvn/gvn-null.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "gvn-null.h" + +/** + * SECTION: gvn-null + * @Short_description: a boxed type to represent a null-valued #GValue + * @Title: GvnNull + **/ + +static GvnNull * gvn_null_copy (GvnNull * obj) +{ + return NULL; +} + +static void gvn_null_free (GvnNull * obj) {} + +G_DEFINE_BOXED_TYPE (GvnNull, gvn_null, gvn_null_copy, gvn_null_free); \ No newline at end of file diff --git a/gvn/gvn-null.h b/gvn/gvn-null.h new file mode 100644 index 0000000..c08cd6c --- /dev/null +++ b/gvn/gvn-null.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef GVN_NULL_H +#define GVN_NULL_H + +#include + +/** + * GVN_TYPE_NULL: + * + * The #GType for a boxed type holding a #GvnNull. + **/ +#define GVN_TYPE_NULL (gvn_null_get_type()) + +/** + * GvnNull: + * + * Primary struct to handle the NULL type. This struct has no members. + **/ +typedef struct _GvnNull GvnNull; + +struct _GvnNull; + +GType gvn_null_get_type (); + +#endif diff --git a/gvn/gvn-param-spec.c b/gvn/gvn-param-spec.c new file mode 100644 index 0000000..515813e --- /dev/null +++ b/gvn/gvn-param-spec.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "gvn-param-spec.h" +#include "gvn-misc.h" +#include "gvn-value.h" + +/** + * SECTION: gvn-param-spec + * @Short_description: + * @Title: GvnParamSpec + **/ + +/** + * gvn_param_spec_new: + * + * Creates a new #GvnParamSpec. + * + * Return value: the new #GvnParamSpec + **/ +GvnParamSpec * gvn_param_spec_new () +{ + return gvn_param_spec_new_with_attrs (G_TYPE_NONE, TRUE, TRUE, NULL); +} + +/** + * gvn_param_spec_new_with_attrs: + * @gtype: the #GType + * @editable: a %gboolean indicating whether it will be editable + * @null: a %gboolean indicating whether it can be null + * @def: the default #GValue + * + * Creates a new #GvnParamSpec. + * + * Return value: the new #GvnParamSpec + **/ +GvnParamSpec * gvn_param_spec_new_with_attrs (GType gtype, + gboolean editable, gboolean null, const GValue * def) +{ + g_return_val_if_fail (G_IS_VALUE (def) || !def, NULL); + + GvnParamSpec * obj = g_new (GvnParamSpec, 1); + obj->gtype = gtype; + obj->editable = editable; + obj->null = null; + obj->def = NULL; + gvn_param_spec_set_default (obj, def); + return obj; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * gvn_param_spec_get_gtype: + * @obj: a #GvnParamSpec + * + * Gets the type of @obj. + * + * Return value: the type + **/ +GType gvn_param_spec_get_gtype (const GvnParamSpec * obj) +{ + g_return_val_if_fail (obj, G_TYPE_INVALID); + + return obj->gtype; +} + +/** + * gvn_param_spec_set_gtype: + * @obj: a #GvnParamSpec + * @gtype: the #GType + **/ +void gvn_param_spec_set_gtype (GvnParamSpec * obj, GType gtype) +{ + g_return_if_fail (obj); + g_return_if_fail (G_TYPE_IS_VALUE_TYPE (gtype) || gtype == G_TYPE_NONE); + + obj->gtype = gtype; +} + +/** + * gvn_param_spec_get_editable: + * @obj: a #GvnParamSpec + **/ +gboolean gvn_param_spec_get_editable (const GvnParamSpec * obj) +{ + g_return_val_if_fail (obj, FALSE); + + return obj->editable; +} + +/** + * gvn_param_spec_set_editable: + * @obj: a #GvnParamSpec + * @editable: a %gboolean indicating whether it will be editable + * + * Sets if the value can be edited. + **/ +void gvn_param_spec_set_editable (GvnParamSpec * obj, gboolean editable) +{ + g_return_if_fail (obj); + + obj->editable = editable; +} + +/** + * gvn_param_spec_get_null: + * @obj: a #GvnParamSpec + **/ +gboolean gvn_param_spec_get_null (const GvnParamSpec * obj) +{ + g_return_val_if_fail (obj, FALSE); + + return obj->null; +} + +/** + * gvn_param_spec_set_null: + * @obj: a #GvnParamSpec + * @null: a %gboolean indicating whether it can be null + * + * Set if the value can be null. + **/ +void gvn_param_spec_set_null (GvnParamSpec * obj, gboolean null) +{ + g_return_if_fail (obj); + + obj->null = null; +} + +/** + * gvn_param_spec_get_default: + * @obj: a #GvnParamSpec + * + * Gets the default value. + * + * Return value: the #GValue or %NULL if it havent + **/ +const GValue * gvn_param_spec_get_default (const GvnParamSpec * obj) +{ + g_return_val_if_fail (obj, NULL); + + return obj->def; +} + +/** + * gvn_param_spec_set_default: + * @obj: a #GvnParamSpec + * @def: the default #GValue + * + * Sets the default value. + **/ +void gvn_param_spec_set_default (GvnParamSpec * obj, const GValue * def) +{ + g_return_if_fail (obj); + g_return_if_fail (G_IS_VALUE (def) || !def); + + if (obj->def) + { + g_value_unset (obj->def); + g_free (obj->def); + obj->def = NULL; + } + if (def) + { + GValue * value = g_new0 (GValue, 1); + g_value_init (value, G_VALUE_TYPE (def)); + g_value_copy (def, value); + obj->def = value; + } +} + +/** + * gvn_param_spec_merge: + * @obj: a #GvnParamSpec + * @merge: (allow-none): a #GvnParamSpec + * + * Merges the attributes of @merge into @obj remaining the most restrictive. + **/ +void gvn_param_spec_merge (GvnParamSpec * obj, const GvnParamSpec * merge) +{ + g_return_if_fail (obj); + + if (merge) + { + obj->editable = obj->editable && merge->editable; + obj->null = obj->null && merge->null; + + if (obj->gtype == G_TYPE_NONE) + obj->gtype = merge->gtype; + if (!obj->def && merge->def) + gvn_param_spec_set_default (obj, merge->def); + } +} + +/** + * gvn_param_spec_ccopy_value: + * @obj: a #GvnParamSpec + * @src: source value to be copied + * @dst: destination for the copied value + * + * Return value: %TRUE if the value has been copied, %FALSE otherwise + **/ +gboolean gvn_param_spec_ccopy_value (const GvnParamSpec * obj, const GValue * src, GValue * dst) +{ + g_return_val_if_fail (obj, FALSE); + g_return_val_if_fail (G_IS_VALUE (src), FALSE); + g_return_val_if_fail (G_IS_VALUE (dst), FALSE); + + if (!gvn_value_is_null (src) + && G_VALUE_TYPE (src) != obj->gtype + && obj->gtype != G_TYPE_NONE) + { + if (gvn_value_is_null (dst)) + { + g_value_unset (dst); + g_value_init (dst, obj->gtype); + } + + g_value_transform (src, dst); + } + else + return gvn_value_ccopy (src, dst); + + return TRUE; +} + +/** + * gvn_param_spec_validate: + * @obj: #GvnParamSpec object where @GValue wants to be validated + * @value: new value + * @err: (out) (allow-none): the return location for an allocated @GError, or + * %NULL to ignore errors. + * + * It checks if the value is valid to be saved into @obj + * + * Return value: gboolean + **/ +gboolean gvn_param_spec_validate (const GvnParamSpec * obj, const GValue * value, GError ** err) +{ + g_return_val_if_fail (obj, FALSE); + + gboolean is_null = gvn_value_is_null (value); + + if (!obj->editable) + g_set_error (err + ,GVN_PARAM_SPEC_LOG_DOMAIN + ,GVN_PARAM_SPEC_ERROR_NOT_EDITABLE + ,_("Param not editable")); + else if (is_null && !obj->null) + g_set_error (err + ,GVN_PARAM_SPEC_LOG_DOMAIN + ,GVN_PARAM_SPEC_ERROR_NOT_NULL + ,_("Param can't be NULL")); + else if (!is_null && obj->gtype != G_TYPE_NONE + && !g_value_type_transformable (obj->gtype, G_VALUE_TYPE (value))) + g_set_error (err + ,GVN_PARAM_SPEC_LOG_DOMAIN + ,GVN_PARAM_SPEC_ERROR_WRONG_TYPE + ,_("Incompatible type for this param")); + else + return TRUE; + + return FALSE; +} + +/** + * gvn_param_spec_unset: + * @obj: a #GvnParamSpec + **/ +void gvn_param_spec_unset (GvnParamSpec * obj) +{ + g_return_if_fail (obj); + + obj->null = TRUE; + obj->editable = TRUE; + obj->gtype = G_TYPE_NONE; + gvn_param_spec_set_default (obj, NULL); +} + +/** + * gvn_param_spec_copy: + * @obj: a #GvnParamSpec + * + * Makes a copy of @obj with the same attributes. + * + * Return value: the new created #GvnParamSpec + **/ +GvnParamSpec * gvn_param_spec_copy (const GvnParamSpec * obj) +{ + g_return_val_if_fail (obj, NULL); + + return gvn_param_spec_new_with_attrs (obj->gtype, + obj->editable, obj->null, obj->def); +} + +/** + * gvn_param_spec_free: + * @obj: a #GvnParamSpec + * + * Frees the memory used by @obj + **/ +void gvn_param_spec_free (GvnParamSpec * obj) +{ + g_return_if_fail (obj); + + gvn_param_spec_set_default (obj, NULL); + g_free (obj); +} + +G_DEFINE_BOXED_TYPE (GvnParamSpec, gvn_param_spec, gvn_param_spec_copy, gvn_param_spec_free); diff --git a/gvn/gvn-param-spec.h b/gvn/gvn-param-spec.h new file mode 100644 index 0000000..b93811d --- /dev/null +++ b/gvn/gvn-param-spec.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef GVN_PARAM_SPEC_H +#define GVN_PARAM_SPEC_H + +#include + +#define GVN_PARAM_SPEC_LOG_DOMAIN (g_quark_from_string ("GvnParamSpec")) + +/** + * GVN_TYPE_PARAM_SPEC: + * + * The #GType for a boxed type holding a #GvnParamSpec. + **/ +#define GVN_TYPE_PARAM_SPEC (gvn_param_spec_get_type ()) + +typedef struct _GvnParamSpec GvnParamSpec; + +/** + * GvnParamSpec: + * @gtype: Type of the param + * @editable: %TRUE if value is editable + * @null: %TRUE if value can be of type %GVN_TYPE_NULL + * @def: Default value + **/ +struct _GvnParamSpec +{ + GType gtype; + gboolean editable; + gboolean null; + GValue * def; +}; + +/** + * GvnParamError: + * @GVN_PARAM_SPEC_ERROR_NOT_EDITABLE: The value can not be edited + * @GVN_PARAM_SPEC_ERROR_NOT_NULL: The value can not be NULL + * @GVN_PARAM_SPEC_ERROR_WRONG_TYPE: The value have a wrong type + **/ +typedef enum +{ + GVN_PARAM_SPEC_ERROR_NOT_EDITABLE = 1 + ,GVN_PARAM_SPEC_ERROR_NOT_NULL + ,GVN_PARAM_SPEC_ERROR_WRONG_TYPE +} +GvnParamError; + +GType gvn_param_spec_get_type (); +GvnParamSpec * gvn_param_spec_new (); +GvnParamSpec * gvn_param_spec_new_with_attrs (GType gtype, gboolean editable, gboolean null, const GValue * def); +GType gvn_param_spec_get_gtype (const GvnParamSpec * obj); +void gvn_param_spec_set_gtype (GvnParamSpec * obj, GType gtype); +gboolean gvn_param_spec_get_editable (const GvnParamSpec * obj); +void gvn_param_spec_set_editable (GvnParamSpec * obj, gboolean editable); +gboolean gvn_param_spec_get_null (const GvnParamSpec * obj); +void gvn_param_spec_set_null (GvnParamSpec * obj, gboolean null); +const GValue * gvn_param_spec_get_default (const GvnParamSpec * obj); +void gvn_param_spec_set_default (GvnParamSpec * obj, const GValue * def); +void gvn_param_spec_merge (GvnParamSpec * obj, const GvnParamSpec * merge); +gboolean gvn_param_spec_ccopy_value (const GvnParamSpec * obj, const GValue * src, GValue * dst); +gboolean gvn_param_spec_validate (const GvnParamSpec * obj, const GValue * value, GError ** err); +void gvn_param_spec_unset (GvnParamSpec * obj); +GvnParamSpec * gvn_param_spec_copy (const GvnParamSpec * obj); +void gvn_param_spec_free (GvnParamSpec * obj); + +#endif \ No newline at end of file diff --git a/gvn/gvn-param.c b/gvn/gvn-param.c new file mode 100644 index 0000000..94e92fd --- /dev/null +++ b/gvn/gvn-param.c @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "gvn-misc.h" +#include "gvn-param.h" +#include "gvn-value.h" + +/** + * SECTION: gvn-param + * @Short_description: + * @Title: GvnParam + **/ +G_DEFINE_TYPE (GvnParam, gvn_param, G_TYPE_INITIALLY_UNOWNED); + +enum { + VALUE_CHANGED + ,SPEC_CHANGED + ,STATUS_CHANGED + ,LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +/** + * gvn_param_new: + * + * Creates a new #GvnParam. + * + * Return value: the new #GvnParam + **/ +GvnParam * gvn_param_new () +{ + return g_object_new (GVN_TYPE_PARAM, NULL); +} + +/** + * gvn_param_new_with_spec: + * @spec: the spec of param. + * + * Creates a new #GvnParam. + * + * Return value: the new #GvnParam + **/ +GvnParam * gvn_param_new_with_spec (const GvnParamSpec * spec) +{ + return g_object_new (GVN_TYPE_PARAM, "spec", spec, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void gvn_param_set_status (GvnParam * obj, GvnParamStatus status) +{ + obj->status = status; + g_signal_emit (obj, signals[STATUS_CHANGED], 0, status); +} + +static void gvn_param_set_error (GvnParam * obj, GError * error) +{ + g_clear_error (&obj->error); + obj->error = error; + gvn_param_set_status (obj, GVN_PARAM_STATUS_ERROR); +} + +static void gvn_param_set_spec (GvnParam * obj, const GvnParamSpec * spec) +{ + GSList * n; + + if (obj->mode == GVN_PARAM_MASTER) + for (n = obj->slaves; n; n = n->next) + gvn_param_spec_merge (GVN_PARAM (n->data)->spec, spec); + + gvn_param_spec_unset (obj->spec); + gvn_param_spec_merge (obj->spec, spec); + g_signal_emit (obj, signals[SPEC_CHANGED], 0); +} + +static void gvn_param_put_value (GvnParam * obj, const GValue * value) +{ + if (gvn_param_spec_ccopy_value (obj->spec, value, obj->value)) + { + GError * tmp_err = NULL; + + if (obj->mode == GVN_PARAM_SLAVE) + { + if (!gvn_param_set_value (obj->master, obj->value, &tmp_err)) + gvn_param_set_error (obj, tmp_err); + } + else if (obj->mode == GVN_PARAM_MASTER) + { + GSList * n; + + for (n = obj->slaves; n; n = n->next) + if (!gvn_param_set_value (n->data, obj->value, &tmp_err)) + gvn_param_set_error (n->data, tmp_err); + } + + gvn_param_value_changed (obj); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * gvn_param_get_value: + * @param: a #GvnParam where be took the value + * + * Gets the value of param. + * + * Return value: (transfer none): the #GValue + **/ +const GValue * gvn_param_get_value (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), NULL); + return obj->value; +} + +/** + * gvn_param_set_value: + * @param: #GvnParam object where @value wants to be validated. + * @value: new value. + * @err: (out) (allow-none): the return location for an allocated @GError, or + * NULL to ignore errors. + * + * Sets @value into @param. + * + * Return value: %TRUE if assigment is valid, %FALSE otherwise. + **/ +gboolean gvn_param_set_value (GvnParam * obj, const GValue * value, GError ** err) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), FALSE); + g_return_val_if_fail (G_IS_VALUE (value), FALSE); + + if (gvn_param_spec_validate (obj->spec, value, err)) + { + gvn_param_put_value (obj, value); + return TRUE; + } + + return FALSE; +} + +/** + * gvn_param_get_spec: + * @param: #GvnParam object + * + * Gets the #GvnParamSpec of @obj + * + * Return value: (transfer none): the #GvnParamSpec + **/ +const GvnParamSpec * gvn_param_get_spec (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), NULL); + + return obj->spec; +} + +/** + * gvn_param_get_master: + * @sparam: a #GvnParam where be took the value + * + * Gets the master param of @obj + * + * Return value: (transfer none): the master #GvnParam or %NULL if it haven't. + **/ +GvnParam * gvn_param_get_master (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), NULL); + + return obj->master; +} + +/** + * gvn_param_bind: + * @src: #GvnParam object + * @dst: #GvnParam master + * + * Sets @dst as a master of @src + **/ +void gvn_param_set_master (GvnParam * obj, GvnParam * master) +{ + g_return_if_fail (GVN_IS_PARAM (obj)); + g_return_if_fail (GVN_IS_PARAM (master) || !master); + g_return_if_fail (obj->mode != GVN_PARAM_MASTER); + g_return_if_fail (obj->mode != GVN_PARAM_SLAVE || !master); + g_return_if_fail (!master || master->mode != GVN_PARAM_SLAVE); + + if (master) + { + obj->master = g_object_ref_sink (master); + obj->mode = GVN_PARAM_SLAVE; + + master->slaves = g_slist_prepend (master->slaves, obj); + master->mode = GVN_PARAM_MASTER; + } + else + { + if (obj->mode == GVN_PARAM_SLAVE) + { + GSList ** slaves = &obj->master->slaves; + + if (!(*slaves = g_slist_remove (*slaves, obj))) + master->mode = GVN_PARAM_FREE; + } + + g_clear_object (&obj->master); + obj->mode = GVN_PARAM_FREE; + } +} + +/** + * gvn_param_get_status: + * @obj: #GvnParam object + * + * Gets the status of @obj + * + * Return value: the current status of the param. + **/ +GvnParamStatus gvn_param_get_status (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), GVN_PARAM_STATUS_OK); + + return obj->status; +} + +/** + * gvn_param_get_error: + * @obj: #GvnParam object + * + * Gets the error of @obj + * + * Return value: the current error of the param. + **/ +const GError * gvn_param_get_error (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), NULL); + + return obj->error; +} + +/** + * gvn_param_get_null: + * @param: the #GvnParam + **/ +gboolean gvn_param_get_null (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), FALSE); + + return gvn_param_spec_get_null (obj->spec); +} + +/** + * gvn_param_get_editable: + * @param: the #GvnParam + **/ +gboolean gvn_param_get_editable (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), FALSE); + + return gvn_param_spec_get_editable (obj->spec); +} + +/** + * gvn_param_get_default: + * @obj: the #GvnParam + **/ +const GValue * gvn_param_get_default (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), NULL); + + return gvn_param_spec_get_default (obj->spec); +} + +/** + * gvn_param_set_to_default: + * @obj: the #GvnParam + **/ +void gvn_param_set_to_default (GvnParam * obj) +{ + g_return_if_fail (GVN_IS_PARAM (obj)); + + gvn_param_set_value (obj, + gvn_param_spec_get_default (obj->spec), NULL); +} + +/** + * gvn_param_value_changed: + * @obj: a #GvnParam + * + * Emits the "value-changed" signal for that param. + **/ +void gvn_param_value_changed (GvnParam * obj) +{ + g_signal_emit (obj, signals[VALUE_CHANGED], 0, obj->value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_VALUE = 1 + ,PROP_MASTER + ,PROP_MODE + ,PROP_STATUS + ,PROP_SPEC + ,PROP_GTYPE + ,PROP_EDITABLE + ,PROP_NULL + ,PROP_DEFAULT +}; + +static void gvn_param_set_property (GvnParam * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_VALUE: + gvn_param_set_value (obj, g_value_get_boxed (value), NULL); + break; + case PROP_MASTER: + gvn_param_set_master (obj, g_value_get_object (value)); + break; + case PROP_SPEC: + gvn_param_spec_merge (obj->spec, g_value_get_boxed (value)); + break; + case PROP_GTYPE: + gvn_param_spec_set_gtype (obj->spec, g_value_get_gtype (value)); + break; + case PROP_EDITABLE: + gvn_param_spec_set_editable (obj->spec, g_value_get_boolean (value)); + break; + case PROP_NULL: + gvn_param_spec_set_null (obj->spec, g_value_get_boolean (value)); + break; + case PROP_DEFAULT: + gvn_param_spec_set_default (obj->spec, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void gvn_param_get_property (GvnParam * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_VALUE: + g_value_set_boxed (value, obj->value); + break; + case PROP_MASTER: + g_value_set_object (value, obj->master); + break; + case PROP_MODE: + g_value_set_int (value, obj->mode); + break; + case PROP_STATUS: + g_value_set_boolean (value, obj->status); + break; + case PROP_SPEC: + g_value_set_boxed (value, obj->spec); + break; + case PROP_GTYPE: + g_value_set_gtype (value, gvn_param_spec_get_gtype (obj->spec)); + break; + case PROP_EDITABLE: + g_value_set_boolean (value, gvn_param_spec_get_editable (obj->spec)); + break; + case PROP_NULL: + g_value_set_boolean (value, gvn_param_spec_get_null (obj->spec)); + break; + case PROP_DEFAULT: + g_value_set_boxed (value, gvn_param_spec_get_default (obj->spec)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void gvn_param_init (GvnParam * obj) +{ + obj->error = NULL; + obj->master = NULL; + obj->slaves = NULL; + obj->mode = GVN_PARAM_FREE; + obj->status = GVN_PARAM_STATUS_OK; + obj->spec = gvn_param_spec_new (); + + obj->value = g_new0 (GValue, 1); + g_value_init (obj->value, GVN_TYPE_NULL); +} + +static void gvn_param_finalize (GvnParam * obj) +{ + gvn_param_set_master (obj, NULL); + gvn_param_spec_free (obj->spec); + g_clear_error (&obj->error); + g_value_unset (obj->value); + g_free (obj->value); + G_OBJECT_CLASS (gvn_param_parent_class)->finalize (G_OBJECT (obj)); +} + +static void gvn_param_class_init (GvnParamClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) gvn_param_finalize; + k->set_property = (GObjectSetPropertyFunc) gvn_param_set_property; + k->get_property = (GObjectGetPropertyFunc) gvn_param_get_property; + klass->put_value = gvn_param_put_value; + klass->set_spec = gvn_param_set_spec; + klass->set_status = gvn_param_set_status; + klass->set_error = gvn_param_set_error; + + signals[VALUE_CHANGED] = g_signal_new ("value-changed", + GVN_TYPE_PARAM, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, G_TYPE_VALUE + ); + signals[SPEC_CHANGED] = g_signal_new ("spec-changed", + GVN_TYPE_PARAM, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + signals[STATUS_CHANGED] = g_signal_new ("status-changed", + GVN_TYPE_PARAM, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + + g_object_class_install_property (k, PROP_VALUE, + g_param_spec_boxed ("value" + ,_("Value") + ,_("The value of the param") + ,G_TYPE_VALUE + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_MASTER, + g_param_spec_object ("master" + ,_("Master") + ,_("The master GvnParam of this parameter") + ,GVN_TYPE_PARAM + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_MODE, + g_param_spec_uint ("mode" + ,_("Mode") + ,_("The mode of the parameter") + ,0 ,256 ,GVN_PARAM_FREE + ,G_PARAM_READABLE + )); + g_object_class_install_property (k, PROP_STATUS, + g_param_spec_uint ("status" + ,_("Status") + ,_("The current status of the parameter") + ,0 ,256 ,GVN_PARAM_STATUS_OK + ,G_PARAM_READABLE + )); + g_object_class_install_property (k, PROP_SPEC, + g_param_spec_boxed ("spec" + ,_("Spec") + ,_("The spec of the parameter") + ,GVN_TYPE_PARAM_SPEC + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_GTYPE, + g_param_spec_gtype ("gtype" + ,_("Glib Type") + ,_("The type of the value") + ,G_TYPE_NONE + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_EDITABLE, + g_param_spec_boolean ("editable" + ,_("Editable") + ,_("Whether the param value can be modified") + ,TRUE + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_NULL, + g_param_spec_boolean ("null" + ,_("Null") + ,_("Whether the param value can be of type GVN_TYPE_NULL") + ,TRUE + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_DEFAULT, + g_param_spec_boxed ("default" + ,_("Default Value") + ,_("The default value") + ,G_TYPE_VALUE + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); +} diff --git a/gvn/gvn-param.h b/gvn/gvn-param.h new file mode 100644 index 0000000..d36fa34 --- /dev/null +++ b/gvn/gvn-param.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef GVN_PARAM_H +#define GVN_PARAM_H + +#include +#include "gvn-param-spec.h" + +#define GVN_PARAM_LOG_DOMAIN (g_quark_from_string ("GvnParam")) + +#define GVN_TYPE_PARAM (gvn_param_get_type ()) +#define GVN_PARAM(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GVN_TYPE_PARAM, GvnParam)) +#define GVN_IS_PARAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GVN_TYPE_PARAM)) +#define GVN_PARAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, GVN_TYPE_PARAM, GvnParamClass)) + +typedef struct _GvnParam GvnParam; +typedef struct _GvnParamClass GvnParamClass; + +/** + * GvnParamMode: + * @GVN_PARAM_FREE: It is not set. + * @GVN_PARAM_SLAVE: It is set as slave. + * @GVN_PARAM_MASTER: It is set as master. + * + * The mode in which is Binded. + **/ +typedef enum +{ + GVN_PARAM_FREE + ,GVN_PARAM_SLAVE + ,GVN_PARAM_MASTER +} +GvnParamMode; + +/** + * GvnParamStatus: + * @GVN_PARAM_STATUS_OK: The parameter is ready. + * @GVN_PARAM_STATUS_BUSY: The parameter is busy. + * @GVN_PARAM_STATUS_ERROR: The parameter value is incompatible to its spec. + * + * The status of the param. + **/ +typedef enum +{ + GVN_PARAM_STATUS_OK + ,GVN_PARAM_STATUS_BUSY + ,GVN_PARAM_STATUS_ERROR +} +GvnParamStatus; + +/** + * GvnParam: + * @slaves: (element-type Gvn.Param): + **/ +struct _GvnParam +{ + GInitiallyUnowned parent; + GValue * value; + GError * error; + GvnParamStatus status; + GvnParamSpec * spec; + GvnParamMode mode; + GvnParam * master; + GSList * slaves; +}; + +struct _GvnParamClass +{ + /* */ + GInitiallyUnownedClass parent; + void (* put_value) (GvnParam * obj, const GValue * value); + void (* set_spec) (GvnParam * obj, const GvnParamSpec * spec); + void (* set_status) (GvnParam * obj, GvnParamStatus status); + void (* set_error) (GvnParam * obj, GError * error); +}; + +GType gvn_param_get_type (); +GvnParam * gvn_param_new (); +GvnParam * gvn_param_new_with_spec (const GvnParamSpec * spec); +const GValue * gvn_param_get_value (GvnParam * param); +gboolean gvn_param_set_value (GvnParam * param, const GValue * value, GError ** err); +const GvnParamSpec * gvn_param_get_spec (GvnParam * param); +GType gvn_param_get_gtype (GvnParam * param); +gboolean gvn_param_get_null (GvnParam * param); +gboolean gvn_param_get_editable (GvnParam * param); +const GValue * gvn_param_get_default (GvnParam * obj); +void gvn_param_set_to_default (GvnParam * obj); +GvnParam * gvn_param_get_master (GvnParam * sparam); +void gvn_param_set_master (GvnParam * sparam, GvnParam * dparam); +void gvn_param_value_changed (GvnParam * obj); +GvnParamStatus gvn_param_get_status (GvnParam * obj); +const GError * gvn_param_get_error (GvnParam * obj); + +#endif \ No newline at end of file diff --git a/gvn/gvn-time.c b/gvn/gvn-time.c new file mode 100644 index 0000000..36cb0fd --- /dev/null +++ b/gvn/gvn-time.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "gvn-time.h" + +/** + * SECTION: gvn-time + * @Short_description: a boxed type to represent time with optional time-zone + * @Title: GvnTime + * + * #GvnTime is a boxed type to manage time of a day without specifying the date, + * just hours, minutes, seconds and the time-zone. + **/ +G_DEFINE_BOXED_TYPE (GvnTime, gvn_time, gvn_time_copy, gvn_time_free); + +/** + * gvn_time_copy: + * @obj: a #GvnTime + * + * Copies @obj to a new #GvnTime. + * + * Return value: a new #GvnTime + **/ +GvnTime * gvn_time_copy (GvnTime * obj) +{ + g_return_val_if_fail (obj, NULL); + + return gvn_time_new (obj->hour, obj->min, obj->sec, obj->tz); +} + +/** + * gvn_time_free: + * @obj: a #GvnTime + * + * Releases the memory used by @obj. + **/ +void gvn_time_free (GvnTime * obj) +{ + g_return_if_fail (obj); + g_free (obj); +} + +/** + * gvn_time_new: + * @h: the hour, from 0 to 23 + * @m: the minutes, from 0 to 59 + * @s: the seconds, from 0 to 59 + * @tz: the time-zone, from -12 to 12 + * + * Creates a new #GvnTime with the given parameters, if any parameter is out of + * range, that parameter will be put to 0 instead. + **/ +GvnTime * gvn_time_new (guint h, guint m, guint s, gint tz) +{ + GvnTime * time = g_new (GvnTime, 1); + time->hour = h > 24 ? 0 : h; + time->min = m > 59 ? 0 : m; + time->sec = s > 59 ? 0 : s; + time->tz = tz < -12 || tz > 12 ? 0 : tz; + + return time; +} + +/** + * gvn_time_to_string: + * @obj: a #GvnTime + * @tz: whether or not the time-zone will be used + * + * Returns a newly allocated string containing the time set by @obj. By passing + * %FALSE in @tz, the time-zone is omited from the string. The format is + * hh:mm:ss±tz if @tz is %TRUE and hh:mm:ss otherwise. The string must be freed. + * + * Return value: a string containing the time + **/ +gchar * gvn_time_to_string (GvnTime * obj, gboolean tz) +{ + g_return_val_if_fail (obj, NULL); + if (tz) + return g_strdup_printf ("%d:%d:%d%s%d", obj->hour, obj->min, obj->sec, + obj->tz > 0 ? "+" : "-", obj->tz < 0 ? obj->tz * -1 : obj->tz); + else + return g_strdup_printf ("%d:%d:%d", obj->hour, obj->min, obj->sec); +} diff --git a/gvn/gvn-time.h b/gvn/gvn-time.h new file mode 100644 index 0000000..8cadb5c --- /dev/null +++ b/gvn/gvn-time.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef GVN_TIME_H +#define GVN_TIME_H + +#include + +/** + * GVN_TYPE_TIME: + * + * The #GType for a boxed type holding a #GvnTime. + **/ +#define GVN_TYPE_TIME (gvn_time_get_type()) + +/** + * GvnTime: + * @hour: the hour, from 0 to 23 + * @min: the minutes, from 0 to 59 + * @sec: the seconds, from 0 to 59 + * @tz: the time-zone, from -12 to 12 + * + * Primary struct to handle the TIME type. + **/ +typedef struct _GvnTime GvnTime; + +struct _GvnTime +{ + guint hour; + guint min; + guint sec; + gint tz; +}; + +GType gvn_time_get_type (); + +GvnTime * gvn_time_copy (GvnTime * obj); +void gvn_time_free (GvnTime * obj); +GvnTime * gvn_time_new (guint h, guint m, guint s, gint tz); +gchar * gvn_time_to_string (GvnTime * obj, gboolean tz); + +#endif diff --git a/gvn/gvn-value.c b/gvn/gvn-value.c new file mode 100644 index 0000000..ffe9db2 --- /dev/null +++ b/gvn/gvn-value.c @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include +#include +#include "gvn-value.h" +#include "gvn-misc.h" + +/** + * SECTION: gvn-value + * @Short_description: additional value manipulation functions + * @Title: GvnValue + * @See_also: #DbForm, #DbConn + * + * This functions are intended to expand or ease the use of the original GObject + * library #GValue utilites. + **/ + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void gvn_value_new_from_string_real (GValue * value, GType type, const gchar * string, gsize len) +{ + switch (type) + { + case G_TYPE_BOOLEAN: + g_value_set_boolean (value, atoi (string)); + break; + case G_TYPE_CHAR: + g_value_set_schar (value, atoi (string)); + break; + case G_TYPE_INT: + g_value_set_int (value, atoi (string)); + break; + case G_TYPE_UINT: + g_value_set_int (value, (guint) atoi (string)); + break; + case G_TYPE_LONG: + g_value_set_long (value, g_ascii_strtoll (string, NULL, 0)); + break; + case G_TYPE_ULONG: + g_value_set_ulong (value, g_ascii_strtoull (string, NULL, 0)); + break; + case G_TYPE_FLOAT: + g_value_set_float (value, atof (string)); + break; + case G_TYPE_DOUBLE: + g_value_set_double (value, g_ascii_strtod (string, NULL)); + break; + case G_TYPE_STRING: + g_value_set_string (value, string); + break; + default: + { + if (len == -1) + len = strlen (string); + + if (type == G_TYPE_DATE) + { + if (len >= 10) + { + GDate * date = g_date_new_dmy ( + atoi (string + 8) + ,atoi (string + 5) + ,atoi (string) + ); + g_value_take_boxed (value, date); + } + else + g_warning ("Gvn: Can't transform string to GDate"); + } + else if (type == G_TYPE_DATE_TIME) + { + if (len >= 19) + { + GDateTime * date_time = g_date_time_new_local ( + atoi (string) + ,atoi (string + 5) + ,atoi (string + 8) + ,atoi (string + 11) + ,atoi (string + 14) + ,atoi (string + 17) + ); + g_value_take_boxed (value, date_time); + } + else + g_warning ("Gvn: Can't transform string to GDateTime"); + } + else if (type == G_TYPE_BYTES) + { + if (len > 0) + g_value_take_boxed (value, g_bytes_new (string, len)); + else + g_warning ("Gvn: Can't transform string to GBytes"); + } + else if (type != GVN_TYPE_NULL) + g_return_if_reached (); + } + } +} + +static void gvn_value_transform_string_to_any (const GValue * src, GValue * dst) +{ + gvn_value_new_from_string_real (dst, + G_VALUE_TYPE (dst), g_value_get_string (src), -1); +} + +static void gvn_value_transform_null_to_any (const GValue * src, GValue * dst) {} + +static void gvn_value_transform_any_to_null (const GValue * src, GValue * dst) +{ + gvn_value_set_null (dst); +} + +static void gvn_value_transform_date_to_string (const GValue * src, GValue * dst) +{ + GDate * date = g_value_get_boxed (src); + + if (date) + { + g_value_take_string (dst, g_strdup_printf ("%04u-%02u-%02u" + ,g_date_get_year (date) + ,g_date_get_month (date) + ,g_date_get_day (date) + )); + } + else + g_value_take_string (dst, NULL); +} + +static void gvn_value_transform_date_time_to_string (const GValue * src, GValue * dst) +{ + gpointer date = g_value_get_boxed (src); + + if (date) + g_value_take_string (dst, g_date_time_format (date, "%Y-%m-%d %T")); + else + g_value_take_string (dst, NULL); +} + +static void gvn_value_transform_date_time_to_date (const GValue * src, GValue * dst) +{ + GDateTime * date_time = g_value_get_boxed (src); + + if (date_time) + { + GDate * date = g_date_new_dmy ( + g_date_time_get_day_of_month (date_time) + ,g_date_time_get_month (date_time) + ,g_date_time_get_year (date_time) + ); + g_value_take_boxed (dst, date); + } + else + g_value_take_boxed (dst, NULL); +} + +static void gvn_value_transform_date_to_date_time (const GValue * src, GValue * dst) +{ + GDate * date = g_value_get_boxed (src); + + if (date) + { + GDateTime * date_time = g_date_time_new_utc ( + g_date_get_year (date) + ,g_date_get_month (date) + ,g_date_get_day (date) + ,0 + ,0 + ,0.0 + ); + g_value_take_boxed (dst, date_time); + } + else + g_value_take_boxed (dst, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * gvn_value_new: + * @type: the type of new #GValue + * + * Creates a new #GValue + * + * Return value: a #GValue. + **/ +GValue * gvn_value_new (GType type) +{ + GValue * value = g_new0 (GValue, 1); + return g_value_init (value, type); +} + +/** + * gvn_value_is_null: + * @value: the value to be checked + * + * Checks if a @value is of type GVN_TYPE_NULL + * + * Return value: %TRUE if its NULL, %FALSE otherwise. + **/ + +/** + * gvn_value_set_null: + * @value: the value to be nulled + * + * Checks if a @value is of type GVN_TYPE_NULL, if not, sets it. + **/ +void gvn_value_set_null (GValue * value) +{ + g_return_if_fail (G_IS_VALUE (value)); + + if (!gvn_value_is_null (value)) + { + g_value_unset (value); + g_value_init (value, GVN_TYPE_NULL); + } +} + +/** + * gvn_value_new_from_string: + * @value: an uninitilized #GValue + * @string: a string to be converted + * @length: length of @string or -1 if its null terminated + * @type: the type to be converted + * + * Creates a new @value from a string + **/ +void gvn_value_new_from_string (GValue * value, GType type, const gchar * string, gsize len) +{ + g_return_if_fail (string != NULL); + + g_value_init (value, type); + gvn_value_new_from_string_real (value, type, string, len); +} + +/** + * gvn_value_new_valist: + * @value: an uninitilized #GValue + * @type: the type of value + * @content: a pointer to the value content + * + * Initializes the @value with the specified type and content. + **/ +void gvn_value_new_with_content (GValue * value, GType type, gpointer content) +{ + g_value_init (value, type); + + switch (type) + { + case G_TYPE_BOOLEAN: + g_value_set_boolean (value, *((gboolean *) content)); + break; + case G_TYPE_CHAR: + g_value_set_schar (value, *((gint8 *) content)); + break; + case G_TYPE_INT: + g_value_set_int (value, *((gint *) content)); + break; + case G_TYPE_UINT: + g_value_set_uint (value, *((guint *) content)); + break; + case G_TYPE_LONG: + g_value_set_long (value, *((glong *) content)); + break; + case G_TYPE_ULONG: + g_value_set_ulong (value, *((gulong *) content)); + break; + case G_TYPE_FLOAT: + g_value_set_float (value, *((gfloat *) content)); + break; + case G_TYPE_DOUBLE: + g_value_set_double (value, *((gdouble *) content)); + break; + case G_TYPE_STRING: + g_value_set_string (value, (gchar *) content); + break; + default: + if (type == G_TYPE_DATE + || type == G_TYPE_BYTES) + g_value_set_boxed (value, content); + else if (G_TYPE_IS_OBJECT (type)) + g_value_set_object (value, content); + else if (type != GVN_TYPE_NULL) + g_return_if_reached (); + } +} + +/** + * gvn_value_get_valist: + * @value: the type of value + * @va: a string to be converted + * + * Sets the value of the initilized @value with the value of next + * element in @va + **/ +void gvn_value_get_valist (const GValue * value, va_list va) +{ + GType type = G_VALUE_TYPE (value); + + switch (type) + { + case G_TYPE_BOOLEAN: + *va_arg (va, gboolean*) = g_value_get_boolean (value); + break; + case G_TYPE_CHAR: + *va_arg (va, gint8*) = g_value_get_schar (value); + break; + case G_TYPE_INT: + *va_arg (va, gint*) = g_value_get_int (value); + break; + case G_TYPE_UINT: + *va_arg (va, guint*) = g_value_get_uint (value); + break; + case G_TYPE_LONG: + *va_arg (va, glong*) = g_value_get_long (value); + break; + case G_TYPE_ULONG: + *va_arg (va, gulong*) = g_value_get_ulong (value); + break; + case G_TYPE_FLOAT: + *va_arg (va, gfloat*) = g_value_get_float (value); + break; + case G_TYPE_DOUBLE: + *va_arg (va, gdouble*) = g_value_get_double (value); + break; + case G_TYPE_STRING: + *va_arg (va, gchar**) = g_value_dup_string (value); + break; + default: + if (type == G_TYPE_DATE + || type == G_TYPE_BYTES) + *va_arg (va, gpointer*) = g_value_dup_boxed (value); + else if (G_TYPE_IS_OBJECT (type)) + *va_arg (va, gpointer*) = g_value_dup_object (value); + else if (type != GVN_TYPE_NULL) + g_return_if_reached (); + } +} + +/** + * gvn_value_compare: + * @a: a #GValue to be compared + * @b: a #GValue to be compared + * + * Does the same as g_value_compare0(), but returns a %gboolean. + * + * Return value: %TRUE if two values are equal, %FALSE otherwise. + **/ + +/** + * gvn_value_compare0: + * @a: #GValue to be compared + * @b: #GValue to be compared + * + * Compares a and b. The two values ​​must be of the same type or GVN_TYPE_NULL. + * + * Return value: -1, 0 or 1, if @a is <, == or > than @b. + **/ +gint gvn_value_compare0 (const GValue * a, const GValue * b) +{ + GType a_type = G_VALUE_TYPE (a); + gboolean a_is_val = G_IS_VALUE (a); + gboolean b_is_val = G_IS_VALUE (b); + + if (!(a_is_val && b_is_val)) + { + if (a_is_val) + return 1; + if (b_is_val) + return -1; + } + else if (a_type == G_VALUE_TYPE (b)) + { + switch (a_type) + { + case G_TYPE_FLOAT: + { + gfloat aux = g_value_get_float (a) - g_value_get_float (b); + return (aux > 0.0) ? 1 : (aux < 0.0) ? -1 : 0; + } + case G_TYPE_DOUBLE: + { + gdouble aux = g_value_get_double (a) - g_value_get_double (b); + return (aux > 0.0) ? 1 : (aux < 0.0) ? -1 : 0; + } + case G_TYPE_INT: + return g_value_get_int (a) - g_value_get_int (b); + case G_TYPE_UINT: + return (gint) (g_value_get_uint (a) - g_value_get_uint (b)); + case G_TYPE_LONG: + return (gint) (g_value_get_long (a) - g_value_get_long (b)); + case G_TYPE_ULONG: + return (gint) (g_value_get_ulong (a) - g_value_get_ulong (b)); + case G_TYPE_BOOLEAN: + return (gint) (g_value_get_boolean (a) - g_value_get_boolean (b)); + case G_TYPE_CHAR: + return (gint) (g_value_get_schar (a) - g_value_get_schar (b)); + case G_TYPE_STRING: + return g_strcmp0 (g_value_get_string (a), g_value_get_string (b)); + default: + if (a_type == GVN_TYPE_NULL) + return 0; + if (G_TYPE_FUNDAMENTAL (a_type) == G_TYPE_BOXED) + { + gpointer a_boxed = g_value_get_boxed (a); + gpointer b_boxed = g_value_get_boxed (b); + + if (!a_boxed) + return (gint) (a_boxed - b_boxed); + if (!b_boxed) + return (gint) (a_boxed - b_boxed); + if (a_type == G_TYPE_DATE) + return g_date_compare (a_boxed, b_boxed); + if (a_type == G_TYPE_DATE_TIME) + return g_date_time_compare (a_boxed, b_boxed); + else if (a_type == G_TYPE_BYTES) + return (gint) (a_boxed - b_boxed); + } + + g_warning (_("Attempting to compare invalid types: %s\n"), + g_type_name (a_type)); + } + } + else if (gvn_value_is_null (a)) + return -1; + else if (gvn_value_is_null (b)) + return 1; + + return 1; +} + +/** + * gvn_value_copy: + * @src: source #GValue + * @dst: destination #GValue + * + * Copies the value of @src into @dst. This function also works with #GvnNull + * values. + **/ +void gvn_value_copy (const GValue * src, GValue * dst) +{ + g_return_if_fail (G_IS_VALUE (src)); + g_return_if_fail (G_IS_VALUE (dst)); + + if (G_VALUE_TYPE (src) != G_VALUE_TYPE (dst)) + { + g_value_unset (dst); + g_value_init (dst, G_VALUE_TYPE (src)); + } + + g_value_copy (src, dst); +} + +/** + * gvn_value_ccopy: + * @src: source #GValue + * @dst: destination #GValue + * + * Compares @src with @dst, if they are different copies the value of @src into + * @dst. This function also works with #GvnNull values. + * + * Return value: %TRUE if they are copied, %FALSE if they are equals. + **/ +gboolean gvn_value_ccopy (const GValue * src, GValue * dst) +{ + if (gvn_value_compare (src, dst)) + return FALSE; + + gvn_value_copy (src, dst); + return TRUE; +} + +/** + * gvn_value_to_format_string: + **/ +void gvn_value_to_format_string (const GValue * src, guint digits, GValue * dst) +{ + GType type = G_VALUE_TYPE (src); + + g_value_init (dst, G_TYPE_STRING); + + switch (type) + { + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + { + gdouble dec; + gchar * format; + gchar buffer [G_ASCII_DTOSTR_BUF_SIZE]; + + if (type == G_TYPE_FLOAT) + dec = (gdouble) g_value_get_float (src); + else + dec = g_value_get_double (src); + + format = g_strdup_printf ("%%.%df", digits); + g_ascii_formatd (buffer, G_ASCII_DTOSTR_BUF_SIZE, format, dec); + g_value_set_string (dst, buffer); + g_free (format); + break; + } + default: + if (!gvn_value_is_null (src)) + g_value_transform (src, dst); + else + g_value_set_string (dst, ""); + } +} + +/** + * gvn_type_init: + * + * Initializes library types, also calls g_type_init(). + **/ +void gvn_type_init () +{ + gint n; + + GType types[] = { + G_TYPE_BOOLEAN + ,G_TYPE_CHAR + ,G_TYPE_INT + ,G_TYPE_UINT + ,G_TYPE_LONG + ,G_TYPE_ULONG + ,G_TYPE_FLOAT + ,G_TYPE_DOUBLE + ,G_TYPE_STRING + ,G_TYPE_DATE + ,G_TYPE_DATE_TIME + ,G_TYPE_BYTES + ,G_TYPE_NONE + }; + + for (n = 0; types[n] != G_TYPE_NONE; n++) + { + g_value_register_transform_func (types[n], GVN_TYPE_NULL, + gvn_value_transform_any_to_null + ); + g_value_register_transform_func (GVN_TYPE_NULL, types[n], + gvn_value_transform_null_to_any + ); + g_value_register_transform_func (G_TYPE_STRING, types[n], + gvn_value_transform_string_to_any + ); + } + + g_value_register_transform_func (G_TYPE_DATE, G_TYPE_STRING, + gvn_value_transform_date_to_string + ); + g_value_register_transform_func (G_TYPE_DATE_TIME, G_TYPE_STRING, + gvn_value_transform_date_time_to_string + ); + g_value_register_transform_func (G_TYPE_DATE_TIME, G_TYPE_DATE, + gvn_value_transform_date_time_to_date + ); + g_value_register_transform_func (G_TYPE_DATE, G_TYPE_DATE_TIME, + gvn_value_transform_date_to_date_time + ); +} + diff --git a/gvn/gvn-value.h b/gvn/gvn-value.h new file mode 100644 index 0000000..63c9aaf --- /dev/null +++ b/gvn/gvn-value.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef GVN_VALUE_H +#define GVN_VALUE_H + +#include +#include "gvn-null.h" + +#define gvn_value_compare(a, b) (!gvn_value_compare0 (a, b)) +#define gvn_value_is_null(value) (G_VALUE_TYPE (value) == GVN_TYPE_NULL) + +GValue * gvn_value_new (GType type); +void gvn_value_new_with_content (GValue * value, GType type, gpointer content); +void gvn_value_new_from_string (GValue * value, GType type, const gchar * string, gsize length); +void gvn_value_set_null (GValue * value); +void gvn_value_get_valist (const GValue * value, va_list va); +gint gvn_value_compare0 (const GValue * a, const GValue * b); +void gvn_value_copy (const GValue * src, GValue * dst); +gboolean gvn_value_ccopy (const GValue * src, GValue * dst); +void gvn_value_to_format_string (const GValue * src, guint digits, GValue * dst); +void gvn_type_init (); + +#endif diff --git a/gvn/gvn.h b/gvn/gvn.h new file mode 100644 index 0000000..5434c0c --- /dev/null +++ b/gvn/gvn.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef GVN_H +#define GVN_H + +#include +#include +#include "gvn-misc.h" +#include "gvn-null.h" +#include "gvn-time.h" +#include "gvn-param-spec.h" +#include "gvn-param.h" +#include "gvn-value.h" + +#endif \ No newline at end of file diff --git a/gvn/gvn.pc.in b/gvn/gvn.pc.in new file mode 100644 index 0000000..7cd5acf --- /dev/null +++ b/gvn/gvn.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/hedera +includedir=@includedir@/hedera + +Name: Gvn +Description: Utility Module for Hedera Library +Version: @VERSION@ +Requires: glib-2.0 gobject-2.0 +Libs: -L${libdir} -lgvn +Cflags: -I${includedir}/gvn diff --git a/image/icon.svg b/image/icon.svg new file mode 100644 index 0000000..010caec --- /dev/null +++ b/image/icon.svg @@ -0,0 +1,558 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/image/logo.svg b/image/logo.svg new file mode 100644 index 0000000..aeaea8e --- /dev/null +++ b/image/logo.svg @@ -0,0 +1,172 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/main/Makefile.am b/main/Makefile.am new file mode 100644 index 0000000..c60c904 --- /dev/null +++ b/main/Makefile.am @@ -0,0 +1,45 @@ +include $(top_srcdir)/Makefile.decl + +hedera_bin_PROGRAMS = hedera-bin + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(gtk_CFLAGS) \ + -D_HEDERA_LOCALE_DIR=\"$(datadir)/locale\" +hedera_bin_LDFLAGS = -Wl,--export-dynamic +hedera_bin_SOURCES = main.c +hedera_bin_LDADD = \ + $(top_builddir)/gvn/libgvn.la \ + $(top_builddir)/db/libdb.la \ + $(top_builddir)/vn/libvn.la + +SCRIPT = $(top_srcdir)/main/hedera.sh.in +hedera_bin_SCRIPTS = hedera +$(hedera_bin_SCRIPTS): $(SCRIPT) + $(SED) -e 's,[@]bindir[@],$(bindir),g' $(SCRIPT) > $(hedera_bin_SCRIPTS) + chmod +x $(hedera_bin_SCRIPTS) + +pkgconfig_DATA = hedera.pc + +desktop_DATA = vn-hedera.desktop + +HOST=verdnatura.es +certdir = $(datadir)/ca-certificates/$(HOST) +cert_DATA = cacert.pem +syscertdir = $(sysconfdir)/ssl/certs + +man_MANS = hedera.1 + +EXTRA_DIST = \ + $(SCRIPT) \ + hedera.pc.in + +DISTCLEANFILES = \ + $(hedera_bin_SCRIPTS) \ + hedera.pc \ + vn-hedera.desktop + +install-data-hook: + mkdir -p $(DESTDIR)$(syscertdir) + (cd $(DESTDIR)$(syscertdir) && \ + $(LN_S) -f $(DESTDIR)$(certdir)/$(cert_DATA) $(HOST).pem) diff --git a/main/cacert.pem b/main/cacert.pem new file mode 100644 index 0000000..f7bd939 --- /dev/null +++ b/main/cacert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIJAJimL+J4jUaQMA0GCSqGSIb3DQEBBQUAMIG2MQswCQYD +VQQGEwJFUzEdMBsGA1UECAwUQ29tdW5pZGFkIFZhbGVuY2lhbmExETAPBgNVBAcM +CFZhbGVuY2lhMR4wHAYDVQQKDBVWZXJkbmF0dXJhIExldmFudGUgU0wxFDASBgNV +BAsMC0luZm9ybWF0aWNhMRYwFAYDVQQDDA12ZXJkbmF0dXJhLmVzMScwJQYJKoZI +hvcNAQkBFhhob3N0bWFzdGVyQHZlcmRuYXR1cmEuZXMwIBcNMTMwNjExMTE1MjQ1 +WhgPMjA2MzA1MzAxMTUyNDVaMIG2MQswCQYDVQQGEwJFUzEdMBsGA1UECAwUQ29t +dW5pZGFkIFZhbGVuY2lhbmExETAPBgNVBAcMCFZhbGVuY2lhMR4wHAYDVQQKDBVW +ZXJkbmF0dXJhIExldmFudGUgU0wxFDASBgNVBAsMC0luZm9ybWF0aWNhMRYwFAYD +VQQDDA12ZXJkbmF0dXJhLmVzMScwJQYJKoZIhvcNAQkBFhhob3N0bWFzdGVyQHZl +cmRuYXR1cmEuZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsqq+h +1kUfZE19Inm3M2XF1ytIqXeIsXsiZ5T9aVdedQrF2xcVOr4dvJyE5zp9g7NNxy9T +FyAHPXiPWKSoX3w0uoQfLW9Hm19QZYu40hBD5oRrJy5m6l4JU3Gj0tnkvYc4m8Fl +2uEUT3ain0FE2I1XjKpL6eQi9TKjhgmWNRgDJUbllAB3lsQLkNfB6EFo2QJykiKl +aRi4UaHdSkd3zkUJyYRdcG7IxCFvsViKpwr3JF+TLXSN/oronLFgG8cKedrn+sMW +pBm2FZQKAh8hLT3QQQqQgGpQ2y4t2EVJkYVdcGBXCyWYDKegMXgJR10WunICFCaD +kM0P3pYQdtbgr7e9AgMBAAGjUDBOMB0GA1UdDgQWBBQ9+iZdf7pLNQItxs4o43dN +FZD0CTAfBgNVHSMEGDAWgBQ9+iZdf7pLNQItxs4o43dNFZD0CTAMBgNVHRMEBTAD +AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAcFzJhm3jjuexZf9tTgQfzwtsafLLsYkrm +EZOTkhvZFJYbBd8UngL5UPF1M3buSEV1VqsWCALLrFkxzbmjk5uM8cswZR/3J6un +7GFYWTEllVgkD8KmNA2b6j5uSuGCQmOccsR4069vwNjrvTbtQpEpEAAnDf6d5/N4 +MXuDozHq3iEYi7qyGf++5mp4D+82y2ONSUsiLgxqnMT5JX/yhDKI1IiX1ndyt+6r +ie2i2fzdOgovlAILuTM4hoUjPYFOxwmUwpxU2EAjPqo9Bi7bQiH+fv0QBzbpQ9on +DRHo7NuXaDG49nvatJjuf4DiDELBCTMeKBOk5hG7kyWCIwp5wr+w +-----END CERTIFICATE----- diff --git a/main/hedera.1 b/main/hedera.1 new file mode 100644 index 0000000..bbf6193 --- /dev/null +++ b/main/hedera.1 @@ -0,0 +1,50 @@ +.\"Created with GNOME Manpages Editor Wizard +.\"http://sourceforge.net/projects/gmanedit2 +.TH hedera 1 "2013-01-02" "1.0" "Hedera" + +.SH NAME +hedera \- modular management system + +.SH SYNOPSIS +.B hedera +.\"RI [ options ] +.br + +.SH DESCRIPTION +.B hedera +is an enterprise management and administration application. It features modular +conectivity to user-made modules. +.PP +Being so easily extensible, +.B hedera +can handle virtually any task related to a database if the right module is +available. The modules can be written in C or in Vala. +.PP +By default +.B hedera +will look for the modules in /usr/lib/hedera/module and for its corresponding +data (GUI files, configuration...) in /usr/share/hedera/module but more +directories can be added by setting +.B VN_MODULE_LIB_PATH +(for the binaries) and +.B VN_MODULE_DATA_PATH +(for the additional data). These environment variables are intended for testing +during the developement and the expected is to use the modules installed. +.PP +The format for the configuration files for the modules and some information on +how to write the modules themselves, can be seen in the official documentation +for +.B hedera. + +.SH OPTIONS + +.SH EXAMPLE +Given a module project in the home directory, +.B hedera +should be called like this to load the module without installing it: + hedera --lib-dir ~/module/src/.libs --data-dir ~/module/data + +.SH AUTHORS +Copyright (C) 2012 Juan Ferrer Toribio . +.PP +Manual page written by Alejandro T. Colombini. \ No newline at end of file diff --git a/main/hedera.pc.in b/main/hedera.pc.in new file mode 100644 index 0000000..b5d38fe --- /dev/null +++ b/main/hedera.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/hedera +includedir=@includedir@/hedera + +Name: Hedera +Description: The Hedera Library +Version: @VERSION@ +Requires: gtk+-3.0 gvn sql db vn +Libs: -L${libdir} +Cflags: -I${includedir} diff --git a/main/hedera.sh.in b/main/hedera.sh.in new file mode 100644 index 0000000..b7acf53 --- /dev/null +++ b/main/hedera.sh.in @@ -0,0 +1,10 @@ +#!/bin/bash + +if [ -x @bindir@/vn-updater-gui ] +then + echo "Running updater..." + @bindir@/vn-updater-gui +fi + +export LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN=1 +exec @bindir@/hedera-bin diff --git a/main/main.c b/main/main.c new file mode 100644 index 0000000..47ca0d6 --- /dev/null +++ b/main/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include +#include +#include + +int main (int argc, char * argv[]) +{ +#ifdef ENABLE_NLS + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, _HEDERA_LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); +#endif + + gvn_type_init (); + gtk_init (&argc, &argv); + + VnLogin * login = vn_login_new ("apps.hedera"); + vn_login_run (login); + g_object_unref (login); + return 0; +} diff --git a/main/vn-hedera.desktop.in b/main/vn-hedera.desktop.in new file mode 100644 index 0000000..e949638 --- /dev/null +++ b/main/vn-hedera.desktop.in @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=Hedera +GenericName=Office Tool +Comment=Runtime for the Hedera system +Exec=hedera +Icon=@prefix@/share/hedera/vn/image/icon.svg +Terminal=false +Type=Application +Categories=GNOME;GTK;Office; +StartupNotify=true +Version=@VERSION@ diff --git a/module/Makefile.am b/module/Makefile.am new file mode 100644 index 0000000..b2980bc --- /dev/null +++ b/module/Makefile.am @@ -0,0 +1,5 @@ + +SUBDIRS = \ + src \ + data \ + sql diff --git a/module/data/Makefile.am b/module/data/Makefile.am new file mode 100644 index 0000000..374a026 --- /dev/null +++ b/module/data/Makefile.am @@ -0,0 +1,13 @@ +include $(top_srcdir)/Makefile.decl + +exampledir = $(module_datadir) + +example_DATA = \ + example.xml \ + example.ui \ + consulter.glade \ + consulter.ui \ + users.glade \ + customer.glade + +EXTRA_DIST = $(example_DATA) diff --git a/module/data/consulter.glade b/module/data/consulter.glade new file mode 100644 index 0000000..34a0453 --- /dev/null +++ b/module/data/consulter.glade @@ -0,0 +1,304 @@ + + + + + + + + + Send + Send + Send the current query + system-run + + + + + + + Clean + Clean + Clean the messages + edit-clear + + + + + + + _Consulter + + + + + + + + + + + 300 + True + False + 8 + vertical + 8 + + + True + False + 8 + + + True + False + 1 + Query: + + + False + True + 0 + + + + + True + False + True + 0 + 1 + + + True + True + Set on the code + + + + + + True + True + 1 + + + + + send + True + True + True + send + + + False + True + 2 + + + + + clean + True + True + True + clean + + + False + False + 3 + + + + + gtk-stop + False + True + True + True + False + True + + + + False + False + 4 + + + + + False + True + 0 + + + + + True + True + 120 + True + + + True + True + never + in + + + True + True + model + True + 0 + True + True + + + + + + + + False + True + + + + + True + True + in + + + True + True + + + + + + + + True + True + + + + + True + True + 1 + + + + + True + False + 8 + + + True + False + 8 + start + + + Immediate changes + False + True + False + True + False + False + 0 + True + True + + + + False + True + 0 + + + + + Start + False + True + True + True + False + + + + False + False + 1 + + + + + Commit + False + True + True + True + False + + + + False + False + 2 + + + + + Rollback + False + True + True + True + False + + + + False + False + 3 + + + + + True + True + 0 + + + + + True + False + start + VN_HANDLER_SHOW_REFRESH | VN_HANDLER_SHOW_UNDO | VN_HANDLER_SHOW_SAVE | VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD + + + False + True + 1 + + + + + False + True + 2 + + + + diff --git a/module/data/consulter.ui b/module/data/consulter.ui new file mode 100644 index 0000000..1069a8f --- /dev/null +++ b/module/data/consulter.ui @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/module/data/customer.glade b/module/data/customer.glade new file mode 100644 index 0000000..d34db37 --- /dev/null +++ b/module/data/customer.glade @@ -0,0 +1,415 @@ + + + + + + + SELECT id, street, pc, city, province, ok FROM user_address WHERE #p ORDER BY id + + + SELECT id, name, credit, active, born, photo, object_id FROM "user" ORDER BY id + + + True + False + 6 + vertical + + + True + False + 0 + none + + + True + False + 4 + vertical + 8 + + + True + False + 10 + + + True + False + 8 + 8 + True + + + True + False + 1 + Search: + + + 0 + 0 + 1 + 1 + + + + + True + False + SELECT id, #p FROM article WHERE #p + name + + + 1 + 0 + 1 + 1 + + + + + True + False + 1 + Id: + + + 0 + 1 + 1 + 1 + + + + + True + False + 1 + Name: + + + 0 + 2 + 1 + 1 + + + + + True + False + 1 + Credit: + + + 0 + 3 + 1 + 1 + + + + + True + False + 1 + Type: + + + 0 + 4 + 1 + 1 + + + + + True + False + 1 + Active: + + + 0 + 5 + 1 + 1 + + + + + True + False + info + id + + + 1 + 1 + 1 + 1 + + + + + True + False + info + name + + + 1 + 2 + 1 + 1 + + + + + True + False + info + object_id + SELECT id, object FROM object + + + 1 + 4 + 1 + 1 + + + + + True + False + info + active + + + 1 + 5 + 1 + 1 + + + + + True + False + info + credit + + + 1 + 3 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + info + born + + + False + True + 1 + + + + + True + False + vertical + 6 + + + True + False + info + born + + + False + True + 0 + + + + + True + False + + + False + True + 1 + + + + + False + True + 2 + + + + + False + True + 0 + + + + + True + False + end + info + VN_HANDLER_SHOW_REFRESH | VN_HANDLER_SHOW_UNDO | VN_HANDLER_SHOW_SAVE | VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD | VN_HANDLER_SHOW_SCROLL + + + False + True + 1 + + + + + + + True + False + <b>Info</b> + True + + + + + False + False + 0 + + + + + True + False + 0 + none + + + True + False + 4 + vertical + 8 + + + True + True + in + + + True + True + homes + + + + + + Id + id + + + + + Default + ok + True + + + + + Street + True + street + True + + + + + PC + pc + True + + + + + City + city + + + + + Province + True + province + True + + + + + + + True + True + 0 + + + + + True + False + end + homes + VN_HANDLER_SHOW_REFRESH | VN_HANDLER_SHOW_UNDO | VN_HANDLER_SHOW_SAVE | VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD + + + False + True + 1 + + + + + + + True + False + <b>Homes</b> + True + + + + + True + True + 1 + + + + diff --git a/module/data/example.ui b/module/data/example.ui new file mode 100644 index 0000000..b290204 --- /dev/null +++ b/module/data/example.ui @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/module/data/example.xml b/module/data/example.xml new file mode 100644 index 0000000..9729d1e --- /dev/null +++ b/module/data/example.xml @@ -0,0 +1,37 @@ + + + Example + + + Example + + + +
+ Consulter +
+
+ Users +
+
+ Customer +
+
+
diff --git a/module/data/signer.glade b/module/data/signer.glade new file mode 100644 index 0000000..edc6f34 --- /dev/null +++ b/module/data/signer.glade @@ -0,0 +1,219 @@ + + + + + 100 + 1 + 10 + + + 365 + 10 + 30 + + + True + False + 5 + 5 + + + True + True + 170 + True + + + True + True + adjustment + always + top-right + True + in + + + True + True + True + True + both + + + + + + + + False + True + + + + + True + True + adjustment + always + in + + + True + True + True + True + both + + + + none + + + + + + + True + True + + + + + True + True + 0 + + + + + True + False + vertical + 5 + + + True + False + 0 + + + True + False + 5 + vertical + 5 + + + True + True + 2012 + 2 + 8 + + + False + True + 1 + + + + + True + False + + Department + + + + False + True + 3 + + + + + + + True + False + <b>Date and Department</b> + True + + + + + False + True + 0 + + + + + True + False + 0 + + + True + False + 5 + vertical + 5 + + + gtk-clear + False + True + True + True + False + True + + + False + True + 0 + + + + + 110 + True + True + in + + + + + + True + True + 1 + + + + + + + True + False + <b>Status</b> + True + + + + + True + True + 1 + + + + + False + True + 1 + + + + diff --git a/module/data/users.glade b/module/data/users.glade new file mode 100644 index 0000000..400bf00 --- /dev/null +++ b/module/data/users.glade @@ -0,0 +1,908 @@ + + + + + + SELECT user_id, group_id, uid, last_change, expire FROM account WHERE #p + on-iter + + + SELECT mail_alias_id, user_id FROM mail_alias_account WHERE #p + + + True + False + 6 + vertical + 6 + + + True + False + 0 + none + + + True + False + 30 + + + True + False + 6 + + + True + False + 1 + User name: + + + False + True + 0 + + + + + True + False + search-user + + + False + True + 1 + + + + + + + + + True + False + <b>Search</b> + True + + + + + False + True + 0 + + + + + True + True + in + + + True + True + False + 1 + users + + + + + + Identifier + id + + + + + User + True + name + + + + + MySQL User + user + + + + + Enabled + active + + + + + + + True + True + 1 + + + + + True + False + end + users + VN_HANDLER_SHOW_REFRESH | VN_HANDLER_SHOW_UNDO | VN_HANDLER_SHOW_SAVE | VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD + + + False + True + 2 + + + + + True + True + start + left + + + True + False + 15 + vertical + 20 + + + True + False + 6 + 6 + True + + + True + False + 1 + Name: + + + 0 + 1 + 1 + 1 + + + + + True + False + 1 + Password: + + + 0 + 2 + 1 + 1 + + + + + True + False + 1 + MySQL user: + + + 0 + 3 + 1 + 1 + + + + + True + False + users + active + + + 1 + 4 + 1 + 1 + + + + + True + False + 1 + Enabled: + + + 0 + 4 + 1 + 1 + + + + + 130 + True + False + 1 + Identifier: + + + 0 + 0 + 1 + 1 + + + + + 170 + True + False + users + id + False + + + 1 + 0 + 1 + 1 + + + + + True + False + users + name + + + 1 + 1 + 1 + 1 + + + + + True + False + users + mysql_user_id + SELECT id, user FROM mysql_user + + + 1 + 3 + 1 + 1 + + + + + gtk-edit + False + True + True + True + False + True + + + + 1 + 2 + 1 + 1 + + + + + False + True + 0 + + + + + + + 100 + True + False + User + + + False + + + + + True + False + 15 + vertical + 20 + + + True + False + 6 + 6 + True + + + True + False + account + group_id + SELECT id, name FROM `group` + + + 1 + 1 + 1 + 1 + + + + + 170 + True + False + account + uid + + + 1 + 0 + 1 + 1 + + + + + 130 + True + False + 1 + UID: + + + 0 + 0 + 1 + 1 + + + + + True + False + 1 + Main group: + + + 0 + 1 + 1 + 1 + + + + + True + False + account + last_change + + + 1 + 2 + 1 + 1 + + + + + True + False + account + expire + + + 1 + 3 + 1 + 1 + + + + + True + False + 1 + Last change: + + + 0 + 2 + 1 + 1 + + + + + True + False + 1 + Expires: + + + 0 + 3 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + end + account + True + + + False + True + 1 + + + + + 1 + + + + + 100 + True + False + Account + + + 1 + False + + + + + True + False + 15 + vertical + 6 + + + 160 + True + True + in + + + True + True + False + alias + + + + + + Alias + mail_alias_id + True + SELECT id, alias FROM mail_alias + + + + + + + False + True + 1 + + + + + True + False + 6 + end + alias + VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD + + + False + True + 2 + + + + + 2 + + + + + 100 + True + False + Mail alias + + + 2 + False + + + + + True + False + 15 + vertical + 20 + + + True + False + start + 6 + 6 + True + + + 130 + True + False + 1 + Extension: + + + 0 + 0 + 1 + 1 + + + + + True + False + 1 + Secret: + + + 0 + 1 + 1 + 1 + + + + + True + False + 1 + Identifier: + + + 0 + 2 + 1 + 1 + + + + + True + False + 1 + Call group: + + + 0 + 3 + 1 + 1 + + + + + 170 + True + False + sip + extension + + + 1 + 0 + 1 + 1 + + + + + True + False + sip + secret + + + 1 + 1 + 1 + 1 + + + + + True + False + sip + callerid + + + 1 + 2 + 1 + 1 + + + + + True + False + sip + callgroup + + + 1 + 3 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + 6 + end + sip + + + False + True + 1 + + + + + 3 + + + + + 100 + True + False + SIP + + + 3 + False + + + + + False + False + 3 + + + + + False + 5 + Change password + False + True + True + dialog + + + + + False + vertical + 2 + + + True + False + + + False + 8 + 8 + + + True + False + + + False + True + 0 + + + + + True + True + 0 + + + + + False + 5 + vertical + end + + + + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + 6 + 6 + 6 + + + True + False + 1 + Password: + + + 0 + 0 + 1 + 1 + + + + + True + False + 1 + Repeat password: + + + 0 + 1 + 1 + 1 + + + + + True + True + False + + True + + + 1 + 0 + 1 + 1 + + + + + True + True + False + + True + + + 1 + 1 + 1 + 1 + + + + + False + True + 1 + + + + + False + end + + + + + + + + + False + True + end + 2 + + + + + + + + SELECT user_id, extension, secret, callerid, callgroup FROM account_sip WHERE #p + on-iter + + + SELECT u.id, u.name, u.mysql_user_id, m.user, u.active FROM `user` u JOIN mysql_user m ON u.mysql_user_id = m.id + + diff --git a/module/src/Makefile.am b/module/src/Makefile.am new file mode 100644 index 0000000..aed27ca --- /dev/null +++ b/module/src/Makefile.am @@ -0,0 +1,21 @@ +include $(top_srcdir)/Makefile.decl + +example_libdir = $(module_libdir) +example_lib_LTLIBRARIES = libexample.la + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(gtk_CFLAGS) +libexample_la_LDFLAGS = -avoid-version +libexample_la_LIBADD = $(top_builddir)/vn/libvn.la +libexample_la_SOURCES = \ + vn-consulter.h \ + vn-consulter.c \ + vn-users.h \ + vn-users.c \ + vn-customer.h \ + vn-customer.c + +install-data-hook: + rm -f $(DESTDIR)$(module_libdir)/libexample.la + rm -f $(DESTDIR)$(module_libdir)/libexample.a \ No newline at end of file diff --git a/module/src/vn-consulter.c b/module/src/vn-consulter.c new file mode 100644 index 0000000..8698c61 --- /dev/null +++ b/module/src/vn-consulter.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-consulter.h" +#include "stdlib.h" + +G_DEFINE_TYPE (VnConsulter, vn_consulter, VN_TYPE_FORM); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +void vn_consulter_clean_clicked (GtkButton * button, VnConsulter * obj) +{ + gtk_list_store_clear (obj->model); +} + +void vn_consulter_start_clicked (GtkButton * button, VnConsulter * obj) +{ + db_conn_start_transaction (VN_FORM (obj)->conn); +} + +void vn_consulter_on_stop_clicked (GtkButton * button, VnConsulter * obj) +{ + db_conn_kill_query (VN_FORM (obj)->conn); +} + +void vn_consulter_commit_clicked (GtkButton * button, VnConsulter * obj) +{ + db_conn_commit (VN_FORM (obj)->conn); +} + +void vn_consulter_rollback_clicked (GtkButton * button, VnConsulter * obj) +{ + db_conn_rollback (VN_FORM (obj)->conn); +} + +static void vn_consulter_set_message (VnConsulter * obj, const gchar * msg) +{ + GtkTreePath * path; + GtkTreeIter iter; + + gtk_list_store_append (obj->model, &iter); + gtk_list_store_set (obj->model, &iter, 0, msg, -1); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (obj->model), &iter); + gtk_tree_view_scroll_to_cell (obj->tv, path, NULL, FALSE, 0, 0); + gtk_tree_path_free (path); +} + +static void vn_consulter_model_status_changed (DbModel * model, + DbModelStatus status, VnConsulter * obj) +{ + GList * n; + GtkTreeView * tv = GTK_TREE_VIEW (obj->tree); + GList * cols = gtk_tree_view_get_columns (tv); + + for (n = cols; n; n = n->next) + gtk_tree_view_remove_column (tv, n->data); + + g_list_free (cols); + + switch (status) + { + case DB_MODEL_STATUS_READY: + { + gint i; + gint cols = db_model_get_ncols (model); + + for (i = 0; i < cols; i++) + { + VnColumn * column; + GType col_type, type = db_model_get_column_type (model, i); + + if (type == G_TYPE_BYTES) + col_type = VN_TYPE_COLUMN_IMAGE; + else if (type == G_TYPE_BOOLEAN) + col_type = VN_TYPE_COLUMN_CHECK; + else + col_type = VN_TYPE_COLUMN_ENTRY; + +if (i == 7) col_type = VN_TYPE_COLUMN_IMAGE; + + column = vn_grid_append_column (obj->tree, i + ,db_model_get_column_name (model, i) + ,col_type + ,TRUE, FALSE + ); + + if (type == G_TYPE_FLOAT || type == G_TYPE_DOUBLE) + g_object_set (column, "digits", 3, NULL); + } + + break; + } + case DB_MODEL_STATUS_ERROR: + vn_consulter_set_message (obj, "Error"); + break; + default: + break; + } + + gtk_widget_set_sensitive (GTK_WIDGET (obj->mode), TRUE); +} + +void vn_consulter_mode_toggled (GtkToggleButton * mode, VnConsulter * obj) +{ + DbIterator * iterator = vn_handler_get_iterator (obj->handler); + + db_iterator_set_mode (iterator + ,(gtk_toggle_button_get_active (mode)) ? + DB_ITERATOR_MODE_ON_CHANGE: + DB_ITERATOR_MODE_ON_DEMAND + ); +} + +void vn_consulter_send (GtkButton * button, VnConsulter * obj) +{ + DbIterator * iterator; + DbModel * model; + gchar * sql; + + sql = gtk_combo_box_text_get_active_text (obj->combo); + + vn_consulter_set_message (obj, sql); + + model = db_model_new_with_sql (VN_FORM (obj)->conn, sql); + g_signal_connect (model, "status-changed", + G_CALLBACK (vn_consulter_model_status_changed), obj); + + iterator = db_iterator_new (model); + iterator->mode = DB_ITERATOR_MODE_ON_DEMAND; + vn_handler_set_iterator (obj->handler, iterator); + vn_grid_set_iterator (obj->tree, iterator); + + vn_consulter_mode_toggled (GTK_TOGGLE_BUTTON (obj->mode), obj); + + g_free (sql); + g_object_unref (model); + g_object_unref (iterator); +} + +static void vn_consulter_open (VnConsulter * obj) +{ + gint i; + VnForm * form = VN_FORM (obj); + + gchar * queries[] = + { +//MySQL (kk schema) + "/*my*/SELECT id, name, item_id, item_id2, amount, item.price " + "FROM movement JOIN item USING (item_id, item_id2)" + ,"/*my*/SELECT item_id,item_id2, i.name, m.id, m.amount, m.price " + "FROM item i LEFT JOIN movement m USING (item_id, item_id2)" + ,"/*my*/SELECT item_id, item_id2, name, price FROM item" +//PgSQL (test schema) + ,"/*pg*/SELECT pg_sleep(120)" + ,"/*pg*/SELECT i.id, i.name, i.color, m.id, m.amount " + "FROM item i LEFT JOIN movement m ON i.id = m.item_id" + ,"/*pg*/SELECT m.id, amount, item_id, name, color " + "FROM movement m JOIN item ON m.item_id = item.id" + ,"/*pg*/SELECT * FROM item" + ,"/*pg*/ SELECT id1, id2, name, ok, date, number, floating, image " + "FROM prueben" + ,NULL + }; + + obj->model = vn_form_get (form, "model"); + obj->combo = vn_form_get (form, "query"); + obj->tv = vn_form_get (form, "treeview"); + obj->mode = vn_form_get (form, "mode"); + obj->tree = vn_form_get (form, "consulter"); + obj->handler = vn_form_get (form, "handler"); + gtk_entry_set_placeholder_text (vn_form_get (form, "combo-entry"), + _("Write your query here or select one from the list")); + + gtk_tree_view_insert_column_with_attributes (obj->tv, -1, "Mensaje", + gtk_cell_renderer_text_new (), "text", 0, NULL); + + for (i = 0; i < g_strv_length (queries); i++) + gtk_combo_box_text_prepend_text (obj->combo, queries[i]); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_consulter_init (VnConsulter * obj) {} + +static void vn_consulter_class_init (VnConsulterClass * k) +{ + VN_FORM_CLASS (k)->open = (VnFormOpenFunc) vn_consulter_open; +} diff --git a/module/src/vn-consulter.h b/module/src/vn-consulter.h new file mode 100644 index 0000000..9873519 --- /dev/null +++ b/module/src/vn-consulter.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_CONSULTER_H +#define VN_CONSULTER_H + +#include + +#define VN_TYPE_CONSULTER (vn_consulter_get_type ()) +#define VN_CONSULTER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_CONSULTER, VnConsulter)) +#define VN_IS_CONSULTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_CONSULTER)) + +typedef struct _VnConsulter VnConsulter; +typedef struct _VnConsulterClass VnConsulterClass; + +struct _VnConsulter +{ + VnForm parent; + GtkButton * connect; + GtkListStore * model; + GtkCheckButton * mode; + GtkComboBoxText * combo; + GtkTreeView * tv; + GtkContainer * box; + VnGrid * tree; + VnHandler * handler; +}; + +struct _VnConsulterClass +{ + VnFormClass parent; +}; + +GType vn_consulter_get_type (); + +#endif \ No newline at end of file diff --git a/module/src/vn-customer.c b/module/src/vn-customer.c new file mode 100644 index 0000000..70b5a8c --- /dev/null +++ b/module/src/vn-customer.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-customer.h" + +G_DEFINE_TYPE (VnCustomer, vn_customer, VN_TYPE_FORM); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_customer_open (VnForm * obj, gpointer user_data) +{ + DbIterator * info = vn_form_get (obj, "info"); + + DbIterator * homes = vn_form_get (obj, "homes"); + db_iterator_link (homes, "user_id", info, "id"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_customer_init (VnCustomer * obj) {} + +static void vn_customer_class_init (VnCustomerClass * k) +{ + VN_FORM_CLASS (k)->open = (VnFormOpenFunc) vn_customer_open; +} diff --git a/module/src/vn-customer.h b/module/src/vn-customer.h new file mode 100644 index 0000000..efe5868 --- /dev/null +++ b/module/src/vn-customer.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_CUSTOMER_H +#define VN_CUSTOMER_H + +#include + +#define VN_TYPE_CUSTOMER (vn_customer_get_type ()) +#define VN_CUSTOMER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_CUSTOMER, VnCustomer)) +#define VN_IS_CUSTOMER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_CUSTOMER)) + +typedef struct _VnCustomer VnCustomer; +typedef struct _VnCustomerClass VnCustomerClass; + +struct _VnCustomer +{ + VnForm parent; +}; + +struct _VnCustomerClass +{ + VnFormClass parent; +}; + +GType vn_customer_get_type (); + +#endif \ No newline at end of file diff --git a/module/src/vn-users.c b/module/src/vn-users.c new file mode 100644 index 0000000..8c008ae --- /dev/null +++ b/module/src/vn-users.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-users.h" +#include "stdlib.h" + +G_DEFINE_TYPE (VnUsers, vn_users, VN_TYPE_FORM); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_users_reset_dialog (VnUsers * obj, const gchar * error) +{ + GtkWidget * infobar = vn_form_get (VN_FORM (obj), "password-infobar"); + + if (error) + { + GtkLabel * password_error = vn_form_get (VN_FORM (obj), "password-error"); + gtk_label_set_text (password_error, error); + gtk_widget_show (infobar); + } + else + gtk_widget_hide (infobar); + + gtk_entry_set_text (obj->repeat_password, ""); + gtk_entry_set_text (obj->password_entry, ""); + gtk_widget_grab_focus (GTK_WIDGET (obj->password_entry)); +} + +void vn_users_on_set_password_clicked (GtkButton * button, VnUsers * obj) +{ + gtk_window_set_transient_for (GTK_WINDOW (obj->password_dialog), + GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (obj)))); + gtk_widget_show (GTK_WIDGET (obj->password_dialog)); + vn_users_reset_dialog (obj, NULL); +} + +static void vn_users_on_password_changed (DbRequest * request, VnUsers * obj) +{ + GValue value = {0}; + + if (db_request_fetch_value (request, &value, NULL)) + { + if (g_value_get_int (&value) != -1) + gtk_widget_hide (GTK_WIDGET (obj->password_dialog)); + else + vn_users_reset_dialog (obj, _("The password is too week.")); + + g_value_unset (&value); + } + + g_object_unref (request); +} + +void vn_users_on_dialog_response (GtkDialog * dialog, gint response_id, VnUsers * obj) +{ + if (response_id == GTK_RESPONSE_ACCEPT) + { + const gchar * password = gtk_entry_get_text (obj->password_entry); + + if (!g_strcmp0 (password, "")) + vn_users_reset_dialog (obj, _("The password can't be empty.")); + else if (g_strcmp0 (password, gtk_entry_get_text (obj->repeat_password))) + vn_users_reset_dialog (obj, _("Passwords do not match.")); + else + { + SqlString * query; + + query = sql_string_new ("SELECT user_set_password (#user, #password)" + ,"user", GVN_TYPE_PARAM, obj->user_id + ,"password", G_TYPE_STRING, password + ,NULL + ); + + db_conn_query_with_stmt_async (VN_FORM (obj)->conn + ,SQL_STMT (query) + ,(DbRequestDoneCallback) vn_users_on_password_changed + ,g_object_ref (obj) + ,g_object_unref + ); + } + } + else + gtk_widget_hide (GTK_WIDGET (dialog)); +} + +static void vn_users_open (VnUsers * obj, gpointer user_data) +{ + DbIterator * users; + VnForm * form = VN_FORM (obj); + + obj->password_entry = vn_form_get (form, "password-entry"); + obj->repeat_password = vn_form_get (form, "repeat-password"); + + obj->password_dialog = vn_form_get (form, "password-dialog"); + gtk_dialog_add_button (obj->password_dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (obj->password_dialog, GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT); + + users = vn_form_get (form, "users"); + obj->user_id = db_iterator_get_param (users, "id"); + + obj->account = vn_form_get (form, "account"); + db_iterator_link_with_param (obj->account, "user_id", obj->user_id); + + obj->alias = vn_form_get (form, "alias"); + db_iterator_link (obj->alias, "user_id", obj->account, "user_id"); + + obj->sip = vn_form_get (form, "sip"); + db_iterator_link (obj->sip, "user_id", obj->account, "user_id"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_users_init (VnUsers * obj) {} + +static void vn_users_class_init (VnUsersClass * k) +{ + VN_FORM_CLASS (k)->open = (VnFormOpenFunc) vn_users_open; +} diff --git a/module/src/vn-users.h b/module/src/vn-users.h new file mode 100644 index 0000000..2f692bb --- /dev/null +++ b/module/src/vn-users.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_USERS_H +#define VN_USERS_H + +#include + +#define VN_TYPE_USERS (vn_users_get_type ()) +#define VN_USERS(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_USERS, VnUsers)) +#define VN_IS_USERS(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_USERS)) + +typedef struct _VnUsers VnUsers; +typedef struct _VnUsersClass VnUsersClass; + +struct _VnUsers +{ + VnForm parent; + GvnParam * user_id; + DbIterator * account; + DbIterator * alias; + DbIterator * sip; + GtkDialog * password_dialog; + GtkEntry * password_entry; + GtkEntry * repeat_password; +}; + +struct _VnUsersClass +{ + VnFormClass parent; +}; + +GType vn_users_get_type (); + +#endif \ No newline at end of file diff --git a/plugin/Makefile.am b/plugin/Makefile.am new file mode 100644 index 0000000..2d415cb --- /dev/null +++ b/plugin/Makefile.am @@ -0,0 +1,4 @@ + +SUBDIRS = \ + pg \ + mysql \ No newline at end of file diff --git a/plugin/mysql/Makefile.am b/plugin/mysql/Makefile.am new file mode 100644 index 0000000..4efb366 --- /dev/null +++ b/plugin/mysql/Makefile.am @@ -0,0 +1,20 @@ +include $(top_srcdir)/Makefile.decl + +mysql_lib_LTLIBRARIES = libdbmysql.la + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(glib_CFLAGS) +libdbmysql_la_LIBADD = \ + $(glib_LIBS) \ + $(top_builddir)/db/libdb.la \ + -lmysqlclient \ + -lz +libdbmysql_la_LDFLAGS = -avoid-version +libdbmysql_la_SOURCES = \ + db-mysql.h \ + db-mysql.c + +install-data-hook: + rm -f $(DESTDIR)$(mysql_libdir)/libdbmysql.la + rm -f $(DESTDIR)$(mysql_libdir)/libdbmysql.a \ No newline at end of file diff --git a/plugin/mysql/db-mysql.c b/plugin/mysql/db-mysql.c new file mode 100644 index 0000000..4c200e2 --- /dev/null +++ b/plugin/mysql/db-mysql.c @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "db-mysql.h" +#include +#include +#include +#include + +/** + * SECTION: db-mysql + * @Short_description: manages a connection to a PostgreSQL database. + * @Title: DbMysql + * @See_also: #DbConn + * + * This class manages a connection to a MySQL database internally. This + * is accessed through the #DbConn class to internally connect, query and + * disconnect the database. + **/ +G_DEFINE_TYPE (DbMysql, db_mysql, DB_TYPE_PLUGIN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void db_mysql_close (DbMysql * obj) +{ + if (obj->mysql) + { + g_free (obj->host); + g_free (obj->user); + g_free (obj->pass); + obj->host = NULL; + obj->user = NULL; + obj->pass = NULL; + + mysql_close (obj->mysql); + obj->mysql = NULL; + obj->thread_id = 0; + obj->socket = -1; + obj->is_open = FALSE; + } +} + +static gboolean db_mysql_open (DbMysql * obj, const gchar * host, + const gchar * schema, const gchar * user, const gchar * pass, GError ** err) +{ + if (!obj->mysql) + obj->mysql = mysql_init (NULL); + + if (!obj->mysql) + { + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_OPENING + ,_("Can't allocate the needed memory") + ); + return FALSE; + } + + if (!mysql_real_connect (obj->mysql, host, user, pass, schema, 0, NULL, CLIENT_MULTI_STATEMENTS)) + { + gint error_code; + + switch (mysql_errno (obj->mysql)) + { + case ER_ACCESS_DENIED_ERROR: + error_code = DB_CONN_ERROR_BAD_LOGIN; + break; + default: + error_code = DB_CONN_ERROR_OPENING; + } + + g_set_error (err + ,DB_CONN_ERROR_OPENING + ,error_code + ,"%s", mysql_error (obj->mysql) + ); + db_mysql_close (obj); + return FALSE; + } + + obj->host = g_strdup (host); + obj->user = g_strdup (user); + obj->pass = g_strdup (pass); + obj->socket = obj->mysql->net.fd; + obj->thread_id = mysql_thread_id (obj->mysql); + obj->is_open = TRUE; + + mysql_set_character_set (obj->mysql, "utf8"); + return TRUE; +} + +static void db_mysql_set_ssl (DbMysql * obj, const gchar * ca) +{ + if (!obj->mysql) + obj->mysql = mysql_init (NULL); + + mysql_ssl_set (obj->mysql, NULL, NULL, ca, NULL, NULL); +} + +static void db_mysql_set_value (GValue * value, GType type, const gchar * string, gsize len) +{ + if (!string) + { + g_value_init (value, GVN_TYPE_NULL); + return; + } + + g_value_init (value, type); + + switch (type) + { + case G_TYPE_BOOLEAN: + g_value_set_boolean (value, atoi (string)); + break; + case G_TYPE_CHAR: + g_value_set_schar (value, atoi (string)); + break; + case G_TYPE_INT: + g_value_set_int (value, atoi (string)); + break; + case G_TYPE_UINT: + g_value_set_int (value, (guint) atoi (string)); + break; + case G_TYPE_LONG: + g_value_set_long (value, g_ascii_strtoll (string, NULL, 0)); + break; + case G_TYPE_ULONG: + g_value_set_ulong (value, g_ascii_strtoull (string, NULL, 0)); + break; + case G_TYPE_FLOAT: + g_value_set_float (value, atof (string)); + break; + case G_TYPE_DOUBLE: + g_value_set_double (value, g_ascii_strtod (string, NULL)); + break; + case G_TYPE_STRING: + g_value_set_string (value, string); + break; + default: + if (type == G_TYPE_DATE) + { + GDate * date = NULL; + + if (len >= 10) + { + GDateDay d = atoi (string + 8); + GDateMonth m = atoi (string + 5); + GDateYear y = atoi (string); + + if (g_date_valid_dmy (d, m, y)) + date = g_date_new_dmy (d, m, y); + } + + g_value_take_boxed (value, date); + } + else if (type == G_TYPE_DATE_TIME) + { + GDateTime * date_time = NULL; + + if (len >= 19) + date_time = g_date_time_new_local ( + atoi (string) + ,atoi (string + 5) + ,atoi (string + 8) + ,atoi (string + 11) + ,atoi (string + 14) + ,atoi (string + 17) + ); + + g_value_take_boxed (value, date_time); + } + else if (type == G_TYPE_BYTES) + g_value_take_boxed (value, g_bytes_new (string, len)); + else + g_warning ("DbMysql: Can't handle this value type: %s", + g_type_name (type)); + } +} + +static DbResultSet * db_mysql_query (DbMysql * obj, const gchar * sql, GError ** err) +{ + gint i, j; + guint errno = 0; + gulong * lengths; + GValue def = {0}; + MYSQL_ROW myrow; + MYSQL_RES * res; + MYSQL_FIELD * field; + DbRow * row; + DbColumn * column; + DbResult * result; + DbResultSet * set = db_result_set_new (); + + if (!mysql_query (obj->mysql, sql)) + do { + if ((res = mysql_store_result (obj->mysql))) + { + result = g_new (DbResult, 1); + result->nrows = mysql_num_rows (res); + result->ncols = mysql_num_fields (res); + result->column = g_new (DbColumn, result->ncols); + result->data = g_ptr_array_new_full (result->nrows + 1, + (GDestroyNotify) db_row_free); + set->results = g_slist_append (set->results, result); + + field = mysql_fetch_fields (res); + + GType gtypes[result->ncols]; + + for (i = 0; i < result->ncols; i++) + { + column = &result->column[i]; + column->info = 0; + column->name = g_strdup (field[i].org_name); + column->display = g_strdup (field[i].name); + column->table = g_strdup (field[i].org_table); + + switch (field[i].type) + { + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_YEAR: + gtypes[i] = G_TYPE_INT; + break; + case MYSQL_TYPE_LONGLONG: + gtypes[i] = G_TYPE_LONG; + break; + case MYSQL_TYPE_FLOAT: + gtypes[i] = G_TYPE_FLOAT; + break; + case MYSQL_TYPE_DOUBLE: + gtypes[i] = G_TYPE_DOUBLE; + break; + case MYSQL_TYPE_DATE: + gtypes[i] = G_TYPE_DATE; + break; + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + gtypes[i] = G_TYPE_DATE_TIME; + break; + case MYSQL_TYPE_BLOB: + gtypes[i] = G_TYPE_BYTES; + break; + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_NULL: + default: + gtypes[i] = G_TYPE_STRING; + } + + if (field[i].flags & AUTO_INCREMENT_FLAG) + { + SqlFunction * func = sql_function_new ("LAST_INSERT_ID", NULL); + + g_value_init (&def, SQL_TYPE_FUNCTION); + g_value_take_object (&def, g_object_ref_sink (func)); + } + else + db_mysql_set_value (&def, gtypes[i], field[i].def, field[i].def_length); + + column->spec = gvn_param_spec_new_with_attrs (gtypes[i], + FALSE, !(field[i].flags & NOT_NULL_FLAG), &def); + g_value_unset (&def); + + if (field[i].flags & PRI_KEY_FLAG) + column->info |= DB_COLUMN_PRI_KEY; + } + + for (i = 0; (myrow = mysql_fetch_row (res)); i++) + { + lengths = mysql_fetch_lengths (res); + row = db_row_new (result->ncols, i); + g_ptr_array_add (result->data, row); + + for (j = 0; j < result->ncols; j++) + db_mysql_set_value (&row->value[j], gtypes[j], myrow[j], lengths[j]); + } + + mysql_free_result (res); + } + else if (mysql_field_count (obj->mysql) == 0) + { + result = g_new (DbResult, 1); + result->nrows = mysql_affected_rows (obj->mysql); + result->ncols = 0; + result->column = NULL; + result->data = NULL; + set->results = g_slist_append (set->results, result); + } + } + while (mysql_more_results (obj->mysql) && !mysql_next_result (obj->mysql)); + + if ((errno = mysql_errno (obj->mysql))) + { + switch (errno) + { + case CR_SERVER_LOST: + case CR_SERVER_GONE_ERROR: + errno = DB_CONN_ERROR_LOST; + break; + case CR_OUT_OF_MEMORY: + case CR_COMMANDS_OUT_OF_SYNC: + errno = DB_CONN_ERROR_QUERY_FATAL; + break; + case CR_UNKNOWN_ERROR: + default: + errno = DB_CONN_ERROR_UNKNOW; + } + + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,errno + ,"%s", mysql_error (obj->mysql) + ); + db_result_set_free (set); + set = NULL; + } + + return set; +} + +static void db_mysql_kill_query (DbMysql * obj) +{ + if (obj->mysql) + { + gboolean killed = FALSE; + MYSQL * new_conn = mysql_init (NULL); + + if (new_conn + && mysql_real_connect (new_conn, obj->host, obj->user, obj->pass, NULL, 0, NULL, 0)) + { + gchar * sql = g_strdup_printf ("KILL QUERY %lu", obj->thread_id); + + if (!mysql_query (new_conn, sql)) + { + MYSQL_RES * res = mysql_store_result (new_conn); + mysql_free_result (res); + killed = TRUE; + } + + g_free (sql); + } + if (new_conn) + { + if (mysql_errno (new_conn)) + g_warning ("DbMysql: %s", mysql_error (new_conn)); + + mysql_close (new_conn); + } + + if (!killed && obj->socket != -1 && !shutdown (obj->socket, SHUT_RDWR)) + obj->socket = -1; + } +} + +static void db_mysql_value_render (SqlValue * obj, SqlRender * render) +{ + if (G_VALUE_TYPE (obj->value) == G_TYPE_BYTES) + { + gsize to_size; + gsize from_size; + GString * buffer = render->buffer; + GBytes * bin = g_value_get_boxed (obj->value); + const gchar * from = g_bytes_get_data (bin, &from_size); + + sql_render_add_espace (render); + sql_render_append (render, "'"); + g_string_set_size (buffer, buffer->len + (from_size * 2 + 1)); + to_size = mysql_real_escape_string ( + DB_MYSQL (render->data)->mysql, + &(buffer->str[buffer->len]), from, from_size + ); + buffer->len += to_size; + sql_render_append (render, "'"); + } + else + sql_object_render (SQL_OBJECT (obj), render); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_mysql_init (DbMysql * obj) +{ + SqlRender * render = sql_render_new ('`'); + sql_render_register_function (render, SQL_TYPE_VALUE, + (SqlRenderFunc) db_mysql_value_render); + + DB_PLUGIN (obj)->render = render; + obj->mysql = NULL; + obj->host = NULL; + obj->user = NULL; + obj->pass = NULL; + obj->thread_id = 0; + obj->socket = -1; + obj->is_open = FALSE; +} + +static void db_mysql_class_init (DbMysqlClass * k) +{ + DbPluginClass * klass = DB_PLUGIN_CLASS (k); + klass->open = (DbPluginOpenFunc) db_mysql_open; + klass->close = (DbPluginCloseFunc) db_mysql_close; + klass->set_ssl = (DbPluginSetSSL) db_mysql_set_ssl; + klass->query = (DbPluginQueryFunc) db_mysql_query; + klass->kill_query = (DbPluginKillQueryFunc) db_mysql_kill_query; +} diff --git a/plugin/mysql/db-mysql.h b/plugin/mysql/db-mysql.h new file mode 100644 index 0000000..31fec5d --- /dev/null +++ b/plugin/mysql/db-mysql.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_MYSQL_H +#define DB_MYSQL_H + +#include +#include + +#define DB_TYPE_MYSQL (db_mysql_get_type ()) +#define DB_MYSQL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_MYSQL, DbMysql)) +#define DB_IS_MYSQL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_MYSQL)) +#define DB_MYSQL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_MYSQL, DbMysqlClass)) +#define DB_IS_MYSQL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_MYSQL)) +#define DB_MYSQL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_MYSQL, DbMysqlClass)) + +typedef struct _DbMysql DbMysql; +typedef struct _DbMysqlClass DbMysqlClass; + +struct _DbMysql +{ + DbPlugin parent; + MYSQL * mysql; + gchar * host; + gchar * user; + gchar * pass; + gulong thread_id; + gint socket; + gboolean is_open; +}; + +struct _DbMysqlClass +{ + DbPluginClass parent; +}; + +GType db_mysql_get_type (); +DbConn * db_mysql_new (); + +#endif \ No newline at end of file diff --git a/plugin/pg/Makefile.am b/plugin/pg/Makefile.am new file mode 100644 index 0000000..39731a1 --- /dev/null +++ b/plugin/pg/Makefile.am @@ -0,0 +1,41 @@ +include $(top_srcdir)/Makefile.decl + +pg_lib_LTLIBRARIES = libdbpg.la + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I/usr/include/postgresql \ + $(glib_CFLAGS) +libdbpg_la_LIBADD = \ + $(glib_LIBS) \ + $(top_builddir)/db/libdb.la \ + -lpq +libdbpg_la_LDFLAGS = -avoid-version +libdbpg_la_SOURCES = \ + db-pg.h \ + db-pg.c + +# FIXME error durante la ejecucion del parser: +# llama a la funcion Parse en gram.c de sql en vez de en db-pg-gram.c +# db-pg-parser.h \ +# db-pg-parser.c + +#lemonpath = $(top_builddir)/sql/parser +#db-pg-parser.c: $(SCAN) db-pg-gram.c +# $(ragel_v)$(RAGEL) -C -G2 -o db-pg-parser.c $(SCAN) + +#db-pg-gram.h: db-pg-gram.c +#db-pg-gram.c: $(GRAM) $(lemonpath)/lemon +# $(lemon_v)$(lemonpath)/lemon -q $(GRAM) + +#$(lemonpath)/lemon: $(lemonpath)/lemon.c $(lemonpath)/lempar.c +# $(CC) -o $(lemonpath)/lemon $(lemonpath)/lemon.c + +#SCAN = db-pg-scan.rl +#GRAM = db-pg-gram.y + +#CLEANFILES = db-pg-gram.c db-pg-gram.h db-pg-parser.c + +install-data-hook: + rm -f $(DESTDIR)$(pg_libdir)/libdbpg.la + rm -f $(DESTDIR)$(pg_libdir)/libdbpg.a \ No newline at end of file diff --git a/plugin/pg/db-pg.c b/plugin/pg/db-pg.c new file mode 100644 index 0000000..ad060e6 --- /dev/null +++ b/plugin/pg/db-pg.c @@ -0,0 +1,880 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include +#include +#include +// Macros to avoid redefinition warnings for constants +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION +#undef _ + +#include +#include +#include +#include "db-pg.h" + +G_DEFINE_TYPE (DbPg, db_pg, DB_TYPE_PLUGIN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void db_pg_close (DbPlugin * obj) +{ + DbPg * pg = DB_PG (obj); + + if (pg->pg_cancel) + { + PQfreeCancel (pg->pg_cancel); + pg->pg_cancel = NULL; + } + + if (pg->conn) + { + PQfinish (pg->conn); + pg->conn = NULL; + pg->socket = -1; + } +} + +static gboolean db_pg_open (DbPlugin * obj, const gchar * host, + const gchar * schema, const gchar * user, const gchar * pass, GError ** err) +{ + DbPg * pg = DB_PG (obj); + + pg->conn = PQsetdbLogin ( + host + ,NULL + ,NULL + ,NULL + ,schema + ,user + ,pass + ); + + if (!pg->conn) + { + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_OPENING + ,_("Can't allocate the needed memory") + ); + return FALSE; + } + + if (PQstatus (pg->conn) != CONNECTION_OK) + { + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_OPENING + ,"%s", PQerrorMessage (pg->conn) + ); + db_pg_close (obj); + return FALSE; + } + + pg->pg_cancel = PQgetCancel (pg->conn); + pg->socket = PQsocket (pg->conn); + return TRUE; +} + +static void db_pg_set_ssl (DbPg * obj, const gchar * ca) +{ + g_warning ("DbPg: SSL not supported by this plugin"); +} + +//+++++++ Internal Methods for db_pg_query () + +static GType db_pg_get_g_type (Oid type) +{ + switch (type) + { + case BOOLOID: + return G_TYPE_BOOLEAN; + case INT8OID: + return G_TYPE_INT64; + case INT2OID: + case INT4OID: + return G_TYPE_INT; + case TIDOID: + case OIDOID: + return G_TYPE_UINT; + case FLOAT4OID: + return G_TYPE_FLOAT; + case FLOAT8OID: + case NUMERICOID: + return G_TYPE_DOUBLE; + case DATEOID: + return G_TYPE_DATE; + case TIMESTAMPOID: + case TIMESTAMPTZOID: + return G_TYPE_DATE_TIME; + case TIMEOID: + case TIMETZOID: + return GVN_TYPE_TIME; + case BYTEAOID: + return G_TYPE_BYTES; + case ABSTIMEOID: + case RELTIMEOID: + case TINTERVALOID: + case CHAROID: + case TEXTOID: + case NAMEOID: + case XMLOID: + case CSTRINGOID: + case BPCHAROID: + case VARCHAROID: + default: + return G_TYPE_STRING; + } +} + +static void db_pg_set_date_value (GValue * val, Oid oid, gchar * pg_val) +{ + gpointer date = NULL; + + switch(oid) + { + case TIMEOID: // hh:mm:ss + { + gchar ** hms = g_strsplit (pg_val, ":", 3); + + if (g_strv_length (hms) >= 3) + date = gvn_time_new (atoi (hms[0]), atoi (hms[1]), + atoi (hms[2]), 0); + + g_value_init (val, GVN_TYPE_TIME); + + g_strfreev (hms); + break; + } + case TIMETZOID: // hh:mm:ss±zz + { + gchar ** hmsz = g_strsplit_set (pg_val, ":+-", 4); + + if (g_strv_length (hmsz) >= 4) + date = gvn_time_new (atoi (hmsz[0]), atoi(hmsz[1]), + atoi(hmsz[2]), atoi (hmsz[3])); + + g_value_init (val, GVN_TYPE_TIME); + + g_strfreev (hmsz); + break; + } + case TIMESTAMPOID: // yyyy-mm-dd hh:mn:ss + { + gchar ** dt = g_strsplit_set (pg_val, " -:", 6); + + if (g_strv_length (dt) >= 6) + date = g_date_time_new_local + (atoi (dt[0]), atoi (dt[1]), atoi (dt[2]), + atoi (dt[3]), atoi (dt[4]), atoi (dt[5])); + + g_value_init (val, G_TYPE_DATE_TIME); + + g_strfreev (dt); + break; + } + case TIMESTAMPTZOID: // yyyy-mm-dd hh:mm:ss±zz + { + GTimeZone * tz = NULL; + gchar ** dtz = g_strsplit_set (pg_val, " -:+", 7); + + if (strlen (pg_val) >= 16 && g_strv_length (dtz) >= 6) + date = g_date_time_new + (tz = g_time_zone_new (pg_val + 16), + atoi (dtz[0]), atoi (dtz[1]), atoi (dtz[2]), + atoi (dtz[3]), atoi (dtz[4]), atoi (dtz[5])); + + g_value_init (val, G_TYPE_DATE_TIME); + + g_strfreev (dtz); + g_time_zone_unref (tz); + break; + } + case DATEOID: // yyyy-mm-dd + { + gchar ** ymd = g_strsplit (pg_val, "-", 3); + + if (g_strv_length (ymd) >= 3) + date = g_date_new_dmy + (atoi (ymd[2]), atoi (ymd[1]) ,atoi (ymd[0])); + + g_value_init (val, G_TYPE_DATE); + + g_strfreev (ymd); + break; + } + case TINTERVALOID: + // TODO If needed + break; + } + + g_value_take_boxed (val, date); +} + +static void db_pg_set_g_value (GValue * val, GType type, gchar * pg_val) +{ + val = g_value_init (val, type); + + switch (type) + { + case G_TYPE_BOOLEAN: + if(!g_strcmp0(pg_val, "t")) + g_value_set_boolean (val, TRUE); + else + g_value_set_boolean (val, FALSE); + break; + case G_TYPE_UINT: + g_value_set_uint(val, (guint) g_ascii_strtoull (pg_val, NULL, 10)); + break; + case G_TYPE_INT: + g_value_set_int (val, atoi (pg_val)); + break; + case G_TYPE_INT64: + g_value_set_int64 (val, g_ascii_strtoll (pg_val, NULL, 10)); + break; + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + g_value_set_double (val, g_strtod (pg_val, NULL)); + break; + case G_TYPE_STRING: + g_value_set_string (val, pg_val); + break; + default: + if (type == G_TYPE_BYTES) + { + gsize l; + guchar * esc = PQunescapeBytea ((guchar *) pg_val, &l); + GBytes * bin = g_bytes_new (esc, l); + g_free (esc); + g_value_set_boxed (val, bin); + g_bytes_unref (bin); + } + } +} + +static inline gint added (GSList * list, const gchar * target) +{ + gint r = 0; + GSList * n; + + for (n = list; n; n = n->next, r++) + if (!g_strcmp0 ((gchar *) n->data, target)) + return r; + + return -1; +} + +static inline void free_array (const gpointer array) +{ + g_ptr_array_free (array, TRUE); +} + +static DbResultSet * __db_pg_query + (DbPlugin * obj, const gchar * sql, gboolean columns, GError ** err) +{ + DbPg * pg = DB_PG (obj); + + PGresult * res = NULL; + DbResult * result; + DbResultSet * set; + gint i = 0, j = 0; + gint ntup = 0, ncol = 0; + guint error_code = 0; + guint query_count = 0; + guint sel_count = 0; + GSList * list = NULL; + gboolean failed = FALSE; + guint s = 5; + GPtrArray * ind_select = g_ptr_array_sized_new (s); + GPtrArray * types = g_ptr_array_new_full (s, (GDestroyNotify) g_free); + GPtrArray * oid_types = g_ptr_array_new_full (s, (GDestroyNotify) g_free); + GPtrArray * rel_oid = g_ptr_array_new_full (s, (GDestroyNotify) free_array); + GPtrArray * col = g_ptr_array_new_full (s,(GDestroyNotify) free_array); + GPtrArray * name = g_ptr_array_new_full (s,(GDestroyNotify) free_array); + + set = db_result_set_new (); + + if (PQsendQuery (pg->conn, sql) != 1) + { + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_QUERY_FAILED + ,"%s", PQerrorMessage (pg->conn) + ); + failed = TRUE; + } + else while ((res = PQgetResult (pg->conn))) + { + result = g_new (DbResult, 1); + result->data = NULL; + result->column = NULL; + result->nrows = 0; + result->ncols = 0; + + switch (PQresultStatus (res)) + { + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + { + gchar ** q_t = NULL; + + q_t = g_strsplit (PQcmdStatus (res), " ", G_MAXINT); + + if (!g_strcmp0 (q_t[0],"SELECT")) + { + ntup = PQntuples (res); + result->nrows = ntup; + ncol = PQnfields (res); + result->ncols = ncol; + + gchar * pg_val; + GPtrArray * row = g_ptr_array_new_full + ((guint) ntup + 15, (GDestroyNotify) db_row_free); + GPtrArray * oid = g_ptr_array_sized_new ((guint) ncol); + GPtrArray * col_num = g_ptr_array_sized_new ((guint) ncol); + GType * type = g_new (GType, ncol); + Oid * type_oid = g_new (Oid, ncol); + GPtrArray * col_name = g_ptr_array_new_full ((guint) ncol, (GDestroyNotify) g_free); + + sel_count++; + + for (j = 0; j < ncol; j++) + { + type_oid[j] = PQftype (res, j); + type[j] = db_pg_get_g_type (type_oid[j]); + + g_ptr_array_add (col_num, GINT_TO_POINTER (PQftablecol (res, j))); + g_ptr_array_add (oid, GUINT_TO_POINTER (PQftable (res, j))); + g_ptr_array_add (col_name, g_strdup (PQfname (res, j))); + } + + if (ntup > 0) + for (i = 0; i < ntup; i++) + { + DbRow * r = db_row_new (result->ncols, i); + + for (j = 0; j < ncol; j++) + { + if (!PQgetisnull (res, i, j)) + { + pg_val = PQgetvalue (res, i, j); + + if (type[j] == G_TYPE_DATE + || type[j] == G_TYPE_DATE_TIME + || type[j] == GVN_TYPE_TIME) + db_pg_set_date_value (&(r->value)[j], + type_oid[j], pg_val); + else + db_pg_set_g_value (&(r->value)[j], type[j], pg_val); + } + else // IF NULL: GvnNull type + g_value_init (&(r->value[j]), GVN_TYPE_NULL); + } + + g_ptr_array_add (row, r); + } + + result->data = row; + + g_ptr_array_add (rel_oid, oid); + g_ptr_array_add (col, col_num); + g_ptr_array_add (name, col_name); + g_ptr_array_add (types, type); + g_ptr_array_add (oid_types, type_oid); + g_ptr_array_add (ind_select, GUINT_TO_POINTER (query_count)); + } + + if (!g_strcmp0 (q_t[0], "INSERT") + || !g_strcmp0 (q_t[0], "UPDATE") + || !g_strcmp0 (q_t[0], "DELETE")) + result->nrows = atoi (PQcmdTuples (res)); + + g_strfreev (q_t); + break; + } + case PGRES_BAD_RESPONSE: + error_code = DB_CONN_ERROR_BAD_RESPONSE; + break; + case PGRES_EMPTY_QUERY: + error_code = DB_CONN_ERROR_QUERY_EMPTY; + break; + case PGRES_COPY_OUT: + case PGRES_COPY_IN: + case PGRES_COPY_BOTH: + case PGRES_NONFATAL_ERROR: + error_code = DB_CONN_ERROR_QUERY_NONFATAL; + break; + case PGRES_FATAL_ERROR: + error_code = DB_CONN_ERROR_QUERY_FATAL; + break; + } // switch STATUS + + if (error_code && !failed) + { + PGresult * discarded_res; + + while ((discarded_res = PQgetResult (pg->conn))) + PQclear (discarded_res); + + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,error_code + ,"%s", PQresultErrorMessage (res) + ); + failed = TRUE; + } + + query_count++; + list = g_slist_prepend (list, result); + + PQclear (res); + } // while (res) END + + if (err && *err && PQstatus (pg->conn) != CONNECTION_OK) + (*err)->code = DB_CONN_ERROR_LOST; + + list = g_slist_reverse (list); + + if (!failed && sel_count > 0 && columns) + { + gint k; + Oid t_oid; + GPtrArray * col_num = NULL; + GPtrArray * oid = NULL; + gchar * info_query = g_strdup (""); + gchar * pkey_query; + gchar * pkey_buff = NULL // buffers for memory leak avoidance + ,* info_buff = NULL; + +// Set info_query string using the previously stored table OIDs and column numbers + for (i = 0; i < (gint) sel_count; i++) + { + pkey_query = g_strdup (""); + oid = g_ptr_array_index (rel_oid, i); + col_num = g_ptr_array_index (col, i); + + info_buff = info_query; + info_query = g_strconcat (info_query, + "SELECT attname, attrelid, attnotnull, adsrc, attnum " + "FROM pg_attribute " + "INNER JOIN pg_class c ON c.oid = attrelid " + "LEFT JOIN pg_attrdef a " + "ON a.adrelid = attrelid AND a.adnum = attnum " + "WHERE attisdropped = false AND (c.oid, attnum) IN (" + , NULL); + g_free (info_buff); + + pkey_buff = pkey_query; + pkey_query = g_strconcat (pkey_query, + "SELECT c.oid, relname, indkey " + "FROM pg_class c " + "LEFT JOIN pg_index i ON indrelid = c.oid " + "AND indisprimary " + "WHERE c.oid IN (" + , NULL); + g_free (pkey_buff); + + gint ncolumns = (gint) oid->len; + gchar * buff_j = NULL; + + for (j = 0; j < ncolumns; j++) + { + t_oid = GPOINTER_TO_UINT (g_ptr_array_index (oid, j)); + + buff_j = g_strdup_printf ("%d", t_oid); + pkey_buff = pkey_query; + pkey_query = g_strconcat (pkey_query, buff_j, NULL); + g_free (pkey_buff); + g_free (buff_j); + + if (j < ncolumns-1 && ncolumns > 1) + { + pkey_buff = pkey_query; + pkey_query = g_strconcat (pkey_query, ",", NULL); + g_free (pkey_buff); + } + + buff_j = g_strdup_printf ("(%d,%d)" + ,t_oid + ,GPOINTER_TO_INT (g_ptr_array_index (col_num, j))); + info_buff = info_query; + info_query = g_strconcat (info_query, buff_j, NULL); + g_free (buff_j); + g_free (info_buff); + + info_buff = info_query; + if (j < ncolumns-1 && ncolumns > 1) + info_query = g_strconcat (info_query, ",", NULL); + else + info_query = g_strconcat (info_query, ")", NULL); + g_free (info_buff); + } + + pkey_buff = pkey_query; + pkey_query = g_strconcat (pkey_query, ");", NULL); + g_free (pkey_buff); + + info_buff = info_query; + info_query = g_strconcat (info_query, ";", NULL); + g_free (info_buff); + + info_buff = info_query; + info_query = g_strconcat (info_query, pkey_query, NULL); + g_free (info_buff); + g_free (pkey_query); + } + + PGresult * res_col; + PQsendQuery (pg->conn, info_query); + + DbResult * r = NULL; + GValue * def = NULL; + Oid * tab_oid = NULL; + gboolean * nullable = NULL; + guint * col_n = NULL; + i = 0; + + while ((res_col = PQgetResult (pg->conn)) && !failed) + { + if (PQresultStatus (res_col) != PGRES_TUPLES_OK) + { + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_QUERY_FAILED + ,"%s", PQresultErrorMessage (res_col) + ); + failed = TRUE; + } + else + { + guint ind = i/2; + ntup = PQntuples (res_col); + guint x = i%2; + + if (!x) + { + GPtrArray * col_iter = g_ptr_array_index (col, ind) + ,* rel_iter = g_ptr_array_index (rel_oid, ind) + ,* name_array; + + r = g_slist_nth_data + (list, GPOINTER_TO_UINT (g_ptr_array_index (ind_select, ind))); + ncol = r->ncols; + r->column = g_new (DbColumn, ncol); + // Relates each column with its corresponding row in the aux query + gint col_tup[ncol]; + // Array of the default values for the columns + def = g_new0 (GValue, ncol); + // Array of GvnParamSpecFlags for the columns + nullable = g_new (gboolean, ncol); + tab_oid = g_new (Oid, ncol); + col_n = g_new (guint, ncol); + + for (j = 0; j < ncol; j++) + { + r->column[j].spec = NULL; + r->column[j].info = 0; + r->column[j].name = NULL; + r->column[j].table = NULL; + + if (GPOINTER_TO_INT (g_ptr_array_index (col_iter, j)) == 0) + { + gchar * fname; + + name_array = g_ptr_array_index (name, ind); + fname = g_strdup (g_ptr_array_index (name_array, j)); + + // Set the metadata if it is a *CALCULATED FIELD* + col_tup[j] = -1; + + if (fname[0] == '?') + { + r->column[j].name = g_strdup (""); + g_free (fname); + } + else + r->column[j].name = fname; + + r->column[j].display = g_strdup (r->column[j].name); + r->column[j].table = g_strdup (""); + r->column[j].spec = gvn_param_spec_new_with_attrs + (((GType*) g_ptr_array_index (types, ind))[j] + , FALSE, FALSE, NULL); + } + else + for (k = 0; k < ntup; k++) + if (GPOINTER_TO_INT (g_ptr_array_index (col_iter, j)) + == atoi (PQgetvalue (res_col, k, 4)) + && GPOINTER_TO_INT (g_ptr_array_index (rel_iter, j)) + == atoi (PQgetvalue (res_col, k, 1))) + { + col_tup[j] = k; + break; + } + + if (col_tup[j] >= 0) // NOT a calculated field. + { + gint ctup = col_tup[j]; + GType type = ((GType*) g_ptr_array_index (types, ind))[j]; + Oid oid = ((Oid*) g_ptr_array_index (oid_types, ind))[j]; + gchar * fname = PQgetvalue (res_col, ctup, 0); + gchar * fdisp; + + name_array = g_ptr_array_index (name, ind); + fdisp = g_ptr_array_index (name_array, j); + + r->column[j].name = g_strdup (fname); + r->column[j].display = g_strdup (fdisp); + + // Getting the default value from res_col //FIXME use the parser + if (!PQgetisnull (res_col, ctup, 3)) + { + gchar * pg_val = PQgetvalue (res_col, ctup, 3); + + if (type == G_TYPE_STRING || type == G_TYPE_CHAR + || type == G_TYPE_BYTES || type == G_TYPE_DATE + || type == G_TYPE_DATE_TIME || type == GVN_TYPE_TIME) + { + gchar ** split = NULL; + + if (type == G_TYPE_DATE + || type == G_TYPE_DATE_TIME + || type == GVN_TYPE_TIME) + db_pg_set_date_value (&def[j], oid + ,(split = g_strsplit + (pg_val, "'", G_MAXINT))[1]); + else + db_pg_set_g_value (&def[j], type + ,(split = g_strsplit + (pg_val, "'", G_MAXINT))[1]); + + g_strfreev (split); + } + else + { + if (g_str_has_prefix (pg_val, "nextval")) + {// Serial fields + GValue * v = g_new0 (GValue, 1); + gchar ** split = g_strsplit_set (pg_val, "(':)", G_MAXINT8); + SqlObject * function = sql_function_new ("currval", NULL); + g_value_set_string (g_value_init (v, G_TYPE_STRING), split[2]); + sql_object_add_child (function, "params", sql_value_new_with_value (v)); + g_value_unset (v); + g_free (v); + g_value_take_object (g_value_init (&def[j], SQL_TYPE_FUNCTION), + g_object_ref_sink (function)); + g_strfreev (split); + } + else + db_pg_set_g_value (&def[j], type, pg_val); + } + } + else + g_value_init (&def[j], GVN_TYPE_NULL); + + + // Checking whether the column can be NULL + nullable[j] = !g_strcmp0 (PQgetvalue (res_col, ctup, 2),"t") + ? FALSE : TRUE; + + tab_oid[j] = atoi (PQgetvalue (res_col, ctup, 1)); + r->column[j].info = 0; + col_n[j] = atoi (PQgetvalue (res_col, ctup, 4)); + } + } + } + else + { + guint nkeys; + gchar ** pkey = NULL; + GSList * prev_tables = NULL; + guint nedit = 0, l; + struct + { + gchar * name; + guint keys[ncol]; + guint num; + guint total; + } + edit[ntup]; + + for (j = 0; j < ncol; j++) + { + GType type; + + for (k = 0; k < ntup; k++) + if (tab_oid[j] == atoi (PQgetvalue (res_col, k, 0))) + { + guint n; + + if (!r->column[j].table) + r->column[j].table = + g_strdup (PQgetvalue (res_col, k, 1)); + + g_strfreev (pkey); + pkey = g_strsplit (PQgetvalue (res_col, k, 2), " ", G_MAXINT); + nkeys = g_strv_length (pkey); + + for (n = 0; n < nkeys; n++) + if (atoi (pkey[n]) == col_n[j]) + { + gint pos = added (prev_tables, + r->column[j].table); + + if (pos >= 0) + { + edit[pos].keys[edit[pos].num] = j; + edit[pos].num++; + } + else + { + edit[nedit].name = r->column[j].table; + edit[nedit].keys[0] = j; + edit[nedit].num = 1; + edit[nedit].total = nkeys; + nedit++; + prev_tables = g_slist_append + (prev_tables, r->column[j].table); + } + } + } + + type = ((GType *) g_ptr_array_index (types, ind))[j]; + + if (!r->column[j].spec) + r->column[j].spec = gvn_param_spec_new_with_attrs + (type, FALSE, nullable[j], &def[j]); + + if (G_IS_VALUE (&def[j])) + g_value_unset (&def[j]); + } + + for (j = 0; j < ncol; j++) + for (k = 0; k < nedit; k++) + if (edit[k].num == edit[k].total + && !g_strcmp0 (r->column[j].table, edit[k].name)) + for (l = 0; l < edit[k].num; l++) + r->column[edit[k].keys[l]].info |= DB_COLUMN_PRI_KEY; + + g_free (nullable); + g_free (def); + g_free (col_n); + g_free (tab_oid); + g_strfreev (pkey); + g_slist_free (prev_tables); + } + } + + i++; + PQclear (res_col); + } + + g_free (info_query); + + PQclear (res_col); + } + + free_array (ind_select); + free_array (types); + free_array (oid_types); + free_array (rel_oid); + free_array (name); + free_array (col); + + set->results = list; + + if (failed) + { + db_result_set_free (set); + return NULL; + } + + return set; +} + +static DbResultSet * db_pg_query (DbPlugin * obj, const gchar * sql, GError ** err) +{ + return __db_pg_query (obj, sql, TRUE, err); +} + +static void db_pg_kill_query (DbPg * obj) +{ + if (obj->conn) + { + gboolean killed = FALSE; + + if (obj->pg_cancel) + { + gchar errbuf[256] = ""; + + if (PQcancel (obj->pg_cancel, errbuf, 256) == 1) + killed = TRUE; + else + g_warning ("DbPg: %s", errbuf); + } + + if (!killed && obj->socket != -1 && !shutdown (obj->socket, SHUT_RDWR)) + obj->socket = -1; + } +} + +static void db_pg_value_render (SqlValue * obj, SqlRender * render) +{ + if (G_VALUE_TYPE (obj->value) == G_TYPE_BYTES) + { + gsize len; + gchar * buffer; + GBytes * bin = g_value_get_boxed (obj->value); + buffer = (gchar *) PQescapeByteaConn ( + DB_PG (render->data)->conn + ,g_bytes_get_data (bin, NULL) + ,g_bytes_get_size (bin) + ,&len + ); + sql_render_printf (render, "E'%s'::bytea", buffer); + PQfreemem (buffer); + } + else + sql_object_render (SQL_OBJECT (obj), render); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_pg_init (DbPg * obj) +{ + SqlRender * render = sql_render_new ('"'); + sql_render_register_function (render, SQL_TYPE_VALUE, + (SqlRenderFunc) db_pg_value_render); + + DB_PLUGIN (obj)->render = render; + obj->conn = NULL; + obj->socket = -1; +} + +static void db_pg_class_init (DbPgClass * k) +{ + DbPluginClass * klass = DB_PLUGIN_CLASS (k); + klass->open = db_pg_open; + klass->close = db_pg_close; + klass->set_ssl = (DbPluginSetSSL) db_pg_set_ssl; + klass->query = db_pg_query; + klass->kill_query = (DbPluginKillQueryFunc) db_pg_kill_query; +} diff --git a/plugin/pg/db-pg.h b/plugin/pg/db-pg.h new file mode 100644 index 0000000..aa4d646 --- /dev/null +++ b/plugin/pg/db-pg.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef DB_PG_H +#define DB_PG_H + +#include + +#define DB_TYPE_PG (db_pg_get_type ()) +#define DB_PG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_PG, DbPg)) +#define DB_IS_PG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_PG)) +#define DB_PG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_PG, DbPgClass)) +#define DB_IS_PG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_PG)) +#define DB_PG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_PG, DbPgClass)) + +typedef struct _DbPg DbPg; +typedef struct _DbPgClass DbPgClass; + +struct _DbPg +{ + DbPlugin parent; + PGconn * conn; + PGcancel * pg_cancel; + gint socket; +}; + +struct _DbPgClass +{ + DbPluginClass parent; +}; + +GType db_pg_get_type (); +DbConn * db_pg_new (); + +#endif \ No newline at end of file diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..fe62387 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,47 @@ +gvn/gvn-misc.c +gvn/gvn-param-spec.c +gvn/gvn-param.c +gvn/gvn-value.c + +sql/sql-render.c +sql/parser/gram.c +sql/sql-value.c +sql/sql-string.c + +plugin/mysql/db-mysql.c +plugin/pg/db-pg.c + +db/db-iterator.c +db/db-model.c +db/db-calc.c +db/db-param.c +db/db-conn.c +db/db-request.c +db/db-file-loader.c + +vn/vn-gui.c +vn/vn-model.c +vn/vn-grid.c +vn/vn-handler.c +vn/vn-form.c +vn/vn-login.c +vn/vn-field.c +vn/field/vn-entry.c +vn/field/vn-spin.c +vn/field/vn-combo.c +vn/field/vn-completion.c +vn/field/vn-date-chooser.c +vn/field/vn-image.c +vn/field/vn-http-image.c +vn/vn-column.c +vn/column/vn-column-entry.c +vn/column/vn-column-spin.c +vn/column/vn-column-combo.c +vn/column/vn-column-image.c + +vn/gui/login.glade +vn/gui/main.glade +vn/gui/child-window.glade +vn/gui/actions.glade + +module/data/users.glade diff --git a/po/POTFILES.skip b/po/POTFILES.skip new file mode 100644 index 0000000..926cfab --- /dev/null +++ b/po/POTFILES.skip @@ -0,0 +1,13 @@ +module/data/example.xml +module/data/consulter.glade +module/data/customer.glade +module/data/signer.glade +module/src/vn-consulter.c +module/src/vn-customer.c +module/src/vn-signer.c +module/src/vn-users.c + +template/lib-object.c + +main/vn-hedera.desktop.in.in +main/vn-hedera.desktop.in diff --git a/po/ca.po b/po/ca.po new file mode 100644 index 0000000..b1e1b14 --- /dev/null +++ b/po/ca.po @@ -0,0 +1,1075 @@ +# Catalan translations for hedera package. +# Copyright (C) 2012 THE hedera'S COPYRIGHT HOLDER +# This file is distributed under the same license as the hedera package. +# Alejandro T. Colombini Gómez , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: hedera 1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-07-10 11:56+0200\n" +"PO-Revision-Date: 2013-06-04 13:38+0200\n" +"Last-Translator: Alejandro T. Colombini Gómez \n" +"Language-Team: Catalan\n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. * +#. * SECTION: gvn-misc +#. * @Short_description: Miscelaneous utility functions +#. * @Title: Miscelaneous utility functions +#. * +#: ../gvn/gvn-misc.c:27 ../gvn/gvn-misc.c:31 +msgid "Err" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Mon" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Tue" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Wed" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Thu" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Fri" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Sat" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Sun" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Jan" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Feb" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Mar" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Apr" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "May" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Jun" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Jul" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Aug" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Sep" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Oct" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Nov" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Dec" +msgstr "" + +#: ../gvn/gvn-param-spec.c:263 +#, c-format +msgid "Param not editable" +msgstr "Parametre no editable" + +#: ../gvn/gvn-param-spec.c:268 +#, c-format +msgid "Param can't be NULL" +msgstr "El parametre no pot ser nul" + +#: ../gvn/gvn-param-spec.c:274 +#, c-format +msgid "Incompatible type for this param" +msgstr "Tipus incompatible per a aquest parametre" + +#: ../gvn/gvn-param.c:443 ../sql/sql-value.c:235 ../vn/vn-field.c:518 +msgid "Value" +msgstr "" + +#: ../gvn/gvn-param.c:444 +msgid "The value of the param" +msgstr "" + +#: ../gvn/gvn-param.c:450 +msgid "Master" +msgstr "" + +#: ../gvn/gvn-param.c:451 +msgid "The master GvnParam of this parameter" +msgstr "" + +#: ../gvn/gvn-param.c:457 ../db/db-iterator.c:1070 +msgid "Mode" +msgstr "" + +#: ../gvn/gvn-param.c:458 +msgid "The mode of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:464 +msgid "Status" +msgstr "Estat" + +#: ../gvn/gvn-param.c:465 +msgid "The current status of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:471 +msgid "Spec" +msgstr "" + +#: ../gvn/gvn-param.c:472 +msgid "The spec of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:478 ../vn/vn-field.c:546 +msgid "Glib Type" +msgstr "" + +#: ../gvn/gvn-param.c:479 ../vn/vn-field.c:547 +msgid "The type of the value" +msgstr "" + +#: ../gvn/gvn-param.c:485 ../vn/vn-field.c:553 ../vn/vn-column.c:354 +msgid "Editable" +msgstr "" + +#: ../gvn/gvn-param.c:486 +msgid "Whether the param value can be modified" +msgstr "" + +#: ../gvn/gvn-param.c:492 ../vn/vn-field.c:560 +msgid "Null" +msgstr "" + +#: ../gvn/gvn-param.c:493 +msgid "Whether the param value can be of type GVN_TYPE_NULL" +msgstr "" + +#: ../gvn/gvn-param.c:499 ../vn/vn-field.c:567 +msgid "Default Value" +msgstr "" + +#: ../gvn/gvn-param.c:500 ../vn/vn-field.c:568 +msgid "The default value" +msgstr "" + +#: ../gvn/gvn-value.c:434 +#, c-format +msgid "Attempting to compare invalid types: %s\n" +msgstr "S'ha intentat comparar tipus invàlids: %s\n" + +#: ../sql/sql-render.c:413 +msgid "Delimiter" +msgstr "" + +#: ../sql/sql-render.c:414 +msgid "The character used for delimite the name of fields, tables..." +msgstr "" + +#: ../sql/sql-value.c:228 +msgid "Param" +msgstr "" + +#: ../sql/sql-value.c:229 +msgid "The param which is linked" +msgstr "" + +#: ../sql/sql-value.c:236 +msgid "The value" +msgstr "" + +#: ../sql/sql-string.c:198 ../db/db-iterator.c:1063 ../db/db-model.c:3411 +#: ../db/db-request.c:466 ../vn/field/vn-combo.c:356 +#: ../vn/field/vn-completion.c:315 ../vn/column/vn-column-combo.c:297 +msgid "SQL" +msgstr "" + +#: ../sql/sql-string.c:199 +msgid "An arbitrary SQL string" +msgstr "" + +#: ../plugin/mysql/db-mysql.c:68 ../plugin/pg/db-pg.c:76 +#, c-format +msgid "Can't allocate the needed memory" +msgstr "" + +#: ../db/db-iterator.c:284 +msgid "DbIterator: Can't reassign the 'model' property" +msgstr "" + +#: ../db/db-iterator.c:942 +msgid "DbIterator: Can't set the 'conn' property because model isn't set" +msgstr "" + +#: ../db/db-iterator.c:1049 ../db/db-calc.c:329 ../vn/field/vn-combo.c:342 +#: ../vn/column/vn-column-combo.c:283 +msgid "Model" +msgstr "" + +#: ../db/db-iterator.c:1050 +msgid "The DbModel handled by the iterator" +msgstr "" + +#: ../db/db-iterator.c:1056 ../db/db-model.c:3395 ../db/db-request.c:459 +#: ../vn/vn-gui.c:1531 ../vn/vn-form.c:241 ../vn/field/vn-combo.c:349 +#: ../vn/field/vn-completion.c:308 ../vn/column/vn-column-combo.c:290 +msgid "Connection" +msgstr "" + +#: ../db/db-iterator.c:1057 ../vn/field/vn-combo.c:350 +#: ../vn/field/vn-completion.c:309 ../vn/column/vn-column-combo.c:291 +msgid "The connection used by the model" +msgstr "" + +#: ../db/db-iterator.c:1064 ../vn/field/vn-combo.c:357 +#: ../vn/field/vn-completion.c:316 ../vn/column/vn-column-combo.c:298 +msgid "The SQL query used to create the model" +msgstr "" + +#: ../db/db-iterator.c:1071 +msgid "The mode in which the iterator is working" +msgstr "" + +#: ../db/db-iterator.c:1078 +#, fuzzy +msgid "Remember selection" +msgstr "Recordar" + +#: ../db/db-iterator.c:1079 +msgid "Wether to rememeber the selection when model is refreshed" +msgstr "" + +#: ../db/db-model.c:3396 +msgid "The DbConn that manages the connection to the database" +msgstr "" + +#: ../db/db-model.c:3403 ../db/db-request.c:473 +msgid "Statement" +msgstr "" + +#: ../db/db-model.c:3404 +msgid "The statement which retrieves the data" +msgstr "" + +#: ../db/db-model.c:3412 +msgid "" +"Depending on the \"use-file\" property this will be the path to a file with " +"queries for the model or a SQL string" +msgstr "" + +#: ../db/db-model.c:3421 +msgid "Use file" +msgstr "" + +#: ../db/db-model.c:3422 +msgid "" +"If this is set to TRUE, the \"sql\" property will hold the name of a file " +"containing a query, if set to FALSE, \"sql\" is used as an SQL string" +msgstr "" + +#: ../db/db-model.c:3431 +msgid "Main Table" +msgstr "" + +#: ../db/db-model.c:3432 +msgid "The main table of the model" +msgstr "" + +#: ../db/db-model.c:3439 +msgid "Update flags" +msgstr "" + +#: ../db/db-model.c:3440 +msgid "The flags that indicate how a model can be modified" +msgstr "" + +#: ../db/db-model.c:3448 +msgid "Result position" +msgstr "" + +#: ../db/db-model.c:3449 +msgid "" +"The position where the query that will fill the model will be placed in a " +"multi-query" +msgstr "" + +#: ../db/db-calc.c:330 +msgid "The model where the operations will be applied" +msgstr "" + +#: ../db/db-calc.c:337 +msgid "Operation type" +msgstr "" + +#: ../db/db-calc.c:338 +msgid "The type of the operation applied over the function" +msgstr "" + +#: ../db/db-calc.c:347 +msgid "Function" +msgstr "" + +#: ../db/db-calc.c:348 +msgid "The function to execute" +msgstr "" + +#: ../db/db-calc.c:354 +msgid "Data" +msgstr "" + +#: ../db/db-calc.c:355 +msgid "The user provided data for the function" +msgstr "" + +#: ../db/db-calc.c:361 +msgid "Column" +msgstr "" + +#: ../db/db-calc.c:362 +msgid "A column to apply the operations over it" +msgstr "" + +#: ../db/db-param.c:243 ../vn/vn-grid.c:349 ../vn/vn-field.c:532 +msgid "Iterator" +msgstr "" + +#: ../db/db-param.c:244 +msgid "The iterator owner of param" +msgstr "" + +#: ../db/db-param.c:250 ../vn/vn-column.c:340 +msgid "Column index" +msgstr "" + +#: ../db/db-param.c:251 +msgid "The referenced column index" +msgstr "" + +#: ../db/db-param.c:257 ../vn/vn-field.c:539 ../vn/vn-column.c:347 +msgid "Column name" +msgstr "Nom de la columna" + +#: ../db/db-param.c:258 ../vn/vn-column.c:348 +msgid "The referenced column name" +msgstr "Nom de la columna referenciada" + +#: ../db/db-conn.c:279 ../db/db-conn.c:284 +#, c-format +msgid "Can't load DbPlugin '%s': %s" +msgstr "" + +#: ../db/db-conn.c:297 +#, c-format +msgid "Plugin can't be loaded" +msgstr "" + +#: ../db/db-conn.c:413 +#, c-format +msgid "Can't open a new connection, it's already open." +msgstr "" + +#: ../db/db-conn.c:555 +#, c-format +msgid "The query is empty." +msgstr "" + +#: ../db/db-conn.c:1060 +msgid "Plugin" +msgstr "" + +#: ../db/db-conn.c:1061 +msgid "The name of the plugin" +msgstr "" + +#: ../db/db-conn.c:1067 +msgid "Query path" +msgstr "" + +#: ../db/db-conn.c:1068 +msgid "The path where query files are located" +msgstr "" + +#: ../db/db-conn.c:1074 ../db/db-file-loader.c:724 +#: ../vn/column/vn-column-image.c:475 +msgid "Host" +msgstr "" + +#: ../db/db-conn.c:1075 +msgid "The host name to connect to" +msgstr "" + +#: ../db/db-conn.c:1081 ../module/data/users.glade.h:4 +msgid "User" +msgstr "Usuari" + +#: ../db/db-conn.c:1082 +msgid "The user name" +msgstr "Nom d'usuari" + +#: ../db/db-conn.c:1088 +msgid "DB name" +msgstr "" + +#: ../db/db-conn.c:1089 +msgid "The default schema" +msgstr "" + +#: ../db/db-request.c:315 +#, c-format +msgid "The request was canceled" +msgstr "" + +#: ../db/db-request.c:460 +msgid "The connection used to render and execute the query" +msgstr "" + +#: ../db/db-request.c:467 +msgid "The SQL query to execute" +msgstr "" + +#: ../db/db-request.c:474 +msgid "The statement to execute" +msgstr "" + +#: ../db/db-request.c:480 +msgid "Result" +msgstr "" + +#: ../db/db-request.c:481 +msgid "The result data of the query" +msgstr "" + +#: ../db/db-request.c:487 +msgid "Error" +msgstr "" + +#: ../db/db-request.c:488 +msgid "The GError, if an error ocurred" +msgstr "" + +#: ../db/db-file-loader.c:333 +#, c-format +msgid "%s not cached" +msgstr "" + +#: ../db/db-file-loader.c:431 +#, c-format +msgid "Unknown content length of file %s" +msgstr "" + +#: ../db/db-file-loader.c:725 ../vn/column/vn-column-image.c:476 +msgid "The host web server name to get the images" +msgstr "" + +#: ../db/db-file-loader.c:732 ../vn/column/vn-column-image.c:483 +msgid "Path" +msgstr "" + +#: ../db/db-file-loader.c:733 +msgid "The path of the directory to interact with" +msgstr "" + +#: ../db/db-file-loader.c:740 +msgid "Cache directory" +msgstr "" + +#: ../db/db-file-loader.c:741 +msgid "" +"The local directory where the downloaded files will be stored. The default " +"cache directory is 'hedera', under g_get_user_cache_dir()." +msgstr "" + +#: ../db/db-file-loader.c:749 +msgid "Maximal cache size" +msgstr "" + +#: ../db/db-file-loader.c:750 +msgid "The maximal size for the contents of the cache directory" +msgstr "" + +#: ../vn/vn-gui.c:620 +msgid "Connection has been lost. Do you want to reconnect?" +msgstr "" + +#: ../vn/vn-gui.c:627 +msgid "An error occurred in the connection." +msgstr "" + +#: ../vn/vn-gui.c:630 +msgid "Database error" +msgstr "" + +#: ../vn/vn-gui.c:637 +msgid "Unknown error" +msgstr "" + +#: ../vn/vn-gui.c:977 +msgid "Closing connection" +msgstr "" + +#: ../vn/vn-gui.c:979 +msgid "Transaction started" +msgstr "" + +#: ../vn/vn-gui.c:981 +msgid "Connecting" +msgstr "" + +#: ../vn/vn-gui.c:983 +msgid "Connection lost" +msgstr "" + +#: ../vn/vn-gui.c:985 +msgid "Connection closed" +msgstr "" + +#: ../vn/vn-gui.c:987 ../vn/field/vn-http-image.c:118 +msgid "Loading" +msgstr "" + +#: ../vn/vn-gui.c:989 ../vn/gui/main.glade.h:19 +msgid "Ready" +msgstr "" + +#: ../vn/vn-gui.c:1532 +msgid "The connection used by Gui" +msgstr "" + +#: ../vn/vn-gui.c:1538 +msgid "Application" +msgstr "" + +#: ../vn/vn-gui.c:1539 +msgid "The application handler for the entire program" +msgstr "" + +#: ../vn/vn-model.c:303 +#, c-format +msgid "Function vn_model_set_sort_func not implemented" +msgstr "Funció vn_model_set_sort_func no implementada" + +#: ../vn/vn-model.c:310 +#, c-format +msgid "Function vn_model_set_default_sort_func not implemented" +msgstr "Funció vn_model_set_default_sort_func no implementada" + +#: ../vn/vn-grid.c:350 +msgid "The iterator used by VnGrid" +msgstr "" + +#: ../vn/vn-handler.c:65 +msgid "Are you sure you want to delete the selected record?" +msgstr "" + +#: ../vn/vn-handler.c:90 +msgid "Are you sure that you want to undo all changes?" +msgstr "" + +#: ../vn/vn-handler.c:219 +msgid "Undo changes" +msgstr "" + +#: ../vn/vn-handler.c:226 +msgid "Save changes" +msgstr "" + +#: ../vn/vn-handler.c:233 +msgid "Refresh data" +msgstr "" + +#: ../vn/vn-handler.c:240 +msgid "Remove record" +msgstr "" + +#: ../vn/vn-handler.c:247 +msgid "Add record" +msgstr "" + +#: ../vn/vn-handler.c:254 +msgid "Move to the first row" +msgstr "" + +#: ../vn/vn-handler.c:261 +msgid "Move to the previous row" +msgstr "" + +#: ../vn/vn-handler.c:268 +msgid "Move to the next row" +msgstr "" + +#: ../vn/vn-handler.c:275 +msgid "Move to the last row" +msgstr "" + +#: ../vn/vn-handler.c:514 +msgid "Show flags" +msgstr "" + +#: ../vn/vn-handler.c:515 +msgid "Sets the buttons that will be shown on the interface" +msgstr "" + +#: ../vn/vn-handler.c:522 +msgid "Simple record" +msgstr "" + +#: ../vn/vn-handler.c:523 +msgid "Sets if it is used to handle a iterator with a single record" +msgstr "" + +#: ../vn/vn-form.c:227 +msgid "Name" +msgstr "" + +#: ../vn/vn-form.c:228 +msgid "The form name" +msgstr "" + +#: ../vn/vn-form.c:234 +msgid "Gui" +msgstr "" + +#: ../vn/vn-form.c:235 +msgid "The Gui object" +msgstr "" + +#: ../vn/vn-form.c:242 +msgid "The connection used by the module" +msgstr "" + +#: ../vn/vn-form.c:248 +msgid "Module" +msgstr "" + +#: ../vn/vn-form.c:249 +msgid "The module" +msgstr "" + +#: ../vn/vn-login.c:276 ../vn/vn-login.c:278 +msgid "Login error" +msgstr "" + +#: ../vn/vn-login.c:336 +#, c-format +msgid "Bad connection settings, please check it." +msgstr "" + +#: ../vn/vn-login.c:457 +msgid "Application id" +msgstr "" + +#: ../vn/vn-login.c:458 +msgid "The application identifier" +msgstr "" + +#: ../vn/vn-field.c:519 +msgid "The current value of the field" +msgstr "" + +#: ../vn/vn-field.c:525 +msgid "Parameter" +msgstr "" + +#: ../vn/vn-field.c:526 +msgid "The param where the field can read/write its value" +msgstr "" + +#: ../vn/vn-field.c:533 +msgid "The iterator used to get the field param" +msgstr "" + +#: ../vn/vn-field.c:540 +msgid "The column name on the iterator" +msgstr "" + +#: ../vn/vn-field.c:554 +msgid "Whether the field value is user editable" +msgstr "" + +#: ../vn/vn-field.c:561 +msgid "Whether the field value can be of type GVN_TYPE_NULL" +msgstr "" + +#: ../vn/field/vn-entry.c:153 ../vn/field/vn-spin.c:172 +#: ../vn/column/vn-column-entry.c:119 ../vn/column/vn-column-spin.c:138 +msgid "Digits" +msgstr "" + +#: ../vn/field/vn-entry.c:154 ../vn/field/vn-spin.c:173 +#: ../vn/column/vn-column-entry.c:120 ../vn/column/vn-column-spin.c:139 +msgid "The number of decimal places to display." +msgstr "" + +#: ../vn/field/vn-combo.c:328 ../vn/column/vn-column-combo.c:269 +msgid "Index column" +msgstr "" + +#: ../vn/field/vn-combo.c:329 ../vn/column/vn-column-combo.c:270 +msgid "The column index of the model" +msgstr "" + +#: ../vn/field/vn-combo.c:335 ../vn/column/vn-column-combo.c:276 +msgid "Show column" +msgstr "" + +#: ../vn/field/vn-combo.c:336 ../vn/column/vn-column-combo.c:277 +msgid "The column of the model shown by combo" +msgstr "" + +#: ../vn/field/vn-combo.c:343 ../vn/column/vn-column-combo.c:284 +msgid "The model from which the combo takes the values shown in the list" +msgstr "" + +#: ../vn/field/vn-completion.c:322 +msgid "Field" +msgstr "" + +#: ../vn/field/vn-completion.c:323 +msgid "The name of the field used for the search" +msgstr "" + +#: ../vn/field/vn-date-chooser.c:71 +#, c-format +msgid "%s, %u %s %u" +msgstr "" + +#: ../vn/field/vn-date-chooser.c:280 +msgid "Change date" +msgstr "" + +#: ../vn/field/vn-image.c:133 +msgid "Select the image" +msgstr "" + +#: ../vn/field/vn-http-image.c:69 +msgid "Undefined error" +msgstr "" + +#: ../vn/field/vn-http-image.c:145 +msgid "File loader already set" +msgstr "" + +#. Se usará para subir imagenes y cambiar nombres, o abrir un menu contextual +#. g_signal_connect (obj, "event", G_CALLBACK (vn_http_image_cb_event), obj); +#: ../vn/field/vn-http-image.c:184 +msgid "No image set" +msgstr "" + +#: ../vn/field/vn-http-image.c:210 +msgid "File loader" +msgstr "" + +#: ../vn/field/vn-http-image.c:211 +msgid "A DbFileLoader, used to download the files" +msgstr "" + +#: ../vn/field/vn-http-image.c:218 +msgid "File path" +msgstr "" + +#: ../vn/field/vn-http-image.c:219 +msgid "The relative path to the image from file loader path" +msgstr "" + +#: ../vn/field/vn-http-image.c:226 +msgid "Image bytes" +msgstr "" + +#: ../vn/field/vn-http-image.c:227 +msgid "A GBytes structure with the image data" +msgstr "" + +#: ../vn/vn-column.c:341 +msgid "The column index in the model" +msgstr "" + +#: ../vn/vn-column.c:355 +msgid "Whether the column values are editable" +msgstr "" + +#: ../vn/column/vn-column-spin.c:130 +msgid "Climb rate" +msgstr "" + +#: ../vn/column/vn-column-spin.c:131 +msgid "The acceleration rate when you hold down a button." +msgstr "" + +#: ../vn/column/vn-column-image.c:484 +msgid "Base path from the host where the images will be downloaded" +msgstr "" + +#: ../vn/column/vn-column-image.c:491 +msgid "Tooltip path" +msgstr "" + +#: ../vn/column/vn-column-image.c:492 +msgid "" +"Prefix for the path of the images to be shown in the tooltip. Starting after " +"the path of the column and appending the name on each cell" +msgstr "" + +#: ../vn/column/vn-column-image.c:501 +msgid "Tooltip size" +msgstr "" + +#: ../vn/column/vn-column-image.c:502 +msgid "" +"Size of the bigger side of the tooltip images, the another side will be " +"scaled accordingly and smaller images won't be scaled" +msgstr "" + +#: ../vn/gui/login.glade.h:1 +msgid "Configuration" +msgstr "" + +#: ../vn/gui/login.glade.h:2 +msgid "Plugin:" +msgstr "" + +#: ../vn/gui/login.glade.h:3 +msgid "Host:" +msgstr "" + +#: ../vn/gui/login.glade.h:4 +msgid "Schema:" +msgstr "" + +#: ../vn/gui/login.glade.h:5 +msgid "SSL CA:" +msgstr "" + +#: ../vn/gui/login.glade.h:6 +msgid "" +"Path to the file containing the CA certificate, if this is empty SSL won't " +"be used" +msgstr "" + +#: ../vn/gui/login.glade.h:7 +#, fuzzy +msgid "Access" +msgstr "Accés" + +#: ../vn/gui/login.glade.h:8 ../vn/gui/main.glade.h:20 +msgid "User:" +msgstr "Usuari:" + +#: ../vn/gui/login.glade.h:9 ../module/data/users.glade.h:8 +msgid "Password:" +msgstr "Contrasenya:" + +#: ../vn/gui/login.glade.h:10 +msgid "Remember" +msgstr "Recordar" + +#: ../vn/gui/main.glade.h:1 +msgid "Copyright - Verdnatura Levante S. L." +msgstr "Copyright - Verdnatura Levante S. L." + +#: ../vn/gui/main.glade.h:2 +msgid "Management and administration of companies" +msgstr "" + +#: ../vn/gui/main.glade.h:3 +msgid "www.verdnatura.es" +msgstr "www.verdnatura.es" + +#: ../vn/gui/main.glade.h:4 +msgid "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU Lesser General Public\n" +"License along with this program; if not, write to the Free\n" +"Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\n" +"02111-1307 USA." +msgstr "" + +#: ../vn/gui/main.glade.h:18 ../vn/gui/child-window.glade.h:1 +msgid "Hedera" +msgstr "Hedera" + +#: ../vn/gui/main.glade.h:21 +msgid "[user-name]" +msgstr "[user-name]" + +#: ../vn/gui/actions.glade.h:1 +msgid "_Logout" +msgstr "" + +#: ../vn/gui/actions.glade.h:2 +msgid "Logout" +msgstr "" + +#: ../vn/gui/actions.glade.h:3 +msgid "_Quit" +msgstr "" + +#: ../vn/gui/actions.glade.h:4 +msgid "_About" +msgstr "" + +#: ../vn/gui/actions.glade.h:5 +msgid "_File" +msgstr "" + +#: ../vn/gui/actions.glade.h:6 +msgid "_Help" +msgstr "" + +#: ../vn/gui/actions.glade.h:7 +msgid "_Reconnect" +msgstr "" + +#: ../vn/gui/actions.glade.h:8 +msgid "Reconnect" +msgstr "" + +#: ../vn/gui/actions.glade.h:9 +msgid "_Close" +msgstr "" + +#: ../vn/gui/actions.glade.h:10 +msgid "_View" +msgstr "" + +#: ../vn/gui/actions.glade.h:11 +msgctxt "View menu option" +msgid "Dynamic _Tabs" +msgstr "" + +#: ../vn/gui/actions.glade.h:12 +msgid "Don't show tabs if there is only one tab open" +msgstr "" + +#: ../vn/gui/actions.glade.h:13 +msgctxt "View menu option" +msgid "Tool_bar" +msgstr "" + +#: ../module/data/users.glade.h:1 +#, fuzzy +msgid "User name:" +msgstr "Nom d'usuari" + +#: ../module/data/users.glade.h:2 +msgid "Search" +msgstr "" + +#: ../module/data/users.glade.h:3 +msgid "Identifier" +msgstr "" + +#: ../module/data/users.glade.h:5 +msgid "MySQL User" +msgstr "" + +#: ../module/data/users.glade.h:6 +msgid "Enabled" +msgstr "" + +#: ../module/data/users.glade.h:7 +msgid "Name:" +msgstr "" + +#: ../module/data/users.glade.h:9 +msgid "MySQL user:" +msgstr "" + +#: ../module/data/users.glade.h:10 +msgid "Enabled:" +msgstr "" + +#: ../module/data/users.glade.h:11 +msgid "Identifier:" +msgstr "" + +#: ../module/data/users.glade.h:12 +msgid "UID:" +msgstr "" + +#: ../module/data/users.glade.h:13 +msgid "Main group:" +msgstr "" + +#: ../module/data/users.glade.h:14 +msgid "Last change:" +msgstr "" + +#: ../module/data/users.glade.h:15 +msgid "Expires:" +msgstr "" + +#: ../module/data/users.glade.h:16 +msgid "Account" +msgstr "" + +#: ../module/data/users.glade.h:17 +msgid "Alias" +msgstr "" + +#: ../module/data/users.glade.h:18 +msgid "Mail alias" +msgstr "" + +#: ../module/data/users.glade.h:19 +msgid "Extension:" +msgstr "" + +#: ../module/data/users.glade.h:20 +msgid "Secret:" +msgstr "" + +#: ../module/data/users.glade.h:21 +msgid "Call group:" +msgstr "" + +#: ../module/data/users.glade.h:22 +msgid "SIP" +msgstr "" + +#: ../module/data/users.glade.h:23 +msgid "Repeat password:" +msgstr "Repetir contrasenya:" diff --git a/po/es.po b/po/es.po new file mode 100644 index 0000000..1536d26 --- /dev/null +++ b/po/es.po @@ -0,0 +1,1107 @@ +# Spanish translations for hedera package +# Traducciones al español para el paquete hedera. +# Copyright (C) 2012 THE hedera'S COPYRIGHT HOLDER +# This file is distributed under the same license as the hedera package. +# Alejandro T. Colombini Gómez , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: hedera 1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-07-10 11:56+0200\n" +"PO-Revision-Date: 2013-06-04 13:36+0200\n" +"Last-Translator: Alejandro T. Colombini Gómez \n" +"Language-Team: Spanish\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. * +#. * SECTION: gvn-misc +#. * @Short_description: Miscelaneous utility functions +#. * @Title: Miscelaneous utility functions +#. * +#: ../gvn/gvn-misc.c:27 ../gvn/gvn-misc.c:31 +msgid "Err" +msgstr "Err" + +#: ../gvn/gvn-misc.c:28 +msgid "Mon" +msgstr "Lun" + +#: ../gvn/gvn-misc.c:28 +msgid "Tue" +msgstr "Mar" + +#: ../gvn/gvn-misc.c:28 +msgid "Wed" +msgstr "Mie" + +#: ../gvn/gvn-misc.c:28 +msgid "Thu" +msgstr "Jue" + +#: ../gvn/gvn-misc.c:28 +msgid "Fri" +msgstr "Vie" + +#: ../gvn/gvn-misc.c:28 +msgid "Sat" +msgstr "Sa" + +#: ../gvn/gvn-misc.c:28 +msgid "Sun" +msgstr "Dom" + +#: ../gvn/gvn-misc.c:32 +msgid "Jan" +msgstr "Ene" + +#: ../gvn/gvn-misc.c:32 +msgid "Feb" +msgstr "Feb" + +#: ../gvn/gvn-misc.c:32 +msgid "Mar" +msgstr "Mar" + +#: ../gvn/gvn-misc.c:32 +msgid "Apr" +msgstr "Abr" + +#: ../gvn/gvn-misc.c:32 +msgid "May" +msgstr "May" + +#: ../gvn/gvn-misc.c:32 +msgid "Jun" +msgstr "Jun" + +#: ../gvn/gvn-misc.c:33 +msgid "Jul" +msgstr "Jul" + +#: ../gvn/gvn-misc.c:33 +msgid "Aug" +msgstr "Ago" + +#: ../gvn/gvn-misc.c:33 +msgid "Sep" +msgstr "Sep" + +#: ../gvn/gvn-misc.c:33 +msgid "Oct" +msgstr "Oct" + +#: ../gvn/gvn-misc.c:33 +msgid "Nov" +msgstr "Nov" + +#: ../gvn/gvn-misc.c:33 +msgid "Dec" +msgstr "Dic" + +#: ../gvn/gvn-param-spec.c:263 +#, c-format +msgid "Param not editable" +msgstr "Parametro no editable" + +#: ../gvn/gvn-param-spec.c:268 +#, c-format +msgid "Param can't be NULL" +msgstr "El parámetro no puede ser nulo" + +#: ../gvn/gvn-param-spec.c:274 +#, c-format +msgid "Incompatible type for this param" +msgstr "Tipo incompatible para este parametro" + +#: ../gvn/gvn-param.c:443 ../sql/sql-value.c:235 ../vn/vn-field.c:518 +msgid "Value" +msgstr "Valor" + +#: ../gvn/gvn-param.c:444 +msgid "The value of the param" +msgstr "El valor del parámetro" + +#: ../gvn/gvn-param.c:450 +msgid "Master" +msgstr "Maestro" + +#: ../gvn/gvn-param.c:451 +msgid "The master GvnParam of this parameter" +msgstr "El GvnParam maestro de este parámetro" + +#: ../gvn/gvn-param.c:457 ../db/db-iterator.c:1070 +msgid "Mode" +msgstr "Modo" + +#: ../gvn/gvn-param.c:458 +msgid "The mode of the parameter" +msgstr "El modo del parámetro" + +#: ../gvn/gvn-param.c:464 +msgid "Status" +msgstr "Estado" + +#: ../gvn/gvn-param.c:465 +msgid "The current status of the parameter" +msgstr "El estado actual del parámetro" + +#: ../gvn/gvn-param.c:471 +msgid "Spec" +msgstr "Spec" + +#: ../gvn/gvn-param.c:472 +msgid "The spec of the parameter" +msgstr "Datos específicos del parámetro" + +#: ../gvn/gvn-param.c:478 ../vn/vn-field.c:546 +msgid "Glib Type" +msgstr "Tipo Glib" + +#: ../gvn/gvn-param.c:479 ../vn/vn-field.c:547 +msgid "The type of the value" +msgstr "El tipo del valor" + +#: ../gvn/gvn-param.c:485 ../vn/vn-field.c:553 ../vn/vn-column.c:354 +msgid "Editable" +msgstr "Editable" + +#: ../gvn/gvn-param.c:486 +msgid "Whether the param value can be modified" +msgstr "Indica si el parámetro puede modificarse" + +#: ../gvn/gvn-param.c:492 ../vn/vn-field.c:560 +msgid "Null" +msgstr "Nulo" + +#: ../gvn/gvn-param.c:493 +msgid "Whether the param value can be of type GVN_TYPE_NULL" +msgstr "Indica si el parámetro puede ser nulo" + +#: ../gvn/gvn-param.c:499 ../vn/vn-field.c:567 +msgid "Default Value" +msgstr "Valor por defecto" + +#: ../gvn/gvn-param.c:500 ../vn/vn-field.c:568 +msgid "The default value" +msgstr "El valor por defecto" + +#: ../gvn/gvn-value.c:434 +#, c-format +msgid "Attempting to compare invalid types: %s\n" +msgstr "Se ha intentado comparar tipos incompatibles: %s\n" + +#: ../sql/sql-render.c:413 +msgid "Delimiter" +msgstr "Delimitador" + +#: ../sql/sql-render.c:414 +msgid "The character used for delimite the name of fields, tables..." +msgstr "El carácter usado para delimitar los nombres de campos, tablas..." + +#: ../sql/sql-value.c:228 +msgid "Param" +msgstr "Parámetro" + +#: ../sql/sql-value.c:229 +msgid "The param which is linked" +msgstr "El parametro al cual está vinculado" + +#: ../sql/sql-value.c:236 +msgid "The value" +msgstr "Valor" + +#: ../sql/sql-string.c:198 ../db/db-iterator.c:1063 ../db/db-model.c:3411 +#: ../db/db-request.c:466 ../vn/field/vn-combo.c:356 +#: ../vn/field/vn-completion.c:315 ../vn/column/vn-column-combo.c:297 +msgid "SQL" +msgstr "SQL" + +#: ../sql/sql-string.c:199 +msgid "An arbitrary SQL string" +msgstr "Una cadena SQL arbitraria" + +#: ../plugin/mysql/db-mysql.c:68 ../plugin/pg/db-pg.c:76 +#, c-format +msgid "Can't allocate the needed memory" +msgstr "No es posible asignar la memoria necesaria" + +#: ../db/db-iterator.c:284 +msgid "DbIterator: Can't reassign the 'model' property" +msgstr "DbIterator: No puedo reasignarse la propiedad 'model'" + +#: ../db/db-iterator.c:942 +msgid "DbIterator: Can't set the 'conn' property because model isn't set" +msgstr "" +"DbIterator: No se pudo configurar la propiedad porque 'model' no está " +"configurado" + +#: ../db/db-iterator.c:1049 ../db/db-calc.c:329 ../vn/field/vn-combo.c:342 +#: ../vn/column/vn-column-combo.c:283 +msgid "Model" +msgstr "Modelo" + +#: ../db/db-iterator.c:1050 +msgid "The DbModel handled by the iterator" +msgstr "El DbModel manejado por el iterador" + +#: ../db/db-iterator.c:1056 ../db/db-model.c:3395 ../db/db-request.c:459 +#: ../vn/vn-gui.c:1531 ../vn/vn-form.c:241 ../vn/field/vn-combo.c:349 +#: ../vn/field/vn-completion.c:308 ../vn/column/vn-column-combo.c:290 +msgid "Connection" +msgstr "Conexión" + +#: ../db/db-iterator.c:1057 ../vn/field/vn-combo.c:350 +#: ../vn/field/vn-completion.c:309 ../vn/column/vn-column-combo.c:291 +msgid "The connection used by the model" +msgstr "La conexión empleada por el modelo" + +#: ../db/db-iterator.c:1064 ../vn/field/vn-combo.c:357 +#: ../vn/field/vn-completion.c:316 ../vn/column/vn-column-combo.c:298 +msgid "The SQL query used to create the model" +msgstr "La consulta SQL usada para crear el modelo" + +#: ../db/db-iterator.c:1071 +msgid "The mode in which the iterator is working" +msgstr "El modo en el que está trabajando el iterador" + +#: ../db/db-iterator.c:1078 +msgid "Remember selection" +msgstr "Recordar fila seleccionada" + +#: ../db/db-iterator.c:1079 +msgid "Wether to rememeber the selection when model is refreshed" +msgstr "" +"Si se recordará o no la fila seleccionado cuando se refresque el modelo" + +#: ../db/db-model.c:3396 +msgid "The DbConn that manages the connection to the database" +msgstr "La DbConn que controla la conexión a la base de datos" + +#: ../db/db-model.c:3403 ../db/db-request.c:473 +msgid "Statement" +msgstr "Consulta" + +#: ../db/db-model.c:3404 +msgid "The statement which retrieves the data" +msgstr "La consulta que recupera los datos" + +#: ../db/db-model.c:3412 +msgid "" +"Depending on the \"use-file\" property this will be the path to a file with " +"queries for the model or a SQL string" +msgstr "" +"Dependiendo de la propiedad \"use-file\", esta propiedad será la ruta a un " +"fichero con consultas para el modelo o una cadena SQL" + +#: ../db/db-model.c:3421 +msgid "Use file" +msgstr "Usa fichero" + +#: ../db/db-model.c:3422 +msgid "" +"If this is set to TRUE, the \"sql\" property will hold the name of a file " +"containing a query, if set to FALSE, \"sql\" is used as an SQL string" +msgstr "" +"Si está puesto a TRUE, la propiedad \"sql\" contiene el nombre de un " +"archivo, si es falso, \"sql\" se usa como una cadena SQL" + +#: ../db/db-model.c:3431 +msgid "Main Table" +msgstr "Tabla principal" + +#: ../db/db-model.c:3432 +msgid "The main table of the model" +msgstr "La tabla principal del modelo" + +#: ../db/db-model.c:3439 +msgid "Update flags" +msgstr "Flags de actualización" + +#: ../db/db-model.c:3440 +msgid "The flags that indicate how a model can be modified" +msgstr "Indican como se puede modificar el modelo" + +#: ../db/db-model.c:3448 +msgid "Result position" +msgstr "Posición del resultado" + +#: ../db/db-model.c:3449 +msgid "" +"The position where the query that will fill the model will be placed in a " +"multi-query" +msgstr "" +"La posición en la que se situa la consulta que pone datos en el modelo en " +"una consulta múltiple" + +#: ../db/db-calc.c:330 +msgid "The model where the operations will be applied" +msgstr "El modelo en el que se aplicarán las operaciones" + +#: ../db/db-calc.c:337 +msgid "Operation type" +msgstr "Tipo de operación" + +#: ../db/db-calc.c:338 +msgid "The type of the operation applied over the function" +msgstr "El tipo de operación aplicada sobre la función" + +#: ../db/db-calc.c:347 +msgid "Function" +msgstr "Función" + +#: ../db/db-calc.c:348 +msgid "The function to execute" +msgstr "Función a ejecutar" + +#: ../db/db-calc.c:354 +msgid "Data" +msgstr "Datos" + +#: ../db/db-calc.c:355 +msgid "The user provided data for the function" +msgstr "Datos proporcionados por el usuario" + +#: ../db/db-calc.c:361 +msgid "Column" +msgstr "Columna" + +#: ../db/db-calc.c:362 +msgid "A column to apply the operations over it" +msgstr "Una columna sobre la que se aplicarán las operaciones" + +#: ../db/db-param.c:243 ../vn/vn-grid.c:349 ../vn/vn-field.c:532 +msgid "Iterator" +msgstr "Iterator" + +#: ../db/db-param.c:244 +msgid "The iterator owner of param" +msgstr "El iterador dueño del parámetro" + +#: ../db/db-param.c:250 ../vn/vn-column.c:340 +msgid "Column index" +msgstr "Columna usada como índice" + +#: ../db/db-param.c:251 +msgid "The referenced column index" +msgstr "El índice de la columna referenciada" + +#: ../db/db-param.c:257 ../vn/vn-field.c:539 ../vn/vn-column.c:347 +msgid "Column name" +msgstr "Nombre de la columna" + +#: ../db/db-param.c:258 ../vn/vn-column.c:348 +msgid "The referenced column name" +msgstr "" +"El nombre del campo en el Iterator vinculado al Grid al que pretenece la " +"columna" + +#: ../db/db-conn.c:279 ../db/db-conn.c:284 +#, c-format +msgid "Can't load DbPlugin '%s': %s" +msgstr "No se puede cargar el DbPlugin '%s': %s" + +#: ../db/db-conn.c:297 +#, c-format +msgid "Plugin can't be loaded" +msgstr "No se puede cargar el plug-in" + +#: ../db/db-conn.c:413 +#, c-format +msgid "Can't open a new connection, it's already open." +msgstr "No se puede abrir una nueva conexión, ya está abierta" + +#: ../db/db-conn.c:555 +#, c-format +msgid "The query is empty." +msgstr "La consulta está vacía." + +#: ../db/db-conn.c:1060 +msgid "Plugin" +msgstr "Plugin" + +#: ../db/db-conn.c:1061 +msgid "The name of the plugin" +msgstr "El nombre del plugin" + +#: ../db/db-conn.c:1067 +msgid "Query path" +msgstr "Ruta de consultas" + +#: ../db/db-conn.c:1068 +msgid "The path where query files are located" +msgstr "La ruta donde se encuentran los archivos de consultas" + +#: ../db/db-conn.c:1074 ../db/db-file-loader.c:724 +#: ../vn/column/vn-column-image.c:475 +msgid "Host" +msgstr "Host" + +#: ../db/db-conn.c:1075 +msgid "The host name to connect to" +msgstr "El nombre del host al que se conectará" + +#: ../db/db-conn.c:1081 ../module/data/users.glade.h:4 +msgid "User" +msgstr "Usuario" + +#: ../db/db-conn.c:1082 +msgid "The user name" +msgstr "El nombre de usuario" + +#: ../db/db-conn.c:1088 +msgid "DB name" +msgstr "Nombre de la BD" + +#: ../db/db-conn.c:1089 +msgid "The default schema" +msgstr "El esquema por defecto" + +#: ../db/db-request.c:315 +#, c-format +msgid "The request was canceled" +msgstr "Se ha cancelado la petición" + +#: ../db/db-request.c:460 +msgid "The connection used to render and execute the query" +msgstr "La conexión empleada por el módulo" + +#: ../db/db-request.c:467 +msgid "The SQL query to execute" +msgstr "La consulta SQL a ejecutar" + +#: ../db/db-request.c:474 +msgid "The statement to execute" +msgstr "La instrucción a ejecutar" + +#: ../db/db-request.c:480 +msgid "Result" +msgstr "Result" + +#: ../db/db-request.c:481 +msgid "The result data of the query" +msgstr "Los datos resultantes de la consulta" + +#: ../db/db-request.c:487 +msgid "Error" +msgstr "Error" + +#: ../db/db-request.c:488 +msgid "The GError, if an error ocurred" +msgstr "El GError, si ha habido algún error" + +#: ../db/db-file-loader.c:333 +#, c-format +msgid "%s not cached" +msgstr "No se ha guardado %s en la cache" + +#: ../db/db-file-loader.c:431 +#, c-format +msgid "Unknown content length of file %s" +msgstr "Longitud del contenido del archivo %s desconocida" + +#: ../db/db-file-loader.c:725 ../vn/column/vn-column-image.c:476 +msgid "The host web server name to get the images" +msgstr "El nombre del servidor web al que se conectará" + +#: ../db/db-file-loader.c:732 ../vn/column/vn-column-image.c:483 +msgid "Path" +msgstr "Ruta" + +#: ../db/db-file-loader.c:733 +msgid "The path of the directory to interact with" +msgstr "La ruta del directorio con el que se interactuará" + +#: ../db/db-file-loader.c:740 +msgid "Cache directory" +msgstr "Directorio de caché" + +#: ../db/db-file-loader.c:741 +msgid "" +"The local directory where the downloaded files will be stored. The default " +"cache directory is 'hedera', under g_get_user_cache_dir()." +msgstr "" +"El directorio local en el cual se guardarán los archivos descargados.El " +"directorio por defecto es 'hedera', debajo de g_get_user_cache_dir()." + +#: ../db/db-file-loader.c:749 +msgid "Maximal cache size" +msgstr "Tamaño máximo de la caché" + +#: ../db/db-file-loader.c:750 +msgid "The maximal size for the contents of the cache directory" +msgstr "El tamaño máximo para los contenidos del directorio de caché" + +#: ../vn/vn-gui.c:620 +msgid "Connection has been lost. Do you want to reconnect?" +msgstr "Se ha perdido la conexión. ¿Quieres reconectar?" + +#: ../vn/vn-gui.c:627 +msgid "An error occurred in the connection." +msgstr "Ha habido un error en la conexión." + +#: ../vn/vn-gui.c:630 +msgid "Database error" +msgstr "Error en la base de datos" + +#: ../vn/vn-gui.c:637 +msgid "Unknown error" +msgstr "Error desconocido" + +#: ../vn/vn-gui.c:977 +msgid "Closing connection" +msgstr "Cerrando conexión" + +#: ../vn/vn-gui.c:979 +msgid "Transaction started" +msgstr "Transacción iniciada" + +#: ../vn/vn-gui.c:981 +msgid "Connecting" +msgstr "Conectando" + +#: ../vn/vn-gui.c:983 +msgid "Connection lost" +msgstr "Conexión perdida" + +#: ../vn/vn-gui.c:985 +msgid "Connection closed" +msgstr "Conexión cerrada" + +#: ../vn/vn-gui.c:987 ../vn/field/vn-http-image.c:118 +msgid "Loading" +msgstr "Cargando" + +#: ../vn/vn-gui.c:989 ../vn/gui/main.glade.h:19 +msgid "Ready" +msgstr "Listo" + +#: ../vn/vn-gui.c:1532 +msgid "The connection used by Gui" +msgstr "La conexión empleada por Gui" + +#: ../vn/vn-gui.c:1538 +msgid "Application" +msgstr "Aplicación" + +#: ../vn/vn-gui.c:1539 +msgid "The application handler for the entire program" +msgstr "El manejador de la aplicación para todo el programa" + +#: ../vn/vn-model.c:303 +#, c-format +msgid "Function vn_model_set_sort_func not implemented" +msgstr "Función vn_model_set_sort_func no implementada" + +#: ../vn/vn-model.c:310 +#, c-format +msgid "Function vn_model_set_default_sort_func not implemented" +msgstr "Función vn_model_set_default_sort_func no implementada" + +#: ../vn/vn-grid.c:350 +msgid "The iterator used by VnGrid" +msgstr "El iterador empleado por VnGrid" + +#: ../vn/vn-handler.c:65 +msgid "Are you sure you want to delete the selected record?" +msgstr "¿Estás seguro de que quieres eliminar los registros seleccionados?" + +#: ../vn/vn-handler.c:90 +msgid "Are you sure that you want to undo all changes?" +msgstr "¿Estás seguro de que quieres deshacer todos los cambios?" + +#: ../vn/vn-handler.c:219 +msgid "Undo changes" +msgstr "Deshacer cambios" + +#: ../vn/vn-handler.c:226 +msgid "Save changes" +msgstr "Guardar cambios" + +#: ../vn/vn-handler.c:233 +msgid "Refresh data" +msgstr "Refrescar datos" + +#: ../vn/vn-handler.c:240 +msgid "Remove record" +msgstr "Eliminar registro" + +#: ../vn/vn-handler.c:247 +msgid "Add record" +msgstr "Añadir registro" + +#: ../vn/vn-handler.c:254 +msgid "Move to the first row" +msgstr "Mover a la primera fila" + +#: ../vn/vn-handler.c:261 +msgid "Move to the previous row" +msgstr "Mover a la fila anterior" + +#: ../vn/vn-handler.c:268 +msgid "Move to the next row" +msgstr "Mover a la siguiente fila" + +#: ../vn/vn-handler.c:275 +msgid "Move to the last row" +msgstr "Mover a la última fila" + +#: ../vn/vn-handler.c:514 +msgid "Show flags" +msgstr "Visibilidad" + +#: ../vn/vn-handler.c:515 +msgid "Sets the buttons that will be shown on the interface" +msgstr "Indica los botones que se mostrarán en la interfaz" + +#: ../vn/vn-handler.c:522 +msgid "Simple record" +msgstr "Registro simple" + +#: ../vn/vn-handler.c:523 +msgid "Sets if it is used to handle a iterator with a single record" +msgstr "Indica si se utiliza para manejar solo un registro" + +#: ../vn/vn-form.c:227 +msgid "Name" +msgstr "Nombre" + +#: ../vn/vn-form.c:228 +msgid "The form name" +msgstr "El nombre del form" + +#: ../vn/vn-form.c:234 +msgid "Gui" +msgstr "Gui" + +#: ../vn/vn-form.c:235 +msgid "The Gui object" +msgstr "El objeto Gui" + +#: ../vn/vn-form.c:242 +msgid "The connection used by the module" +msgstr "La conexión empleada por el módulo" + +#: ../vn/vn-form.c:248 +msgid "Module" +msgstr "Módulo" + +#: ../vn/vn-form.c:249 +msgid "The module" +msgstr "El módulo" + +#: ../vn/vn-login.c:276 ../vn/vn-login.c:278 +msgid "Login error" +msgstr "Error de identificación" + +#: ../vn/vn-login.c:336 +#, c-format +msgid "Bad connection settings, please check it." +msgstr "Conexión mal configurado, compruébelo por favor" + +#: ../vn/vn-login.c:457 +msgid "Application id" +msgstr "Id de la aplicación" + +#: ../vn/vn-login.c:458 +msgid "The application identifier" +msgstr "El identificador de la aplicación" + +#: ../vn/vn-field.c:519 +msgid "The current value of the field" +msgstr "El valor actual del campo" + +#: ../vn/vn-field.c:525 +msgid "Parameter" +msgstr "Parámetro" + +#: ../vn/vn-field.c:526 +msgid "The param where the field can read/write its value" +msgstr "El parámetro en el que el campo escribe/lee su valor" + +#: ../vn/vn-field.c:533 +msgid "The iterator used to get the field param" +msgstr "El iterador con el que se obtiene el parámetro" + +#: ../vn/vn-field.c:540 +msgid "The column name on the iterator" +msgstr "El nombre de la columna en el iterador" + +#: ../vn/vn-field.c:554 +msgid "Whether the field value is user editable" +msgstr "Si el valor de campo puede ser editado por el usuario" + +#: ../vn/vn-field.c:561 +msgid "Whether the field value can be of type GVN_TYPE_NULL" +msgstr "Si el campo puede ser del tipo GVN_TYPE_NULL" + +#: ../vn/field/vn-entry.c:153 ../vn/field/vn-spin.c:172 +#: ../vn/column/vn-column-entry.c:119 ../vn/column/vn-column-spin.c:138 +msgid "Digits" +msgstr "Dígitos" + +#: ../vn/field/vn-entry.c:154 ../vn/field/vn-spin.c:173 +#: ../vn/column/vn-column-entry.c:120 ../vn/column/vn-column-spin.c:139 +msgid "The number of decimal places to display." +msgstr "El número de posiciones decimales que se muestran" + +#: ../vn/field/vn-combo.c:328 ../vn/column/vn-column-combo.c:269 +msgid "Index column" +msgstr "Columna índice" + +#: ../vn/field/vn-combo.c:329 ../vn/column/vn-column-combo.c:270 +msgid "The column index of the model" +msgstr "El índice de columna del modelo" + +#: ../vn/field/vn-combo.c:335 ../vn/column/vn-column-combo.c:276 +msgid "Show column" +msgstr "Mostrar columna" + +#: ../vn/field/vn-combo.c:336 ../vn/column/vn-column-combo.c:277 +msgid "The column of the model shown by combo" +msgstr "La columna del modelo que se mostrará en el combo" + +#: ../vn/field/vn-combo.c:343 ../vn/column/vn-column-combo.c:284 +msgid "The model from which the combo takes the values shown in the list" +msgstr "El modelo del cual el combo tomará los valores mostrados en la lista" + +#: ../vn/field/vn-completion.c:322 +msgid "Field" +msgstr "Campo" + +#: ../vn/field/vn-completion.c:323 +msgid "The name of the field used for the search" +msgstr "El nombre del campo usado para la búsqueda" + +#: ../vn/field/vn-date-chooser.c:71 +#, c-format +msgid "%s, %u %s %u" +msgstr "%s, %u %s %u" + +#: ../vn/field/vn-date-chooser.c:280 +msgid "Change date" +msgstr "Cambiar fecha" + +#: ../vn/field/vn-image.c:133 +msgid "Select the image" +msgstr "Selecciona la imagen" + +#: ../vn/field/vn-http-image.c:69 +msgid "Undefined error" +msgstr "Error indefinido" + +#: ../vn/field/vn-http-image.c:145 +msgid "File loader already set" +msgstr "Ya se ha especificado un cargador de archivos" + +#. Se usará para subir imagenes y cambiar nombres, o abrir un menu contextual +#. g_signal_connect (obj, "event", G_CALLBACK (vn_http_image_cb_event), obj); +#: ../vn/field/vn-http-image.c:184 +msgid "No image set" +msgstr "Sin imagen" + +#: ../vn/field/vn-http-image.c:210 +msgid "File loader" +msgstr "Cargador de archivos" + +#: ../vn/field/vn-http-image.c:211 +msgid "A DbFileLoader, used to download the files" +msgstr "Un DbFileLoader que se usará para descargar los archivos" + +#: ../vn/field/vn-http-image.c:218 +msgid "File path" +msgstr "Ruta del archivo" + +#: ../vn/field/vn-http-image.c:219 +msgid "The relative path to the image from file loader path" +msgstr "La ruta relativa a la imagen desde la del cargador de archivos" + +#: ../vn/field/vn-http-image.c:226 +msgid "Image bytes" +msgstr "Bytes" + +#: ../vn/field/vn-http-image.c:227 +msgid "A GBytes structure with the image data" +msgstr "Una estructura GBytes con los datos de la imágen" + +#: ../vn/vn-column.c:341 +msgid "The column index in the model" +msgstr "El índice de la columna en el modelo" + +#: ../vn/vn-column.c:355 +msgid "Whether the column values are editable" +msgstr "Si el valor de campo puede ser editado por el usuario" + +#: ../vn/column/vn-column-spin.c:130 +msgid "Climb rate" +msgstr "Tasa de subida" + +#: ../vn/column/vn-column-spin.c:131 +msgid "The acceleration rate when you hold down a button." +msgstr "La tasa de aceleración cuando se mantiene apretado el botón." + +#: ../vn/column/vn-column-image.c:484 +msgid "Base path from the host where the images will be downloaded" +msgstr "La ruta base del servidor desde donde se descargarán las imágenes." + +#: ../vn/column/vn-column-image.c:491 +msgid "Tooltip path" +msgstr "Ruta del tooltip" + +#: ../vn/column/vn-column-image.c:492 +msgid "" +"Prefix for the path of the images to be shown in the tooltip. Starting after " +"the path of the column and appending the name on each cell" +msgstr "" +"Prefijo para la ruta de las imagenes que se muestren en el tooltip. " +"Empezando desde la ruta de la columna y añadiendo después el nombre de cada " +"celda" + +#: ../vn/column/vn-column-image.c:501 +msgid "Tooltip size" +msgstr "Tamaño del tooltip" + +#: ../vn/column/vn-column-image.c:502 +msgid "" +"Size of the bigger side of the tooltip images, the another side will be " +"scaled accordingly and smaller images won't be scaled" +msgstr "" +"Tamaño del lado más grande de las imágenes de tooltip, el otro lado se " +"escalará de acuerdo a este y las imágenes más pequeñas no se escalarán" + +#: ../vn/gui/login.glade.h:1 +msgid "Configuration" +msgstr "Configuración" + +#: ../vn/gui/login.glade.h:2 +msgid "Plugin:" +msgstr "Plugin:" + +#: ../vn/gui/login.glade.h:3 +msgid "Host:" +msgstr "Host:" + +#: ../vn/gui/login.glade.h:4 +msgid "Schema:" +msgstr "Esquema:" + +#: ../vn/gui/login.glade.h:5 +msgid "SSL CA:" +msgstr "SSL CA" + +#: ../vn/gui/login.glade.h:6 +msgid "" +"Path to the file containing the CA certificate, if this is empty SSL won't " +"be used" +msgstr "" +"Ruta al archivo con el certificado de la CA, si está vacío, no se usará SSL" + +#: ../vn/gui/login.glade.h:7 +msgid "Access" +msgstr "Acceso" + +#: ../vn/gui/login.glade.h:8 ../vn/gui/main.glade.h:20 +msgid "User:" +msgstr "Usuario:" + +#: ../vn/gui/login.glade.h:9 ../module/data/users.glade.h:8 +msgid "Password:" +msgstr "Clave:" + +#: ../vn/gui/login.glade.h:10 +msgid "Remember" +msgstr "Recordar" + +#: ../vn/gui/main.glade.h:1 +msgid "Copyright - Verdnatura Levante S. L." +msgstr "Copyright - Verdnatura Levante S. L." + +#: ../vn/gui/main.glade.h:2 +msgid "Management and administration of companies" +msgstr "Gestión y administración de empresas" + +#: ../vn/gui/main.glade.h:3 +msgid "www.verdnatura.es" +msgstr "www.verdnatura.es" + +#: ../vn/gui/main.glade.h:4 +msgid "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU Lesser General Public\n" +"License along with this program; if not, write to the Free\n" +"Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\n" +"02111-1307 USA." +msgstr "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU Lesser General Public\n" +"License along with this program; if not, write to the Free\n" +"Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\n" +"02111-1307 USA." + +#: ../vn/gui/main.glade.h:18 ../vn/gui/child-window.glade.h:1 +msgid "Hedera" +msgstr "Hedera" + +#: ../vn/gui/main.glade.h:21 +msgid "[user-name]" +msgstr "[user-name]" + +#: ../vn/gui/actions.glade.h:1 +msgid "_Logout" +msgstr "_Desconectar" + +#: ../vn/gui/actions.glade.h:2 +msgid "Logout" +msgstr "Desconectar" + +#: ../vn/gui/actions.glade.h:3 +msgid "_Quit" +msgstr "_Salir" + +#: ../vn/gui/actions.glade.h:4 +msgid "_About" +msgstr "_Acerca de" + +#: ../vn/gui/actions.glade.h:5 +msgid "_File" +msgstr "_Archivo" + +#: ../vn/gui/actions.glade.h:6 +msgid "_Help" +msgstr "Ay_uda" + +#: ../vn/gui/actions.glade.h:7 +msgid "_Reconnect" +msgstr "_Reconectar" + +#: ../vn/gui/actions.glade.h:8 +msgid "Reconnect" +msgstr "Reconectar" + +#: ../vn/gui/actions.glade.h:9 +msgid "_Close" +msgstr "_Cerrar" + +#: ../vn/gui/actions.glade.h:10 +msgid "_View" +msgstr "_Ver" + +#: ../vn/gui/actions.glade.h:11 +msgctxt "View menu option" +msgid "Dynamic _Tabs" +msgstr "_Pestañas dinámicas" + +#: ../vn/gui/actions.glade.h:12 +msgid "Don't show tabs if there is only one tab open" +msgstr "No mostrar las pestañas si solo hay una abierta" + +#: ../vn/gui/actions.glade.h:13 +msgctxt "View menu option" +msgid "Tool_bar" +msgstr "_Barra de herramientas" + +#: ../module/data/users.glade.h:1 +msgid "User name:" +msgstr "Nombre de usuario:" + +#: ../module/data/users.glade.h:2 +msgid "Search" +msgstr "Buscar" + +#: ../module/data/users.glade.h:3 +msgid "Identifier" +msgstr "Identificador" + +#: ../module/data/users.glade.h:5 +msgid "MySQL User" +msgstr "Usuario MySQL" + +#: ../module/data/users.glade.h:6 +msgid "Enabled" +msgstr "Habilitado" + +#: ../module/data/users.glade.h:7 +msgid "Name:" +msgstr "Nombre:" + +#: ../module/data/users.glade.h:9 +msgid "MySQL user:" +msgstr "Usuario MySQL:" + +#: ../module/data/users.glade.h:10 +msgid "Enabled:" +msgstr "Habilitado:" + +#: ../module/data/users.glade.h:11 +msgid "Identifier:" +msgstr "Identificador:" + +#: ../module/data/users.glade.h:12 +msgid "UID:" +msgstr "UID:" + +#: ../module/data/users.glade.h:13 +msgid "Main group:" +msgstr "Grupo principal:" + +#: ../module/data/users.glade.h:14 +msgid "Last change:" +msgstr "Último cambio:" + +#: ../module/data/users.glade.h:15 +msgid "Expires:" +msgstr "Expira:" + +#: ../module/data/users.glade.h:16 +msgid "Account" +msgstr "Cuenta" + +#: ../module/data/users.glade.h:17 +msgid "Alias" +msgstr "Alias" + +#: ../module/data/users.glade.h:18 +msgid "Mail alias" +msgstr "Alias de correo" + +#: ../module/data/users.glade.h:19 +msgid "Extension:" +msgstr "Extensión:" + +#: ../module/data/users.glade.h:20 +msgid "Secret:" +msgstr "Clave:" + +#: ../module/data/users.glade.h:21 +msgid "Call group:" +msgstr "Call group:" + +#: ../module/data/users.glade.h:22 +msgid "SIP" +msgstr "SIP" + +#: ../module/data/users.glade.h:23 +msgid "Repeat password:" +msgstr "Repetir contraseña:" diff --git a/po/nl.po b/po/nl.po new file mode 100644 index 0000000..5985f0f --- /dev/null +++ b/po/nl.po @@ -0,0 +1,1074 @@ +# Dutch translations for hedera package +# Traducciones al español para el paquete hedera. +# Copyright (C) 2012 THE hedera'S COPYRIGHT HOLDER +# This file is distributed under the same license as the hedera package. +# Alejandro T. Colombini Gómez , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: hedera 1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-07-10 11:56+0200\n" +"PO-Revision-Date: 2012-10-09 11:38+0200\n" +"Last-Translator: Alejandro T. Colombini Gómez \n" +"Language-Team: Dutch\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. * +#. * SECTION: gvn-misc +#. * @Short_description: Miscelaneous utility functions +#. * @Title: Miscelaneous utility functions +#. * +#: ../gvn/gvn-misc.c:27 ../gvn/gvn-misc.c:31 +msgid "Err" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Mon" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Tue" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Wed" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Thu" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Fri" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Sat" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Sun" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Jan" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Feb" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Mar" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Apr" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "May" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Jun" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Jul" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Aug" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Sep" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Oct" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Nov" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Dec" +msgstr "" + +#: ../gvn/gvn-param-spec.c:263 +#, c-format +msgid "Param not editable" +msgstr "" + +#: ../gvn/gvn-param-spec.c:268 +#, c-format +msgid "Param can't be NULL" +msgstr "" + +#: ../gvn/gvn-param-spec.c:274 +#, c-format +msgid "Incompatible type for this param" +msgstr "" + +#: ../gvn/gvn-param.c:443 ../sql/sql-value.c:235 ../vn/vn-field.c:518 +msgid "Value" +msgstr "" + +#: ../gvn/gvn-param.c:444 +msgid "The value of the param" +msgstr "" + +#: ../gvn/gvn-param.c:450 +msgid "Master" +msgstr "" + +#: ../gvn/gvn-param.c:451 +msgid "The master GvnParam of this parameter" +msgstr "" + +#: ../gvn/gvn-param.c:457 ../db/db-iterator.c:1070 +msgid "Mode" +msgstr "" + +#: ../gvn/gvn-param.c:458 +msgid "The mode of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:464 +msgid "Status" +msgstr "" + +#: ../gvn/gvn-param.c:465 +msgid "The current status of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:471 +msgid "Spec" +msgstr "" + +#: ../gvn/gvn-param.c:472 +msgid "The spec of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:478 ../vn/vn-field.c:546 +msgid "Glib Type" +msgstr "" + +#: ../gvn/gvn-param.c:479 ../vn/vn-field.c:547 +msgid "The type of the value" +msgstr "" + +#: ../gvn/gvn-param.c:485 ../vn/vn-field.c:553 ../vn/vn-column.c:354 +msgid "Editable" +msgstr "" + +#: ../gvn/gvn-param.c:486 +msgid "Whether the param value can be modified" +msgstr "" + +#: ../gvn/gvn-param.c:492 ../vn/vn-field.c:560 +msgid "Null" +msgstr "" + +#: ../gvn/gvn-param.c:493 +msgid "Whether the param value can be of type GVN_TYPE_NULL" +msgstr "" + +#: ../gvn/gvn-param.c:499 ../vn/vn-field.c:567 +msgid "Default Value" +msgstr "" + +#: ../gvn/gvn-param.c:500 ../vn/vn-field.c:568 +msgid "The default value" +msgstr "" + +#: ../gvn/gvn-value.c:434 +#, c-format +msgid "Attempting to compare invalid types: %s\n" +msgstr "" + +#: ../sql/sql-render.c:413 +msgid "Delimiter" +msgstr "" + +#: ../sql/sql-render.c:414 +msgid "The character used for delimite the name of fields, tables..." +msgstr "" + +#: ../sql/sql-value.c:228 +msgid "Param" +msgstr "" + +#: ../sql/sql-value.c:229 +msgid "The param which is linked" +msgstr "" + +#: ../sql/sql-value.c:236 +msgid "The value" +msgstr "" + +#: ../sql/sql-string.c:198 ../db/db-iterator.c:1063 ../db/db-model.c:3411 +#: ../db/db-request.c:466 ../vn/field/vn-combo.c:356 +#: ../vn/field/vn-completion.c:315 ../vn/column/vn-column-combo.c:297 +msgid "SQL" +msgstr "" + +#: ../sql/sql-string.c:199 +msgid "An arbitrary SQL string" +msgstr "" + +#: ../plugin/mysql/db-mysql.c:68 ../plugin/pg/db-pg.c:76 +#, c-format +msgid "Can't allocate the needed memory" +msgstr "" + +#: ../db/db-iterator.c:284 +msgid "DbIterator: Can't reassign the 'model' property" +msgstr "" + +#: ../db/db-iterator.c:942 +msgid "DbIterator: Can't set the 'conn' property because model isn't set" +msgstr "" + +#: ../db/db-iterator.c:1049 ../db/db-calc.c:329 ../vn/field/vn-combo.c:342 +#: ../vn/column/vn-column-combo.c:283 +msgid "Model" +msgstr "" + +#: ../db/db-iterator.c:1050 +msgid "The DbModel handled by the iterator" +msgstr "" + +#: ../db/db-iterator.c:1056 ../db/db-model.c:3395 ../db/db-request.c:459 +#: ../vn/vn-gui.c:1531 ../vn/vn-form.c:241 ../vn/field/vn-combo.c:349 +#: ../vn/field/vn-completion.c:308 ../vn/column/vn-column-combo.c:290 +msgid "Connection" +msgstr "" + +#: ../db/db-iterator.c:1057 ../vn/field/vn-combo.c:350 +#: ../vn/field/vn-completion.c:309 ../vn/column/vn-column-combo.c:291 +msgid "The connection used by the model" +msgstr "" + +#: ../db/db-iterator.c:1064 ../vn/field/vn-combo.c:357 +#: ../vn/field/vn-completion.c:316 ../vn/column/vn-column-combo.c:298 +msgid "The SQL query used to create the model" +msgstr "" + +#: ../db/db-iterator.c:1071 +msgid "The mode in which the iterator is working" +msgstr "" + +#: ../db/db-iterator.c:1078 +msgid "Remember selection" +msgstr "" + +#: ../db/db-iterator.c:1079 +msgid "Wether to rememeber the selection when model is refreshed" +msgstr "" + +#: ../db/db-model.c:3396 +msgid "The DbConn that manages the connection to the database" +msgstr "" + +#: ../db/db-model.c:3403 ../db/db-request.c:473 +msgid "Statement" +msgstr "" + +#: ../db/db-model.c:3404 +msgid "The statement which retrieves the data" +msgstr "" + +#: ../db/db-model.c:3412 +msgid "" +"Depending on the \"use-file\" property this will be the path to a file with " +"queries for the model or a SQL string" +msgstr "" + +#: ../db/db-model.c:3421 +msgid "Use file" +msgstr "" + +#: ../db/db-model.c:3422 +msgid "" +"If this is set to TRUE, the \"sql\" property will hold the name of a file " +"containing a query, if set to FALSE, \"sql\" is used as an SQL string" +msgstr "" + +#: ../db/db-model.c:3431 +msgid "Main Table" +msgstr "" + +#: ../db/db-model.c:3432 +msgid "The main table of the model" +msgstr "" + +#: ../db/db-model.c:3439 +msgid "Update flags" +msgstr "" + +#: ../db/db-model.c:3440 +msgid "The flags that indicate how a model can be modified" +msgstr "" + +#: ../db/db-model.c:3448 +msgid "Result position" +msgstr "" + +#: ../db/db-model.c:3449 +msgid "" +"The position where the query that will fill the model will be placed in a " +"multi-query" +msgstr "" + +#: ../db/db-calc.c:330 +msgid "The model where the operations will be applied" +msgstr "" + +#: ../db/db-calc.c:337 +msgid "Operation type" +msgstr "" + +#: ../db/db-calc.c:338 +msgid "The type of the operation applied over the function" +msgstr "" + +#: ../db/db-calc.c:347 +msgid "Function" +msgstr "" + +#: ../db/db-calc.c:348 +msgid "The function to execute" +msgstr "" + +#: ../db/db-calc.c:354 +msgid "Data" +msgstr "" + +#: ../db/db-calc.c:355 +msgid "The user provided data for the function" +msgstr "" + +#: ../db/db-calc.c:361 +msgid "Column" +msgstr "" + +#: ../db/db-calc.c:362 +msgid "A column to apply the operations over it" +msgstr "" + +#: ../db/db-param.c:243 ../vn/vn-grid.c:349 ../vn/vn-field.c:532 +msgid "Iterator" +msgstr "" + +#: ../db/db-param.c:244 +msgid "The iterator owner of param" +msgstr "" + +#: ../db/db-param.c:250 ../vn/vn-column.c:340 +msgid "Column index" +msgstr "" + +#: ../db/db-param.c:251 +msgid "The referenced column index" +msgstr "" + +#: ../db/db-param.c:257 ../vn/vn-field.c:539 ../vn/vn-column.c:347 +msgid "Column name" +msgstr "" + +#: ../db/db-param.c:258 ../vn/vn-column.c:348 +msgid "The referenced column name" +msgstr "" + +#: ../db/db-conn.c:279 ../db/db-conn.c:284 +#, c-format +msgid "Can't load DbPlugin '%s': %s" +msgstr "" + +#: ../db/db-conn.c:297 +#, c-format +msgid "Plugin can't be loaded" +msgstr "" + +#: ../db/db-conn.c:413 +#, c-format +msgid "Can't open a new connection, it's already open." +msgstr "" + +#: ../db/db-conn.c:555 +#, c-format +msgid "The query is empty." +msgstr "" + +#: ../db/db-conn.c:1060 +msgid "Plugin" +msgstr "" + +#: ../db/db-conn.c:1061 +msgid "The name of the plugin" +msgstr "" + +#: ../db/db-conn.c:1067 +msgid "Query path" +msgstr "" + +#: ../db/db-conn.c:1068 +msgid "The path where query files are located" +msgstr "" + +#: ../db/db-conn.c:1074 ../db/db-file-loader.c:724 +#: ../vn/column/vn-column-image.c:475 +msgid "Host" +msgstr "" + +#: ../db/db-conn.c:1075 +msgid "The host name to connect to" +msgstr "" + +#: ../db/db-conn.c:1081 ../module/data/users.glade.h:4 +msgid "User" +msgstr "" + +#: ../db/db-conn.c:1082 +msgid "The user name" +msgstr "" + +#: ../db/db-conn.c:1088 +msgid "DB name" +msgstr "" + +#: ../db/db-conn.c:1089 +msgid "The default schema" +msgstr "" + +#: ../db/db-request.c:315 +#, c-format +msgid "The request was canceled" +msgstr "" + +#: ../db/db-request.c:460 +msgid "The connection used to render and execute the query" +msgstr "" + +#: ../db/db-request.c:467 +msgid "The SQL query to execute" +msgstr "" + +#: ../db/db-request.c:474 +msgid "The statement to execute" +msgstr "" + +#: ../db/db-request.c:480 +msgid "Result" +msgstr "" + +#: ../db/db-request.c:481 +msgid "The result data of the query" +msgstr "" + +#: ../db/db-request.c:487 +msgid "Error" +msgstr "" + +#: ../db/db-request.c:488 +msgid "The GError, if an error ocurred" +msgstr "" + +#: ../db/db-file-loader.c:333 +#, c-format +msgid "%s not cached" +msgstr "" + +#: ../db/db-file-loader.c:431 +#, c-format +msgid "Unknown content length of file %s" +msgstr "" + +#: ../db/db-file-loader.c:725 ../vn/column/vn-column-image.c:476 +msgid "The host web server name to get the images" +msgstr "" + +#: ../db/db-file-loader.c:732 ../vn/column/vn-column-image.c:483 +msgid "Path" +msgstr "" + +#: ../db/db-file-loader.c:733 +msgid "The path of the directory to interact with" +msgstr "" + +#: ../db/db-file-loader.c:740 +msgid "Cache directory" +msgstr "" + +#: ../db/db-file-loader.c:741 +msgid "" +"The local directory where the downloaded files will be stored. The default " +"cache directory is 'hedera', under g_get_user_cache_dir()." +msgstr "" + +#: ../db/db-file-loader.c:749 +msgid "Maximal cache size" +msgstr "" + +#: ../db/db-file-loader.c:750 +msgid "The maximal size for the contents of the cache directory" +msgstr "" + +#: ../vn/vn-gui.c:620 +msgid "Connection has been lost. Do you want to reconnect?" +msgstr "" + +#: ../vn/vn-gui.c:627 +msgid "An error occurred in the connection." +msgstr "" + +#: ../vn/vn-gui.c:630 +msgid "Database error" +msgstr "" + +#: ../vn/vn-gui.c:637 +msgid "Unknown error" +msgstr "" + +#: ../vn/vn-gui.c:977 +msgid "Closing connection" +msgstr "" + +#: ../vn/vn-gui.c:979 +msgid "Transaction started" +msgstr "" + +#: ../vn/vn-gui.c:981 +msgid "Connecting" +msgstr "" + +#: ../vn/vn-gui.c:983 +msgid "Connection lost" +msgstr "" + +#: ../vn/vn-gui.c:985 +msgid "Connection closed" +msgstr "" + +#: ../vn/vn-gui.c:987 ../vn/field/vn-http-image.c:118 +msgid "Loading" +msgstr "" + +#: ../vn/vn-gui.c:989 ../vn/gui/main.glade.h:19 +msgid "Ready" +msgstr "" + +#: ../vn/vn-gui.c:1532 +msgid "The connection used by Gui" +msgstr "" + +#: ../vn/vn-gui.c:1538 +msgid "Application" +msgstr "" + +#: ../vn/vn-gui.c:1539 +msgid "The application handler for the entire program" +msgstr "" + +#: ../vn/vn-model.c:303 +#, c-format +msgid "Function vn_model_set_sort_func not implemented" +msgstr "" + +#: ../vn/vn-model.c:310 +#, c-format +msgid "Function vn_model_set_default_sort_func not implemented" +msgstr "" + +#: ../vn/vn-grid.c:350 +msgid "The iterator used by VnGrid" +msgstr "" + +#: ../vn/vn-handler.c:65 +msgid "Are you sure you want to delete the selected record?" +msgstr "" + +#: ../vn/vn-handler.c:90 +msgid "Are you sure that you want to undo all changes?" +msgstr "" + +#: ../vn/vn-handler.c:219 +msgid "Undo changes" +msgstr "" + +#: ../vn/vn-handler.c:226 +msgid "Save changes" +msgstr "" + +#: ../vn/vn-handler.c:233 +msgid "Refresh data" +msgstr "" + +#: ../vn/vn-handler.c:240 +msgid "Remove record" +msgstr "" + +#: ../vn/vn-handler.c:247 +msgid "Add record" +msgstr "" + +#: ../vn/vn-handler.c:254 +msgid "Move to the first row" +msgstr "" + +#: ../vn/vn-handler.c:261 +msgid "Move to the previous row" +msgstr "" + +#: ../vn/vn-handler.c:268 +msgid "Move to the next row" +msgstr "" + +#: ../vn/vn-handler.c:275 +msgid "Move to the last row" +msgstr "" + +#: ../vn/vn-handler.c:514 +msgid "Show flags" +msgstr "" + +#: ../vn/vn-handler.c:515 +msgid "Sets the buttons that will be shown on the interface" +msgstr "" + +#: ../vn/vn-handler.c:522 +msgid "Simple record" +msgstr "" + +#: ../vn/vn-handler.c:523 +msgid "Sets if it is used to handle a iterator with a single record" +msgstr "" + +#: ../vn/vn-form.c:227 +msgid "Name" +msgstr "" + +#: ../vn/vn-form.c:228 +msgid "The form name" +msgstr "" + +#: ../vn/vn-form.c:234 +msgid "Gui" +msgstr "" + +#: ../vn/vn-form.c:235 +msgid "The Gui object" +msgstr "" + +#: ../vn/vn-form.c:242 +msgid "The connection used by the module" +msgstr "" + +#: ../vn/vn-form.c:248 +msgid "Module" +msgstr "" + +#: ../vn/vn-form.c:249 +msgid "The module" +msgstr "" + +#: ../vn/vn-login.c:276 ../vn/vn-login.c:278 +msgid "Login error" +msgstr "" + +#: ../vn/vn-login.c:336 +#, c-format +msgid "Bad connection settings, please check it." +msgstr "" + +#: ../vn/vn-login.c:457 +msgid "Application id" +msgstr "" + +#: ../vn/vn-login.c:458 +msgid "The application identifier" +msgstr "" + +#: ../vn/vn-field.c:519 +msgid "The current value of the field" +msgstr "" + +#: ../vn/vn-field.c:525 +msgid "Parameter" +msgstr "" + +#: ../vn/vn-field.c:526 +msgid "The param where the field can read/write its value" +msgstr "" + +#: ../vn/vn-field.c:533 +msgid "The iterator used to get the field param" +msgstr "" + +#: ../vn/vn-field.c:540 +msgid "The column name on the iterator" +msgstr "" + +#: ../vn/vn-field.c:554 +msgid "Whether the field value is user editable" +msgstr "" + +#: ../vn/vn-field.c:561 +msgid "Whether the field value can be of type GVN_TYPE_NULL" +msgstr "" + +#: ../vn/field/vn-entry.c:153 ../vn/field/vn-spin.c:172 +#: ../vn/column/vn-column-entry.c:119 ../vn/column/vn-column-spin.c:138 +msgid "Digits" +msgstr "" + +#: ../vn/field/vn-entry.c:154 ../vn/field/vn-spin.c:173 +#: ../vn/column/vn-column-entry.c:120 ../vn/column/vn-column-spin.c:139 +msgid "The number of decimal places to display." +msgstr "" + +#: ../vn/field/vn-combo.c:328 ../vn/column/vn-column-combo.c:269 +msgid "Index column" +msgstr "" + +#: ../vn/field/vn-combo.c:329 ../vn/column/vn-column-combo.c:270 +msgid "The column index of the model" +msgstr "" + +#: ../vn/field/vn-combo.c:335 ../vn/column/vn-column-combo.c:276 +msgid "Show column" +msgstr "" + +#: ../vn/field/vn-combo.c:336 ../vn/column/vn-column-combo.c:277 +msgid "The column of the model shown by combo" +msgstr "" + +#: ../vn/field/vn-combo.c:343 ../vn/column/vn-column-combo.c:284 +msgid "The model from which the combo takes the values shown in the list" +msgstr "" + +#: ../vn/field/vn-completion.c:322 +msgid "Field" +msgstr "" + +#: ../vn/field/vn-completion.c:323 +msgid "The name of the field used for the search" +msgstr "" + +#: ../vn/field/vn-date-chooser.c:71 +#, c-format +msgid "%s, %u %s %u" +msgstr "" + +#: ../vn/field/vn-date-chooser.c:280 +msgid "Change date" +msgstr "" + +#: ../vn/field/vn-image.c:133 +msgid "Select the image" +msgstr "" + +#: ../vn/field/vn-http-image.c:69 +msgid "Undefined error" +msgstr "" + +#: ../vn/field/vn-http-image.c:145 +msgid "File loader already set" +msgstr "" + +#. Se usará para subir imagenes y cambiar nombres, o abrir un menu contextual +#. g_signal_connect (obj, "event", G_CALLBACK (vn_http_image_cb_event), obj); +#: ../vn/field/vn-http-image.c:184 +msgid "No image set" +msgstr "" + +#: ../vn/field/vn-http-image.c:210 +msgid "File loader" +msgstr "" + +#: ../vn/field/vn-http-image.c:211 +msgid "A DbFileLoader, used to download the files" +msgstr "" + +#: ../vn/field/vn-http-image.c:218 +msgid "File path" +msgstr "" + +#: ../vn/field/vn-http-image.c:219 +msgid "The relative path to the image from file loader path" +msgstr "" + +#: ../vn/field/vn-http-image.c:226 +msgid "Image bytes" +msgstr "" + +#: ../vn/field/vn-http-image.c:227 +msgid "A GBytes structure with the image data" +msgstr "" + +#: ../vn/vn-column.c:341 +msgid "The column index in the model" +msgstr "" + +#: ../vn/vn-column.c:355 +msgid "Whether the column values are editable" +msgstr "" + +#: ../vn/column/vn-column-spin.c:130 +msgid "Climb rate" +msgstr "" + +#: ../vn/column/vn-column-spin.c:131 +msgid "The acceleration rate when you hold down a button." +msgstr "" + +#: ../vn/column/vn-column-image.c:484 +msgid "Base path from the host where the images will be downloaded" +msgstr "" + +#: ../vn/column/vn-column-image.c:491 +msgid "Tooltip path" +msgstr "" + +#: ../vn/column/vn-column-image.c:492 +msgid "" +"Prefix for the path of the images to be shown in the tooltip. Starting after " +"the path of the column and appending the name on each cell" +msgstr "" + +#: ../vn/column/vn-column-image.c:501 +msgid "Tooltip size" +msgstr "" + +#: ../vn/column/vn-column-image.c:502 +msgid "" +"Size of the bigger side of the tooltip images, the another side will be " +"scaled accordingly and smaller images won't be scaled" +msgstr "" + +#: ../vn/gui/login.glade.h:1 +msgid "Configuration" +msgstr "" + +#: ../vn/gui/login.glade.h:2 +msgid "Plugin:" +msgstr "" + +#: ../vn/gui/login.glade.h:3 +msgid "Host:" +msgstr "" + +#: ../vn/gui/login.glade.h:4 +msgid "Schema:" +msgstr "" + +#: ../vn/gui/login.glade.h:5 +msgid "SSL CA:" +msgstr "" + +#: ../vn/gui/login.glade.h:6 +msgid "" +"Path to the file containing the CA certificate, if this is empty SSL won't " +"be used" +msgstr "" + +#: ../vn/gui/login.glade.h:7 +msgid "Access" +msgstr "" + +#: ../vn/gui/login.glade.h:8 ../vn/gui/main.glade.h:20 +msgid "User:" +msgstr "" + +#: ../vn/gui/login.glade.h:9 ../module/data/users.glade.h:8 +msgid "Password:" +msgstr "" + +#: ../vn/gui/login.glade.h:10 +msgid "Remember" +msgstr "" + +#: ../vn/gui/main.glade.h:1 +msgid "Copyright - Verdnatura Levante S. L." +msgstr "" + +#: ../vn/gui/main.glade.h:2 +msgid "Management and administration of companies" +msgstr "" + +#: ../vn/gui/main.glade.h:3 +msgid "www.verdnatura.es" +msgstr "" + +#: ../vn/gui/main.glade.h:4 +msgid "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU Lesser General Public\n" +"License along with this program; if not, write to the Free\n" +"Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\n" +"02111-1307 USA." +msgstr "" + +#: ../vn/gui/main.glade.h:18 ../vn/gui/child-window.glade.h:1 +msgid "Hedera" +msgstr "" + +#: ../vn/gui/main.glade.h:21 +msgid "[user-name]" +msgstr "" + +#: ../vn/gui/actions.glade.h:1 +msgid "_Logout" +msgstr "" + +#: ../vn/gui/actions.glade.h:2 +msgid "Logout" +msgstr "" + +#: ../vn/gui/actions.glade.h:3 +msgid "_Quit" +msgstr "" + +#: ../vn/gui/actions.glade.h:4 +msgid "_About" +msgstr "" + +#: ../vn/gui/actions.glade.h:5 +msgid "_File" +msgstr "" + +#: ../vn/gui/actions.glade.h:6 +msgid "_Help" +msgstr "" + +#: ../vn/gui/actions.glade.h:7 +msgid "_Reconnect" +msgstr "" + +#: ../vn/gui/actions.glade.h:8 +msgid "Reconnect" +msgstr "" + +#: ../vn/gui/actions.glade.h:9 +msgid "_Close" +msgstr "" + +#: ../vn/gui/actions.glade.h:10 +msgid "_View" +msgstr "" + +#: ../vn/gui/actions.glade.h:11 +msgctxt "View menu option" +msgid "Dynamic _Tabs" +msgstr "" + +#: ../vn/gui/actions.glade.h:12 +msgid "Don't show tabs if there is only one tab open" +msgstr "" + +#: ../vn/gui/actions.glade.h:13 +msgctxt "View menu option" +msgid "Tool_bar" +msgstr "" + +#: ../module/data/users.glade.h:1 +msgid "User name:" +msgstr "" + +#: ../module/data/users.glade.h:2 +msgid "Search" +msgstr "" + +#: ../module/data/users.glade.h:3 +msgid "Identifier" +msgstr "" + +#: ../module/data/users.glade.h:5 +msgid "MySQL User" +msgstr "" + +#: ../module/data/users.glade.h:6 +msgid "Enabled" +msgstr "" + +#: ../module/data/users.glade.h:7 +msgid "Name:" +msgstr "" + +#: ../module/data/users.glade.h:9 +msgid "MySQL user:" +msgstr "" + +#: ../module/data/users.glade.h:10 +msgid "Enabled:" +msgstr "" + +#: ../module/data/users.glade.h:11 +msgid "Identifier:" +msgstr "" + +#: ../module/data/users.glade.h:12 +msgid "UID:" +msgstr "" + +#: ../module/data/users.glade.h:13 +msgid "Main group:" +msgstr "" + +#: ../module/data/users.glade.h:14 +msgid "Last change:" +msgstr "" + +#: ../module/data/users.glade.h:15 +msgid "Expires:" +msgstr "" + +#: ../module/data/users.glade.h:16 +msgid "Account" +msgstr "" + +#: ../module/data/users.glade.h:17 +msgid "Alias" +msgstr "" + +#: ../module/data/users.glade.h:18 +msgid "Mail alias" +msgstr "" + +#: ../module/data/users.glade.h:19 +msgid "Extension:" +msgstr "" + +#: ../module/data/users.glade.h:20 +msgid "Secret:" +msgstr "" + +#: ../module/data/users.glade.h:21 +msgid "Call group:" +msgstr "" + +#: ../module/data/users.glade.h:22 +msgid "SIP" +msgstr "" + +#: ../module/data/users.glade.h:23 +msgid "Repeat password:" +msgstr "" diff --git a/sql/Makefile.am b/sql/Makefile.am new file mode 100644 index 0000000..66d2c13 --- /dev/null +++ b/sql/Makefile.am @@ -0,0 +1,119 @@ +include $(top_srcdir)/Makefile.decl + +SUBDIRS = parser + +sql_lib_LTLIBRARIES = libsql.la +sql_include_HEADERS = \ + sql.h \ + sql-object.h \ + sql-param-object.h \ + sql-param-list.h \ + sql-holder.h \ + sql-list.h \ + sql-set.h \ + sql-multi-stmt.h \ + sql-string.h \ + sql-target.h \ + sql-expr.h \ + sql-insert.h \ + sql-select.h \ + sql-select-field.h \ + sql-select-order.h \ + sql-subquery.h \ + sql-update.h \ + sql-update-set.h \ + sql-delete.h \ + sql-field.h \ + sql-join.h \ + sql-operation.h \ + sql-stmt.h \ + sql-table.h \ + sql-value.h \ + sql-dml.h \ + sql-function.h \ + sql-render.h \ + sql-parser.h + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(glib_CFLAGS) +libsql_la_LIBADD = \ + $(glib_LIBS) \ + $(top_builddir)/gvn/libgvn.la +libsql_la_SOURCES = \ + $(sql_include_HEADERS) \ + sql-object.c \ + sql-param-object.c \ + sql-param-list.c \ + sql-holder.c \ + sql-list.c \ + sql-set.c \ + sql-expr.c \ + sql-insert.c \ + sql-select.c \ + sql-select-field.c \ + sql-select-order.c \ + sql-subquery.c \ + sql-update.c \ + sql-update-set.c \ + sql-delete.c \ + sql-field.c \ + sql-join.c \ + sql-operation.c \ + sql-stmt.c \ + sql-table.c \ + sql-value.c \ + sql-dml.c \ + sql-function.c \ + sql-multi-stmt.c \ + sql-string.c \ + sql-target.c \ + sql-render.c \ + $(top_builddir)/sql/sql-parser.c + +pkgconfig_DATA = sql.pc + +EXTRA_DIST = sql.pc.in + +DISTCLEANFILES = sql.pc + +if ENABLE_VALA +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_SCANNER_ARGS = $(GIR_SCANNER_ARGS) + +INTROSPECTION_COMPILER_ARGS = --includedir=$(top_builddir)/gvn + +introspection_sources = $(libsql_la_SOURCES) + +Sql-$(VERSION).gir: $(sql_lib_LTLIBRARIES) $(top_builddir)/gvn/Gvn-$(VERSION).gir + Sql_@uVERSION@_gir_SCANNERFLAGS = \ + --include-uninstalled=$(top_builddir)/gvn/Gvn-$(VERSION).gir + Sql_@uVERSION@_gir_CFLAGS = -I$(top_srcdir) + Sql_@uVERSION@_gir_LIBS = $(sql_lib_LTLIBRARIES) + Sql_@uVERSION@_gir_FILES = $(introspection_sources) + Sql_@uVERSION@_gir_EXPORT_PACKAGES = sql + INTROSPECTION_GIRS = Sql-$(VERSION).gir + +gir_DATA = $(INTROSPECTION_GIRS) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES = $(gir_DATA) $(typelib_DATA) + +$(vapis)/sql.vapi: $(INTROSPECTION_GIRS) $(vapidata)/Sql-$(VERSION).metadata + $(vapigen_v)$(VAPIGEN) -q \ + --directory $(vapis) \ + --vapidir $(vapis) \ + --girdir $(top_builddir)/gvn \ + --metadatadir $(vapidata) \ + --library sql \ + Sql-$(VERSION).gir + +vapi_DATA = $(vapis)/sql.vapi + +CLEANFILES += $(vapi_DATA) + +endif +endif diff --git a/sql/parser/Makefile.am b/sql/parser/Makefile.am new file mode 100644 index 0000000..da1df2b --- /dev/null +++ b/sql/parser/Makefile.am @@ -0,0 +1,34 @@ +include $(top_srcdir)/Makefile.decl + +PARSER = $(top_builddir)/sql/sql-parser.c + +DIR = $(top_srcdir)/sql/parser +TMPL = $(DIR)/lempar-tmpl.c +SCAN = scan.rl +GRAM = gram.y + +all: $(PARSER) +$(PARSER): $(SCAN) gram.c gram.h + $(ragel_v)$(RAGEL) -C -G2 -o $(PARSER) $< + +gram.h: gram.c +gram.c: $(GRAM) lemon $(TMPL) + ln -fs $(TMPL) $(top_builddir)/sql/parser/lempar.c + $(lemon_v)./lemon -q $< + +lemon: $(DIR)/lemon.c + $(CC) -o lemon $< + +EXTRA_DIST = gram.y scan.rl lemon.c lempar-tmpl.c + +CLEANFILES = $(DIR)/gram.c $(DIR)/gram.h $(PARSER) + +DISTCLEANFILES = lemon lempar.c + +ragel_v = $(ragel_v_@AM_V@) +ragel_v_ = $(ragel_v_@AM_DEFAULT_V@) +ragel_v_0 = @echo " RAGEL $@"; + +lemon_v = $(lemon_v_@AM_V@) +lemon_v_ = $(lemon_v_@AM_DEFAULT_V@) +lemon_v_0 = @echo " LEMON $(subst y,c,$(GRAM)) $(subst y,h,$(GRAM))"; \ No newline at end of file diff --git a/sql/parser/gram.y b/sql/parser/gram.y new file mode 100644 index 0000000..07fac6e --- /dev/null +++ b/sql/parser/gram.y @@ -0,0 +1,650 @@ +%include +{ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + + #include + #include + #include + #include "gram.h" + + typedef struct + { + SqlObject * object; + gchar * string; + gchar * current; + gboolean error; + } + ParseState; + + static inline SqlList * list_add (gpointer item, SqlList * list) + { + sql_list_add (list, item); + return list; + } + + static inline SqlList * list_new (gpointer item, GType gtype) + { + SqlList * list = sql_list_new (gtype); + return list_add (item, list); + } +} + +%token_type {gchar *} +%token_destructor {g_free ($$);} +%token_prefix SQL_PARSER_ + +// Return pointer +%extra_argument { ParseState * state } + +// Error treatment +%syntax_error +{ + gchar * pref = ".", * err_str = NULL; + + if (state->current) + { + err_str = state->current - 15 >= state->string ? + state->current - 10 : state->string; + pref = err_str == state->string ? " near: " : " near: [...]"; + } + + g_log (g_quark_to_string (SQL_PARSER_LOG_DOMAIN), G_LOG_LEVEL_WARNING, + "Parsing error%s%s", pref, err_str); + + state->error = TRUE; +} + +%stack_overflow +{ + g_log (g_quark_to_string (SQL_PARSER_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING + ,"Parser stack overflow. Parsing terminated.\n"); +} + +// Operator precedence +%left UNION EXCEPT. +%left INSERSECT. +%left OR. +%left XOR. +%left AND. +%right NOT. +%right EQ NE. +%left GT GE LT LE. +%left LIKE. +%left IS. +%left PLUS MINUS. +%left STAR DIV MOD. +%right SIGN. + +// Start symbol: +%start_symbol start + +%type start {SqlObject *} +start ::= multi_stmt (A). +{ + state->object = SQL_OBJECT (A); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Multi statement + +%type multi_stmt {SqlStmt *} +multi_stmt(A) ::= stmt_list(stmts). +{ + guint len = sql_list_length (stmts); + + if (len == 1) + { + A = sql_list_get (stmts, 0); + g_object_unref (g_object_ref_sink (stmts)); + } + else + A = g_object_new (SQL_TYPE_MULTI_STMT, "stmts", stmts, NULL); +} + +%type stmt_list {SqlList *} +stmt_list(A) ::= stmt(X). { A = list_new (X, SQL_TYPE_STMT); } +stmt_list(A) ::= stmt(X) SC. { A = list_new (X, SQL_TYPE_STMT); } +stmt_list(A) ::= stmt(X) SC stmt_list(B). { A = list_add (X, B); } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Statemet + +%type stmt {SqlStmt *} +stmt(A) ::= select_stmt(X). { A = X; } +stmt(A) ::= delete_stmt(X). { A = X; } +stmt(A) ::= update_stmt(X). { A = X; } +stmt(A) ::= insert_stmt(X). { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Select + +%type select_stmt {SqlStmt *} +select_stmt(A) ::= select_stmt(B) set_op(type) simple_select(X). +{ + g_object_set (B, "type", type, "next", X, NULL); + A = B; +} +select_stmt(A) ::= simple_select(X). { A = X; } + +%type set_op {SqlSelectType} +set_op(A) ::= UNION ANY. { A = SQL_SELECT_UNION_ANY; } +set_op(A) ::= UNION ALL. { A = SQL_SELECT_UNION_ALL; } +set_op(A) ::= UNION. { A = SQL_SELECT_UNION_ALL; } +set_op(A) ::= INTERSECT. { A = SQL_SELECT_INTERSECT; } +set_op(A) ::= EXCEPT. { A = SQL_SELECT_EXCEPT; } + +%type simple_select {SqlStmt *} +simple_select(A) ::= LP simple_select(X) RP. { A = X; } +simple_select(A) ::= SELECT + distinct(distinct) + select_field_list(fields) + from(targets) + where(where) + group(group) having(having) + order(order) + limit(limit). +{ + A = g_object_new (SQL_TYPE_SELECT + ,"distinct" ,distinct + ,"fields" ,fields + ,"targets" ,targets + ,"where" ,where + ,"group" ,group + ,"having" ,having + ,"having" ,having + ,"order" ,order + ,NULL + ); + + if (limit) + g_object_set (A + ,"limit-offset" ,limit->offset + ,"limit-count" ,limit->count + ,NULL + ); +} + +%type distinct {gboolean} +distinct(A) ::= DISTINCT. { A = TRUE; } +distinct(A) ::= ALL. { A = FALSE; } +distinct(A) ::= . { A = FALSE; } + +%type select_field {SqlSelectField *} +select_field(A) ::= expr(X). + { A = g_object_new (SQL_TYPE_SELECT_FIELD, "expr", X, NULL); } +select_field(A) ::= expr(X) alias(Y). + { A = g_object_new (SQL_TYPE_SELECT_FIELD, "expr", X, "alias", Y, NULL); } + +%type select_field_list {SqlList *} +select_field_list(A) ::= select_field(X). { A = list_new (X, SQL_TYPE_SELECT_FIELD); } +select_field_list(A) ::= select_field_list(B) CM select_field(X). { A = list_add (X, B); } + +%type from {SqlList *} +from(A) ::= FROM target_list(X). { A = X; } +from(A) ::= . { A = NULL; } + +%type group {SqlList *} +group(A) ::= GROUP expr_list(X). { A = X; } +group(A) ::= . { A = NULL; } + +%type having {SqlExpr *} +having(A) ::= HAVING expr(X). { A = X; } +having(A) ::= . { A = NULL; } + +%type order {SqlList *} +order(A) ::= ORDER order_list (X). { A = X; } +order(A) ::= . { A = NULL; } + +%type order_list {SqlList *} +order_list(A) ::= order_expr(X). { A = list_new (X, SQL_TYPE_SELECT_ORDER); } +order_list(A) ::= order_list(B) CM order_expr(X). { A = list_add (X, B); } + +%type order_expr {SqlSelectOrder *} +order_expr(A) ::= expr(X) order_way(Y). { A = g_object_new (SQL_TYPE_SELECT_ORDER, "expr", X, "way", Y, NULL); } + +%type order_way {SqlSelectOrderWay} +order_way(A) ::= ASC. { A = SQL_SELECT_ORDER_ASC; } +order_way(A) ::= DESC. { A = SQL_SELECT_ORDER_DESC; } +order_way(A) ::= . { A = SQL_SELECT_ORDER_ASC; } + +%type limit {SelectLimit *} +%destructor limit {g_free ($$);} +%include +{ + typedef struct + { + guint offset; + guint count; + } + SelectLimit; + + static inline SelectLimit * create_limit (gchar * offset, gchar * count) + { + SelectLimit * limit = g_new (SelectLimit, 1); + limit->offset = offset ? atoi (offset) : 0; + limit->count = count ? atoi (count) : 0; + return limit; + } +} +limit(A) ::= LIMIT INTEGER(X). { A = create_limit (NULL, X); } +limit(A) ::= OFFSET INTEGER(Y). { A = create_limit (Y, NULL); } +limit(A) ::= LIMIT INTEGER(X) OFFSET INTEGER(Y). { A = create_limit (Y, X); } +limit(A) ::= LIMIT INTEGER(X) CM INTEGER(Y). { A = create_limit (Y, X); } +limit(A) ::= . { A = NULL; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Delete + +%type delete_stmt {SqlStmt *} +delete_stmt(A) ::= DELETE table_list(targets) del_using(tables) where(where). +{ + A = g_object_new (SQL_TYPE_DELETE + ,"targets" ,targets + ,"tables" ,tables + ,"where" ,where + ,NULL + ); +} + +%type table_list {SqlList *} +table_list(A) ::= table(X). { A = list_new (X, SQL_TYPE_TABLE); } +table_list(A) ::= table_list(B) CM table(X). { A = list_add (X, B); } + +%type del_using {SqlList *} +del_using(A) ::= USING target_list(X). { A = X; } +del_using(A) ::= . { A = NULL; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Update + +%type update_stmt {SqlStmt *} +update_stmt(A) ::= UPDATE table(table) SET update_list(sets) where(where). +{ + A = g_object_new (SQL_TYPE_UPDATE + ,"targets" ,table + ,"sets" ,sets + ,"where" ,where + ,NULL + ); +} + +%type update_list {SqlList *} +update_list(A) ::= update_set(X). { A = list_new (X, SQL_TYPE_UPDATE_SET); } +update_list(A) ::= update_list(B) CM update_set(X). { A = list_add (X, B); } + +%type update_set {SqlUpdateSet *} +update_set(A) ::= field(X) EQ def_expr(Y). +{ + A = g_object_new (SQL_TYPE_UPDATE_SET + ,"field" ,X + ,"expr" ,Y + ,NULL + ); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Insert + +%type insert_stmt {SqlStmt *} +insert_stmt(A) ::= INSERT table(table) insert_fields(fields) insert_values(values). +{ + A = g_object_new (SQL_TYPE_INSERT + ,"table" ,table + ,"fields" ,fields + ,NULL + ); + + if (SQL_IS_SELECT (values)) + g_object_set (A, "select", values, NULL); + else + g_object_set (A, "values", values, NULL); +} + +%type insert_fields {SqlList *} +insert_fields(A) ::= LP field_list(X) RP. { A = X; } +insert_fields(A) ::= . { A = NULL; } + +%type insert_values {gpointer} +insert_values(A) ::= DEFAULT VALUES. { A = NULL; } +insert_values(A) ::= VALUES set_list(X). { A = X; } +//insert_values(A) ::= select_stmt(X). { A = X; } + +%type set_list {SqlList *} +set_list(A) ::= set(X). { A = list_new (X, SQL_TYPE_SET); } +set_list(A) ::= set_list(B) CM set(X). { A = list_add (X, B); } + +%type set {SqlSet *} +set(A) ::= LP def_expr_list(X) RP. { A = g_object_new (SQL_TYPE_SET, "exprs", X, NULL); } + +%type def_expr_list {SqlList *} +def_expr_list(A) ::= def_expr(X). { A = list_new (X, SQL_TYPE_EXPR); } +def_expr_list(A) ::= def_expr_list(B) CM def_expr(X). { A = list_add (X, B); } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Common to statements + +%type where {SqlExpr *} +where(A) ::= WHERE expr(X). { A = SQL_EXPR (X); } +where(A) ::= . { A = NULL; } + +%type def_expr {SqlExpr *} +def_expr(A) ::= expr(X). { A = X; } +def_expr(A) ::= DEFAULT. { A = NULL; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Target + +%type target {SqlTarget *} +target(A) ::= unaliased_target(X). { A = X; } +target(A) ::= unaliased_target(X) alias(Y). +{ + g_object_set (X, "alias", Y, NULL); + A = X; +} +//target(A) ::= LP target(X) RP. { A = X; } + +%type target_list {SqlList *} +target_list(A) ::= target(X). { A = list_new (X, SQL_TYPE_TARGET); } +target_list(A) ::= target_list(B) CM target(X). { A = list_add (X, B); } + +%type unaliased_target {SqlTarget *} +unaliased_target(A) ::= join(X). { A = X; } +unaliased_target(A) ::= subquery(X). { A = X; } +unaliased_target(A) ::= table(X). { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Join + +%type join {SqlTarget *} +join(A) ::= target(left) join_type(type) target(right) join_cond(condition). +{ + A = g_object_new (SQL_TYPE_JOIN + ,"target-left" ,left + ,"target-right" ,right + ,"join-type" ,type + ,NULL + ); + + if (SQL_IS_LIST (condition)) // USING + { + GList * n; + SqlList * exprs = sql_list_new (SQL_TYPE_EXPR); + SqlOperation * op_and = g_object_new (SQL_TYPE_OPERATION + ,"type" ,SQL_OPERATION_TYPE_AND + ,"exprs" ,exprs + ,NULL + ); + + for (n = sql_list_get_items (condition); n; n = n->next) + { + SqlList * equal = sql_list_new (SQL_TYPE_EXPR); + SqlOperation * op = g_object_new (SQL_TYPE_OPERATION + ,"type" ,SQL_OPERATION_TYPE_EQUAL + ,"exprs" ,equal + ,NULL + ); + + gchar * target = left->alias ? + left->alias : SQL_TABLE (left)->name; + gchar * schema = SQL_IS_TABLE(left) && SQL_TABLE (left)->schema ? + SQL_TABLE (left)->schema : NULL; + + g_object_set (n->data, "target", target, "schema", schema, NULL); + sql_list_add (equal, n->data); + + target = right->alias ? right->alias : SQL_TABLE (right)->name; + schema = SQL_IS_TABLE (right) && SQL_TABLE (right)->schema ? + SQL_TABLE (right)->schema : NULL; + + sql_list_add (equal, + sql_field_new (SQL_FIELD (n->data)->name, target, schema)); + + sql_list_add (exprs, op); + } + + g_object_set (A, "condition", op_and, NULL); + g_object_unref (g_object_ref_sink (condition)); + } + else + g_object_set (A, "condition", condition, NULL); +} + +%type join_type {SqlJoinType} +join_type(A) ::= INNER JOIN. { A = SQL_JOIN_TYPE_INNER; } +join_type(A) ::= JOIN. { A = SQL_JOIN_TYPE_INNER; } +join_type(A) ::= LEFT JOIN. { A = SQL_JOIN_TYPE_LEFT; } +join_type(A) ::= RIGHT JOIN. { A = SQL_JOIN_TYPE_RIGHT; } + +%type join_cond {gpointer} +join_cond(A) ::= ON expr(X). { A = X; } +join_cond(A) ::= USING LP field_list(X) RP. { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Subquery + +%type subquery {SqlTarget *} +subquery(A) ::= LP select_stmt(stmts) RP. +{ + A = g_object_new (SQL_TYPE_SUBQUERY, "stms", stmts, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Table + +%type table {SqlTarget *} +table(A) ::= identifier(X). +{ + A = g_object_new (SQL_TYPE_TABLE, "table", X, NULL); +} +table(A) ::= identifier(Y) DOT identifier(X). +{ + A = g_object_new (SQL_TYPE_TABLE, "table", X, "schema", Y, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Expr + +%type expr {SqlExpr *} +expr(A) ::= LP expr(X) RP. { A = X; } + +%type expr_list {SqlList *} +expr_list(A) ::= expr(X). { A = list_new (X, SQL_TYPE_EXPR); } +expr_list(A) ::= expr_list(B) CM expr(X). { A = list_add (X, B); } + +expr(A) ::= field(X). { A = X; } +expr(A) ::= function(X). { A = X; } +expr(A) ::= operation(X). { A = X; } +expr(A) ::= value(X). { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Field + +%type field {SqlExpr *} +field(A) ::= unqualified_field(X). { A = X; } +field(A) ::= identifier(Y) DOT unqualified_field(X). +{ + g_object_set (X, "target", Y, NULL); + A = X; +} +field(A) ::= identifier(Z) DOT identifier(Y) DOT unqualified_field(X). +{ + g_object_set (X, "target", Y, "schema", Z, NULL); + A = X; +} + +%type unqualified_field {SqlExpr *} +unqualified_field(A) ::= identifier(X). +{ + A = g_object_new (SQL_TYPE_FIELD, "name", X, NULL); +} +unqualified_field(A) ::= STAR. +{ + A = g_object_new (SQL_TYPE_FIELD, "name", "*", NULL); +} + +%type field_list {SqlList *} +field_list(A) ::= field(X). { A = list_new (X, SQL_TYPE_FIELD); } +field_list(A) ::= field_list(B) CM field(X). { A = list_add (X, B); } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Function + +%type function {SqlExpr *} +function(A) ::= unqualified_function(X). { A = X; } +function(A) ::= identifier(Z) DOT unqualified_function(X). +{ + g_object_set (X, "schema", Z, NULL); + A = X; +} + +%type unqualified_function {SqlExpr *} +unqualified_function(A) ::= identifier(X) LP function_expr_list(Y) RP. +{ + A = g_object_new (SQL_TYPE_FUNCTION, "name", X, "params", Y, NULL); +} + +%type function_expr_list {SqlList *} +function_expr_list(A) ::= . { A = NULL; } +function_expr_list(A) ::= expr_list(X). { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Operation + +%type operation {SqlExpr *} +%include +{ + static inline SqlExpr * create_operation + (const gpointer X, const gpointer Y, const SqlOperationType type) + { + SqlList * exprs = sql_list_new (SQL_TYPE_EXPR); + sql_list_add (exprs, X); + + if (Y) // Binary operation + sql_list_add (exprs, Y); + + return g_object_new (SQL_TYPE_OPERATION, "type", type, "exprs", exprs, NULL); + } +} +operation(A) ::= MINUS expr(X). [SIGN] +{ + GValue value = {0}; + SqlExpr * minus = sql_value_new (); + g_value_init (&value, G_TYPE_INT); + g_value_set_int (&value, -1); + sql_value_set_value (SQL_VALUE (minus), &value); + A = create_operation (X, minus, SQL_OPERATION_TYPE_MULTIPLICATION); +} + +operation(A) ::= NOT expr(X). + { A = create_operation (X, NULL, SQL_OPERATION_TYPE_NOT); } + +operation(A) ::= PLUS expr(X). [SIGN] + { A = create_operation (X, NULL, SQL_OPERATION_TYPE_SUM); } + +operation(A) ::= expr(X) PLUS expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_SUM); } + +operation(A) ::= expr(X) MINUS expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_SUBTRACTION); } + +operation(A) ::= expr(X) STAR expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_MULTIPLICATION); } + +operation(A) ::= expr(X) DIV expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_DIVISION); } + +operation(A) ::= expr(X) OR expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_OR); } + +operation(A) ::= expr(X) XOR expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_XOR); } + +operation(A) ::= expr(X) AND expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_AND); } + +operation(A) ::= expr(X) EQ expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_EQUAL); } + +operation(A) ::= expr(X) NE expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_NOT_EQUAL); } + +//XXX the following should be %nonassoc operators but are %left to avoid +// conflicts. The DB will warn about a bad use if used as %left. +operation(A) ::= expr(X) GT expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_GREATER); } + +operation(A) ::= expr(X) GE expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_GREATER_EQUAL); } + +operation(A) ::= expr(X) LT expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_LOWER); } + +operation(A) ::= expr(X) LE expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_LOWER_EQUAL); } + +operation(A) ::= expr(X) LIKE expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_LIKE); } + +operation(A) ::= expr(X) IS expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_IS); } + +operation(A) ::= expr(X) MOD expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_MOD); } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Value + +%type value {SqlExpr *} + +value(A) ::= INTEGER(X). +{ + GValue value = {0}; + g_value_set_int (g_value_init (&value, G_TYPE_INT), atoi (X)); + A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); + g_value_unset (&value); +} + +value(A) ::= FLOAT(X). +{ + GValue value = {0}; + g_value_set_double + (g_value_init (&value, G_TYPE_DOUBLE), g_ascii_strtod (X, NULL)); + A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); + g_value_unset (&value); +} + +value(A) ::= STRING(X). +{ + GValue value = {0}; + g_value_set_string (g_value_init (&value, G_TYPE_STRING), X); + A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); + g_value_unset (&value); +} + +value(A) ::= BOOLEAN(X). +{ + GValue value = {0}; + g_value_set_boolean (g_value_init (&value, G_TYPE_BOOLEAN) + ,(!g_strcmp0 (X, "TRUE") || !g_strcmp0 (X, "true")) ? TRUE : FALSE); + A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); + g_value_unset (&value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Alias + +%type alias {gchar *} +%destructor alias {g_free ($$);} +alias(A) ::= AS identifier(X). { A = X; } +alias(A) ::= identifier(X). { A = X; } + +identifier(A) ::= IDENTIFIER(X). { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Placeholder + +%type placeholder {gchar *} +%destructor expr {g_free ($$);} +placeholder(A) ::= PLACEHOLDER(X). { A = X; } + +stmt ::= placeholder. +target ::= placeholder. +expr ::= placeholder. diff --git a/sql/parser/lemon.c b/sql/parser/lemon.c new file mode 100644 index 0000000..5d96995 --- /dev/null +++ b/sql/parser/lemon.c @@ -0,0 +1,4895 @@ +/* +** This file contains all sources (including headers) to the LEMON +** LALR(1) parser generator. The sources have been combined into a +** single file to make it easy to include LEMON in the source tree +** and Makefile of another program. +** +** The author of this program disclaims copyright. +*/ +#include +#include +#include +#include +#include +#include + +#ifndef __WIN32__ +# if defined(_WIN32) || defined(WIN32) +# define __WIN32__ +# endif +#endif + +#ifdef __WIN32__ +#ifdef __cplusplus +extern "C" { +#endif +extern int access(const char *path, int mode); +#ifdef __cplusplus +} +#endif +#else +#include +#endif + +/* #define PRIVATE static */ +#define PRIVATE + +#ifdef TEST +#define MAXRHS 5 /* Set low to exercise exception code */ +#else +#define MAXRHS 1000 +#endif + +static int showPrecedenceConflict = 0; +static char *msort(char*,char**,int(*)(const char*,const char*)); + +/* +** Compilers are getting increasingly pedantic about type conversions +** as C evolves ever closer to Ada.... To work around the latest problems +** we have to define the following variant of strlen(). +*/ +#define lemonStrlen(X) ((int)strlen(X)) + +/* a few forward declarations... */ +struct rule; +struct lemon; +struct action; + +static struct action *Action_new(void); +static struct action *Action_sort(struct action *); + +/********** From the file "build.h" ************************************/ +void FindRulePrecedences(); +void FindFirstSets(); +void FindStates(); +void FindLinks(); +void FindFollowSets(); +void FindActions(); + +/********* From the file "configlist.h" *********************************/ +void Configlist_init(void); +struct config *Configlist_add(struct rule *, int); +struct config *Configlist_addbasis(struct rule *, int); +void Configlist_closure(struct lemon *); +void Configlist_sort(void); +void Configlist_sortbasis(void); +struct config *Configlist_return(void); +struct config *Configlist_basis(void); +void Configlist_eat(struct config *); +void Configlist_reset(void); + +/********* From the file "error.h" ***************************************/ +void ErrorMsg(const char *, int,const char *, ...); + +/****** From the file "option.h" ******************************************/ +enum option_type { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR, + OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR}; +struct s_options { + enum option_type type; + const char *label; + char *arg; + const char *message; +}; +int OptInit(char**,struct s_options*,FILE*); +int OptNArgs(void); +char *OptArg(int); +void OptErr(int); +void OptPrint(void); + +/******** From the file "parse.h" *****************************************/ +void Parse(struct lemon *lemp); + +/********* From the file "plink.h" ***************************************/ +struct plink *Plink_new(void); +void Plink_add(struct plink **, struct config *); +void Plink_copy(struct plink **, struct plink *); +void Plink_delete(struct plink *); + +/********** From the file "report.h" *************************************/ +void Reprint(struct lemon *); +void ReportOutput(struct lemon *); +void ReportTable(struct lemon *, int); +void ReportHeader(struct lemon *); +void CompressTables(struct lemon *); +void ResortStates(struct lemon *); + +/********** From the file "set.h" ****************************************/ +void SetSize(int); /* All sets will be of size N */ +char *SetNew(void); /* A new set for element 0..N */ +void SetFree(char*); /* Deallocate a set */ +int SetAdd(char*,int); /* Add element to a set */ +int SetUnion(char *,char *); /* A <- A U B, thru element N */ +#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */ + +/********** From the file "struct.h" *************************************/ +/* +** Principal data structures for the LEMON parser generator. +*/ + +typedef enum {LEMON_FALSE=0, LEMON_TRUE} Boolean; + +/* Symbols (terminals and nonterminals) of the grammar are stored +** in the following: */ +enum symbol_type { + TERMINAL, + NONTERMINAL, + MULTITERMINAL +}; +enum e_assoc { + LEFT, + RIGHT, + NONE, + UNK +}; +struct symbol { + const char *name; /* Name of the symbol */ + int index; /* Index number for this symbol */ + enum symbol_type type; /* Symbols are all either TERMINALS or NTs */ + struct rule *rule; /* Linked list of rules of this (if an NT) */ + struct symbol *fallback; /* fallback token in case this token doesn't parse */ + int prec; /* Precedence if defined (-1 otherwise) */ + enum e_assoc assoc; /* Associativity if precedence is defined */ + char *firstset; /* First-set for all rules of this symbol */ + Boolean lambda; /* True if NT and can generate an empty string */ + int useCnt; /* Number of times used */ + char *destructor; /* Code which executes whenever this symbol is + ** popped from the stack during error processing */ + int destLineno; /* Line number for start of destructor */ + char *datatype; /* The data type of information held by this + ** object. Only used if type==NONTERMINAL */ + int dtnum; /* The data type number. In the parser, the value + ** stack is a union. The .yy%d element of this + ** union is the correct data type for this object */ + /* The following fields are used by MULTITERMINALs only */ + int nsubsym; /* Number of constituent symbols in the MULTI */ + struct symbol **subsym; /* Array of constituent symbols */ +}; + +/* Each production rule in the grammar is stored in the following +** structure. */ +struct rule { + struct symbol *lhs; /* Left-hand side of the rule */ + const char *lhsalias; /* Alias for the LHS (NULL if none) */ + int lhsStart; /* True if left-hand side is the start symbol */ + int ruleline; /* Line number for the rule */ + int nrhs; /* Number of RHS symbols */ + struct symbol **rhs; /* The RHS symbols */ + const char **rhsalias; /* An alias for each RHS symbol (NULL if none) */ + int line; /* Line number at which code begins */ + const char *code; /* The code executed when this rule is reduced */ + struct symbol *precsym; /* Precedence symbol for this rule */ + int index; /* An index number for this rule */ + Boolean canReduce; /* True if this rule is ever reduced */ + struct rule *nextlhs; /* Next rule with the same LHS */ + struct rule *next; /* Next rule in the global list */ +}; + +/* A configuration is a production rule of the grammar together with +** a mark (dot) showing how much of that rule has been processed so far. +** Configurations also contain a follow-set which is a list of terminal +** symbols which are allowed to immediately follow the end of the rule. +** Every configuration is recorded as an instance of the following: */ +enum cfgstatus { + COMPLETE, + INCOMPLETE +}; +struct config { + struct rule *rp; /* The rule upon which the configuration is based */ + int dot; /* The parse point */ + char *fws; /* Follow-set for this configuration only */ + struct plink *fplp; /* Follow-set forward propagation links */ + struct plink *bplp; /* Follow-set backwards propagation links */ + struct state *stp; /* Pointer to state which contains this */ + enum cfgstatus status; /* used during followset and shift computations */ + struct config *next; /* Next configuration in the state */ + struct config *bp; /* The next basis configuration */ +}; + +enum e_action { + SHIFT, + ACCEPT, + REDUCE, + ERROR, + SSCONFLICT, /* A shift/shift conflict */ + SRCONFLICT, /* Was a reduce, but part of a conflict */ + RRCONFLICT, /* Was a reduce, but part of a conflict */ + SH_RESOLVED, /* Was a shift. Precedence resolved conflict */ + RD_RESOLVED, /* Was reduce. Precedence resolved conflict */ + NOT_USED /* Deleted by compression */ +}; + +/* Every shift or reduce operation is stored as one of the following */ +struct action { + struct symbol *sp; /* The look-ahead symbol */ + enum e_action type; + union { + struct state *stp; /* The new state, if a shift */ + struct rule *rp; /* The rule, if a reduce */ + } x; + struct action *next; /* Next action for this state */ + struct action *collide; /* Next action with the same hash */ +}; + +/* Each state of the generated parser's finite state machine +** is encoded as an instance of the following structure. */ +struct state { + struct config *bp; /* The basis configurations for this state */ + struct config *cfp; /* All configurations in this set */ + int statenum; /* Sequential number for this state */ + struct action *ap; /* Array of actions for this state */ + int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */ + int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */ + int iDflt; /* Default action */ +}; +#define NO_OFFSET (-2147483647) + +/* A followset propagation link indicates that the contents of one +** configuration followset should be propagated to another whenever +** the first changes. */ +struct plink { + struct config *cfp; /* The configuration to which linked */ + struct plink *next; /* The next propagate link */ +}; + +/* The state vector for the entire parser generator is recorded as +** follows. (LEMON uses no global variables and makes little use of +** static variables. Fields in the following structure can be thought +** of as begin global variables in the program.) */ +struct lemon { + struct state **sorted; /* Table of states sorted by state number */ + struct rule *rule; /* List of all rules */ + int nstate; /* Number of states */ + int nrule; /* Number of rules */ + int nsymbol; /* Number of terminal and nonterminal symbols */ + int nterminal; /* Number of terminal symbols */ + struct symbol **symbols; /* Sorted array of pointers to symbols */ + int errorcnt; /* Number of errors */ + struct symbol *errsym; /* The error symbol */ + struct symbol *wildcard; /* Token that matches anything */ + char *name; /* Name of the generated parser */ + char *arg; /* Declaration of the 3th argument to parser */ + char *tokentype; /* Type of terminal symbols in the parser stack */ + char *vartype; /* The default type of non-terminal symbols */ + char *start; /* Name of the start symbol for the grammar */ + char *stacksize; /* Size of the parser stack */ + char *include; /* Code to put at the start of the C file */ + char *error; /* Code to execute when an error is seen */ + char *overflow; /* Code to execute on a stack overflow */ + char *failure; /* Code to execute on parser failure */ + char *accept; /* Code to execute when the parser excepts */ + char *extracode; /* Code appended to the generated file */ + char *tokendest; /* Code to execute to destroy token data */ + char *vardest; /* Code for the default non-terminal destructor */ + char *filename; /* Name of the input file */ + char *outname; /* Name of the current output file */ + char *tokenprefix; /* A prefix added to token names in the .h file */ + int nconflict; /* Number of parsing conflicts */ + int tablesize; /* Size of the parse tables */ + int basisflag; /* Print only basis configurations */ + int has_fallback; /* True if any %fallback is seen in the grammar */ + int nolinenosflag; /* True if #line statements should not be printed */ + char *argv0; /* Name of the program */ +}; + +#define MemoryCheck(X) if((X)==0){ \ + extern void memory_error(); \ + memory_error(); \ +} + +/**************** From the file "table.h" *********************************/ +/* +** All code in this file has been automatically generated +** from a specification in the file +** "table.q" +** by the associative array code building program "aagen". +** Do not edit this file! Instead, edit the specification +** file, then rerun aagen. +*/ +/* +** Code for processing tables in the LEMON parser generator. +*/ +/* Routines for handling a strings */ + +const char *Strsafe(const char *); + +void Strsafe_init(void); +int Strsafe_insert(const char *); +const char *Strsafe_find(const char *); + +/* Routines for handling symbols of the grammar */ + +struct symbol *Symbol_new(const char *); +int Symbolcmpp(const void *, const void *); +void Symbol_init(void); +int Symbol_insert(struct symbol *, const char *); +struct symbol *Symbol_find(const char *); +struct symbol *Symbol_Nth(int); +int Symbol_count(void); +struct symbol **Symbol_arrayof(void); + +/* Routines to manage the state table */ + +int Configcmp(const char *, const char *); +struct state *State_new(void); +void State_init(void); +int State_insert(struct state *, struct config *); +struct state *State_find(struct config *); +struct state **State_arrayof(/* */); + +/* Routines used for efficiency in Configlist_add */ + +void Configtable_init(void); +int Configtable_insert(struct config *); +struct config *Configtable_find(struct config *); +void Configtable_clear(int(*)(struct config *)); + +/****************** From the file "action.c" *******************************/ +/* +** Routines processing parser actions in the LEMON parser generator. +*/ + +/* Allocate a new parser action */ +static struct action *Action_new(void){ + static struct action *freelist = 0; + struct action *newaction; + + if( freelist==0 ){ + int i; + int amt = 100; + freelist = (struct action *)calloc(amt, sizeof(struct action)); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new parser action."); + exit(1); + } + for(i=0; inext; + return newaction; +} + +/* Compare two actions for sorting purposes. Return negative, zero, or +** positive if the first action is less than, equal to, or greater than +** the first +*/ +static int actioncmp( + struct action *ap1, + struct action *ap2 +){ + int rc; + rc = ap1->sp->index - ap2->sp->index; + if( rc==0 ){ + rc = (int)ap1->type - (int)ap2->type; + } + if( rc==0 && ap1->type==REDUCE ){ + rc = ap1->x.rp->index - ap2->x.rp->index; + } + if( rc==0 ){ + rc = (int) (ap2 - ap1); + } + return rc; +} + +/* Sort parser actions */ +static struct action *Action_sort( + struct action *ap +){ + ap = (struct action *)msort((char *)ap,(char **)&ap->next, + (int(*)(const char*,const char*))actioncmp); + return ap; +} + +void Action_add( + struct action **app, + enum e_action type, + struct symbol *sp, + char *arg +){ + struct action *newaction; + newaction = Action_new(); + newaction->next = *app; + *app = newaction; + newaction->type = type; + newaction->sp = sp; + if( type==SHIFT ){ + newaction->x.stp = (struct state *)arg; + }else{ + newaction->x.rp = (struct rule *)arg; + } +} +/********************** New code to implement the "acttab" module ***********/ +/* +** This module implements routines use to construct the yy_action[] table. +*/ + +/* +** The state of the yy_action table under construction is an instance of +** the following structure. +** +** The yy_action table maps the pair (state_number, lookahead) into an +** action_number. The table is an array of integers pairs. The state_number +** determines an initial offset into the yy_action array. The lookahead +** value is then added to this initial offset to get an index X into the +** yy_action array. If the aAction[X].lookahead equals the value of the +** of the lookahead input, then the value of the action_number output is +** aAction[X].action. If the lookaheads do not match then the +** default action for the state_number is returned. +** +** All actions associated with a single state_number are first entered +** into aLookahead[] using multiple calls to acttab_action(). Then the +** actions for that single state_number are placed into the aAction[] +** array with a single call to acttab_insert(). The acttab_insert() call +** also resets the aLookahead[] array in preparation for the next +** state number. +*/ +struct lookahead_action { + int lookahead; /* Value of the lookahead token */ + int action; /* Action to take on the given lookahead */ +}; +typedef struct acttab acttab; +struct acttab { + int nAction; /* Number of used slots in aAction[] */ + int nActionAlloc; /* Slots allocated for aAction[] */ + struct lookahead_action + *aAction, /* The yy_action[] table under construction */ + *aLookahead; /* A single new transaction set */ + int mnLookahead; /* Minimum aLookahead[].lookahead */ + int mnAction; /* Action associated with mnLookahead */ + int mxLookahead; /* Maximum aLookahead[].lookahead */ + int nLookahead; /* Used slots in aLookahead[] */ + int nLookaheadAlloc; /* Slots allocated in aLookahead[] */ +}; + +/* Return the number of entries in the yy_action table */ +#define acttab_size(X) ((X)->nAction) + +/* The value for the N-th entry in yy_action */ +#define acttab_yyaction(X,N) ((X)->aAction[N].action) + +/* The value for the N-th entry in yy_lookahead */ +#define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead) + +/* Free all memory associated with the given acttab */ +void acttab_free(acttab *p){ + free( p->aAction ); + free( p->aLookahead ); + free( p ); +} + +/* Allocate a new acttab structure */ +acttab *acttab_alloc(void){ + acttab *p = (acttab *) calloc( 1, sizeof(*p) ); + if( p==0 ){ + fprintf(stderr,"Unable to allocate memory for a new acttab."); + exit(1); + } + memset(p, 0, sizeof(*p)); + return p; +} + +/* Add a new action to the current transaction set. +** +** This routine is called once for each lookahead for a particular +** state. +*/ +void acttab_action(acttab *p, int lookahead, int action){ + if( p->nLookahead>=p->nLookaheadAlloc ){ + p->nLookaheadAlloc += 25; + p->aLookahead = (struct lookahead_action *) realloc( p->aLookahead, + sizeof(p->aLookahead[0])*p->nLookaheadAlloc ); + if( p->aLookahead==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + } + if( p->nLookahead==0 ){ + p->mxLookahead = lookahead; + p->mnLookahead = lookahead; + p->mnAction = action; + }else{ + if( p->mxLookaheadmxLookahead = lookahead; + if( p->mnLookahead>lookahead ){ + p->mnLookahead = lookahead; + p->mnAction = action; + } + } + p->aLookahead[p->nLookahead].lookahead = lookahead; + p->aLookahead[p->nLookahead].action = action; + p->nLookahead++; +} + +/* +** Add the transaction set built up with prior calls to acttab_action() +** into the current action table. Then reset the transaction set back +** to an empty set in preparation for a new round of acttab_action() calls. +** +** Return the offset into the action table of the new transaction. +*/ +int acttab_insert(acttab *p){ + int i, j, k, n; + assert( p->nLookahead>0 ); + + /* Make sure we have enough space to hold the expanded action table + ** in the worst case. The worst case occurs if the transaction set + ** must be appended to the current action table + */ + n = p->mxLookahead + 1; + if( p->nAction + n >= p->nActionAlloc ){ + int oldAlloc = p->nActionAlloc; + p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20; + p->aAction = (struct lookahead_action *) realloc( p->aAction, + sizeof(p->aAction[0])*p->nActionAlloc); + if( p->aAction==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=oldAlloc; inActionAlloc; i++){ + p->aAction[i].lookahead = -1; + p->aAction[i].action = -1; + } + } + + /* Scan the existing action table looking for an offset that is a + ** duplicate of the current transaction set. Fall out of the loop + ** if and when the duplicate is found. + ** + ** i is the index in p->aAction[] where p->mnLookahead is inserted. + */ + for(i=p->nAction-1; i>=0; i--){ + if( p->aAction[i].lookahead==p->mnLookahead ){ + /* All lookaheads and actions in the aLookahead[] transaction + ** must match against the candidate aAction[i] entry. */ + if( p->aAction[i].action!=p->mnAction ) continue; + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 || k>=p->nAction ) break; + if( p->aLookahead[j].lookahead!=p->aAction[k].lookahead ) break; + if( p->aLookahead[j].action!=p->aAction[k].action ) break; + } + if( jnLookahead ) continue; + + /* No possible lookahead value that is not in the aLookahead[] + ** transaction is allowed to match aAction[i] */ + n = 0; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead<0 ) continue; + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) n++; + } + if( n==p->nLookahead ){ + break; /* An exact match is found at offset i */ + } + } + } + + /* If no existing offsets exactly match the current transaction, find an + ** an empty offset in the aAction[] table in which we can add the + ** aLookahead[] transaction. + */ + if( i<0 ){ + /* Look for holes in the aAction[] table that fit the current + ** aLookahead[] transaction. Leave i set to the offset of the hole. + ** If no holes are found, i is left at p->nAction, which means the + ** transaction will be appended. */ + for(i=0; inActionAlloc - p->mxLookahead; i++){ + if( p->aAction[i].lookahead<0 ){ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 ) break; + if( p->aAction[k].lookahead>=0 ) break; + } + if( jnLookahead ) continue; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break; + } + if( j==p->nAction ){ + break; /* Fits in empty slots */ + } + } + } + } + /* Insert transaction set at index i. */ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + p->aAction[k] = p->aLookahead[j]; + if( k>=p->nAction ) p->nAction = k+1; + } + p->nLookahead = 0; + + /* Return the offset that is added to the lookahead in order to get the + ** index into yy_action of the action */ + return i - p->mnLookahead; +} + +/********************** From the file "build.c" *****************************/ +/* +** Routines to construction the finite state machine for the LEMON +** parser generator. +*/ + +/* Find a precedence symbol of every rule in the grammar. +** +** Those rules which have a precedence symbol coded in the input +** grammar using the "[symbol]" construct will already have the +** rp->precsym field filled. Other rules take as their precedence +** symbol the first RHS symbol with a defined precedence. If there +** are not RHS symbols with a defined precedence, the precedence +** symbol field is left blank. +*/ +void FindRulePrecedences(struct lemon *xp) +{ + struct rule *rp; + for(rp=xp->rule; rp; rp=rp->next){ + if( rp->precsym==0 ){ + int i, j; + for(i=0; inrhs && rp->precsym==0; i++){ + struct symbol *sp = rp->rhs[i]; + if( sp->type==MULTITERMINAL ){ + for(j=0; jnsubsym; j++){ + if( sp->subsym[j]->prec>=0 ){ + rp->precsym = sp->subsym[j]; + break; + } + } + }else if( sp->prec>=0 ){ + rp->precsym = rp->rhs[i]; + } + } + } + } + return; +} + +/* Find all nonterminals which will generate the empty string. +** Then go back and compute the first sets of every nonterminal. +** The first set is the set of all terminal symbols which can begin +** a string generated by that nonterminal. +*/ +void FindFirstSets(struct lemon *lemp) +{ + int i, j; + struct rule *rp; + int progress; + + for(i=0; insymbol; i++){ + lemp->symbols[i]->lambda = LEMON_FALSE; + } + for(i=lemp->nterminal; insymbol; i++){ + lemp->symbols[i]->firstset = SetNew(); + } + + /* First compute all lambdas */ + do{ + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->lhs->lambda ) continue; + for(i=0; inrhs; i++){ + struct symbol *sp = rp->rhs[i]; + assert( sp->type==NONTERMINAL || sp->lambda==LEMON_FALSE ); + if( sp->lambda==LEMON_FALSE ) break; + } + if( i==rp->nrhs ){ + rp->lhs->lambda = LEMON_TRUE; + progress = 1; + } + } + }while( progress ); + + /* Now compute all first sets */ + do{ + struct symbol *s1, *s2; + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + s1 = rp->lhs; + for(i=0; inrhs; i++){ + s2 = rp->rhs[i]; + if( s2->type==TERMINAL ){ + progress += SetAdd(s1->firstset,s2->index); + break; + }else if( s2->type==MULTITERMINAL ){ + for(j=0; jnsubsym; j++){ + progress += SetAdd(s1->firstset,s2->subsym[j]->index); + } + break; + }else if( s1==s2 ){ + if( s1->lambda==LEMON_FALSE ) break; + }else{ + progress += SetUnion(s1->firstset,s2->firstset); + if( s2->lambda==LEMON_FALSE ) break; + } + } + } + }while( progress ); + return; +} + +/* Compute all LR(0) states for the grammar. Links +** are added to between some states so that the LR(1) follow sets +** can be computed later. +*/ +PRIVATE struct state *getstate(struct lemon *); /* forward reference */ +void FindStates(struct lemon *lemp) +{ + struct symbol *sp; + struct rule *rp; + + Configlist_init(); + + /* Find the start symbol */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ){ + ErrorMsg(lemp->filename,0, +"The specified start symbol \"%s\" is not \ +in a nonterminal of the grammar. \"%s\" will be used as the start \ +symbol instead.",lemp->start,lemp->rule->lhs->name); + lemp->errorcnt++; + sp = lemp->rule->lhs; + } + }else{ + sp = lemp->rule->lhs; + } + + /* Make sure the start symbol doesn't occur on the right-hand side of + ** any rule. Report an error if it does. (YACC would generate a new + ** start symbol in this case.) */ + for(rp=lemp->rule; rp; rp=rp->next){ + int i; + for(i=0; inrhs; i++){ + if( rp->rhs[i]==sp ){ /* FIX ME: Deal with multiterminals */ + ErrorMsg(lemp->filename,0, +"The start symbol \"%s\" occurs on the \ +right-hand side of a rule. This will result in a parser which \ +does not work properly.",sp->name); + lemp->errorcnt++; + } + } + } + + /* The basis configuration set for the first state + ** is all rules which have the start symbol as their + ** left-hand side */ + for(rp=sp->rule; rp; rp=rp->nextlhs){ + struct config *newcfp; + rp->lhsStart = 1; + newcfp = Configlist_addbasis(rp,0); + SetAdd(newcfp->fws,0); + } + + /* Compute the first state. All other states will be + ** computed automatically during the computation of the first one. + ** The returned pointer to the first state is not used. */ + (void)getstate(lemp); + return; +} + +/* Return a pointer to a state which is described by the configuration +** list which has been built from calls to Configlist_add. +*/ +PRIVATE void buildshifts(struct lemon *, struct state *); /* Forwd ref */ +PRIVATE struct state *getstate(struct lemon *lemp) +{ + struct config *cfp, *bp; + struct state *stp; + + /* Extract the sorted basis of the new state. The basis was constructed + ** by prior calls to "Configlist_addbasis()". */ + Configlist_sortbasis(); + bp = Configlist_basis(); + + /* Get a state with the same basis */ + stp = State_find(bp); + if( stp ){ + /* A state with the same basis already exists! Copy all the follow-set + ** propagation links from the state under construction into the + ** preexisting state, then return a pointer to the preexisting state */ + struct config *x, *y; + for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){ + Plink_copy(&y->bplp,x->bplp); + Plink_delete(x->fplp); + x->fplp = x->bplp = 0; + } + cfp = Configlist_return(); + Configlist_eat(cfp); + }else{ + /* This really is a new state. Construct all the details */ + Configlist_closure(lemp); /* Compute the configuration closure */ + Configlist_sort(); /* Sort the configuration closure */ + cfp = Configlist_return(); /* Get a pointer to the config list */ + stp = State_new(); /* A new state structure */ + MemoryCheck(stp); + stp->bp = bp; /* Remember the configuration basis */ + stp->cfp = cfp; /* Remember the configuration closure */ + stp->statenum = lemp->nstate++; /* Every state gets a sequence number */ + stp->ap = 0; /* No actions, yet. */ + State_insert(stp,stp->bp); /* Add to the state table */ + buildshifts(lemp,stp); /* Recursively compute successor states */ + } + return stp; +} + +/* +** Return true if two symbols are the same. +*/ +int same_symbol(struct symbol *a, struct symbol *b) +{ + int i; + if( a==b ) return 1; + if( a->type!=MULTITERMINAL ) return 0; + if( b->type!=MULTITERMINAL ) return 0; + if( a->nsubsym!=b->nsubsym ) return 0; + for(i=0; insubsym; i++){ + if( a->subsym[i]!=b->subsym[i] ) return 0; + } + return 1; +} + +/* Construct all successor states to the given state. A "successor" +** state is any state which can be reached by a shift action. +*/ +PRIVATE void buildshifts(struct lemon *lemp, struct state *stp) +{ + struct config *cfp; /* For looping thru the config closure of "stp" */ + struct config *bcfp; /* For the inner loop on config closure of "stp" */ + struct config *newcfg; /* */ + struct symbol *sp; /* Symbol following the dot in configuration "cfp" */ + struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */ + struct state *newstp; /* A pointer to a successor state */ + + /* Each configuration becomes complete after it contibutes to a successor + ** state. Initially, all configurations are incomplete */ + for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE; + + /* Loop through all configurations of the state "stp" */ + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */ + if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */ + Configlist_reset(); /* Reset the new config set */ + sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */ + + /* For every configuration in the state "stp" which has the symbol "sp" + ** following its dot, add the same configuration to the basis set under + ** construction but with the dot shifted one symbol to the right. */ + for(bcfp=cfp; bcfp; bcfp=bcfp->next){ + if( bcfp->status==COMPLETE ) continue; /* Already used */ + if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */ + bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */ + if( !same_symbol(bsp,sp) ) continue; /* Must be same as for "cfp" */ + bcfp->status = COMPLETE; /* Mark this config as used */ + newcfg = Configlist_addbasis(bcfp->rp,bcfp->dot+1); + Plink_add(&newcfg->bplp,bcfp); + } + + /* Get a pointer to the state described by the basis configuration set + ** constructed in the preceding loop */ + newstp = getstate(lemp); + + /* The state "newstp" is reached from the state "stp" by a shift action + ** on the symbol "sp" */ + if( sp->type==MULTITERMINAL ){ + int i; + for(i=0; insubsym; i++){ + Action_add(&stp->ap,SHIFT,sp->subsym[i],(char*)newstp); + } + }else{ + Action_add(&stp->ap,SHIFT,sp,(char *)newstp); + } + } +} + +/* +** Construct the propagation links +*/ +void FindLinks(struct lemon *lemp) +{ + int i; + struct config *cfp, *other; + struct state *stp; + struct plink *plp; + + /* Housekeeping detail: + ** Add to every propagate link a pointer back to the state to + ** which the link is attached. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + cfp->stp = stp; + } + } + + /* Convert all backlinks into forward links. Only the forward + ** links are used in the follow-set computation. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + for(plp=cfp->bplp; plp; plp=plp->next){ + other = plp->cfp; + Plink_add(&other->fplp,cfp); + } + } + } +} + +/* Compute all followsets. +** +** A followset is the set of all symbols which can come immediately +** after a configuration. +*/ +void FindFollowSets(struct lemon *lemp) +{ + int i; + struct config *cfp; + struct plink *plp; + int progress; + int change; + + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + cfp->status = INCOMPLETE; + } + } + + do{ + progress = 0; + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; + for(plp=cfp->fplp; plp; plp=plp->next){ + change = SetUnion(plp->cfp->fws,cfp->fws); + if( change ){ + plp->cfp->status = INCOMPLETE; + progress = 1; + } + } + cfp->status = COMPLETE; + } + } + }while( progress ); +} + +static int resolve_conflict(struct action *,struct action *); + +/* Compute the reduce actions, and resolve conflicts. +*/ +void FindActions(struct lemon *lemp) +{ + int i,j; + struct config *cfp; + struct state *stp; + struct symbol *sp; + struct rule *rp; + + /* Add all of the reduce actions + ** A reduce action is added for each element of the followset of + ** a configuration which has its dot at the extreme right. + */ + for(i=0; instate; i++){ /* Loop over all states */ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */ + if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */ + for(j=0; jnterminal; j++){ + if( SetFind(cfp->fws,j) ){ + /* Add a reduce action to the state "stp" which will reduce by the + ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */ + Action_add(&stp->ap,REDUCE,lemp->symbols[j],(char *)cfp->rp); + } + } + } + } + } + + /* Add the accepting token */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ) sp = lemp->rule->lhs; + }else{ + sp = lemp->rule->lhs; + } + /* Add to the first state (which is always the starting state of the + ** finite state machine) an action to ACCEPT if the lookahead is the + ** start nonterminal. */ + Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0); + + /* Resolve conflicts */ + for(i=0; instate; i++){ + struct action *ap, *nap; + struct state *stp; + stp = lemp->sorted[i]; + /* assert( stp->ap ); */ + stp->ap = Action_sort(stp->ap); + for(ap=stp->ap; ap && ap->next; ap=ap->next){ + for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){ + /* The two actions "ap" and "nap" have the same lookahead. + ** Figure out which one should be used */ + lemp->nconflict += resolve_conflict(ap,nap); + } + } + } + + /* Report an error for each rule that can never be reduced. */ + for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = LEMON_FALSE; + for(i=0; instate; i++){ + struct action *ap; + for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ + if( ap->type==REDUCE ) ap->x.rp->canReduce = LEMON_TRUE; + } + } + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->canReduce ) continue; + ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n"); + lemp->errorcnt++; + } +} + +/* Resolve a conflict between the two given actions. If the +** conflict can't be resolved, return non-zero. +** +** NO LONGER TRUE: +** To resolve a conflict, first look to see if either action +** is on an error rule. In that case, take the action which +** is not associated with the error rule. If neither or both +** actions are associated with an error rule, then try to +** use precedence to resolve the conflict. +** +** If either action is a SHIFT, then it must be apx. This +** function won't work if apx->type==REDUCE and apy->type==SHIFT. +*/ +static int resolve_conflict( + struct action *apx, + struct action *apy +){ + struct symbol *spx, *spy; + int errcnt = 0; + assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */ + if( apx->type==SHIFT && apy->type==SHIFT ){ + apy->type = SSCONFLICT; + errcnt++; + } + if( apx->type==SHIFT && apy->type==REDUCE ){ + spx = apx->sp; + spy = apy->x.rp->precsym; + if( spy==0 || spx->prec<0 || spy->prec<0 ){ + /* Not enough precedence information. */ + apy->type = SRCONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ /* higher precedence wins */ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = SH_RESOLVED; + }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */ + apy->type = RD_RESOLVED; /* associativity */ + }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */ + apx->type = SH_RESOLVED; + }else{ + assert( spx->prec==spy->prec && spx->assoc==NONE ); + apy->type = SRCONFLICT; + errcnt++; + } + }else if( apx->type==REDUCE && apy->type==REDUCE ){ + spx = apx->x.rp->precsym; + spy = apy->x.rp->precsym; + if( spx==0 || spy==0 || spx->prec<0 || + spy->prec<0 || spx->prec==spy->prec ){ + apy->type = RRCONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = RD_RESOLVED; + } + }else{ + assert( + apx->type==SH_RESOLVED || + apx->type==RD_RESOLVED || + apx->type==SSCONFLICT || + apx->type==SRCONFLICT || + apx->type==RRCONFLICT || + apy->type==SH_RESOLVED || + apy->type==RD_RESOLVED || + apy->type==SSCONFLICT || + apy->type==SRCONFLICT || + apy->type==RRCONFLICT + ); + /* The REDUCE/SHIFT case cannot happen because SHIFTs come before + ** REDUCEs on the list. If we reach this point it must be because + ** the parser conflict had already been resolved. */ + } + return errcnt; +} +/********************* From the file "configlist.c" *************************/ +/* +** Routines to processing a configuration list and building a state +** in the LEMON parser generator. +*/ + +static struct config *freelist = 0; /* List of free configurations */ +static struct config *current = 0; /* Top of list of configurations */ +static struct config **currentend = 0; /* Last on list of configs */ +static struct config *basis = 0; /* Top of list of basis configs */ +static struct config **basisend = 0; /* End of list of basis configs */ + +/* Return a pointer to a new configuration */ +PRIVATE struct config *newconfig(){ + struct config *newcfg; + if( freelist==0 ){ + int i; + int amt = 3; + freelist = (struct config *)calloc( amt, sizeof(struct config) ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new configuration."); + exit(1); + } + for(i=0; inext; + return newcfg; +} + +/* The configuration "old" is no longer used */ +PRIVATE void deleteconfig(struct config *old) +{ + old->next = freelist; + freelist = old; +} + +/* Initialized the configuration list builder */ +void Configlist_init(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_init(); + return; +} + +/* Initialized the configuration list builder */ +void Configlist_reset(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_clear(0); + return; +} + +/* Add another configuration to the configuration list */ +struct config *Configlist_add( + struct rule *rp, /* The rule */ + int dot /* Index into the RHS of the rule where the dot goes */ +){ + struct config *cfp, model; + + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + Configtable_insert(cfp); + } + return cfp; +} + +/* Add a basis configuration to the configuration list */ +struct config *Configlist_addbasis(struct rule *rp, int dot) +{ + struct config *cfp, model; + + assert( basisend!=0 ); + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + *basisend = cfp; + basisend = &cfp->bp; + Configtable_insert(cfp); + } + return cfp; +} + +/* Compute the closure of the configuration list */ +void Configlist_closure(struct lemon *lemp) +{ + struct config *cfp, *newcfp; + struct rule *rp, *newrp; + struct symbol *sp, *xsp; + int i, dot; + + assert( currentend!=0 ); + for(cfp=current; cfp; cfp=cfp->next){ + rp = cfp->rp; + dot = cfp->dot; + if( dot>=rp->nrhs ) continue; + sp = rp->rhs[dot]; + if( sp->type==NONTERMINAL ){ + if( sp->rule==0 && sp!=lemp->errsym ){ + ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.", + sp->name); + lemp->errorcnt++; + } + for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){ + newcfp = Configlist_add(newrp,0); + for(i=dot+1; inrhs; i++){ + xsp = rp->rhs[i]; + if( xsp->type==TERMINAL ){ + SetAdd(newcfp->fws,xsp->index); + break; + }else if( xsp->type==MULTITERMINAL ){ + int k; + for(k=0; knsubsym; k++){ + SetAdd(newcfp->fws, xsp->subsym[k]->index); + } + break; + }else{ + SetUnion(newcfp->fws,xsp->firstset); + if( xsp->lambda==LEMON_FALSE ) break; + } + } + if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp); + } + } + } + return; +} + +/* Sort the configuration list */ +void Configlist_sort(){ + current = (struct config *)msort((char *)current,(char **)&(current->next),Configcmp); + currentend = 0; + return; +} + +/* Sort the basis configuration list */ +void Configlist_sortbasis(){ + basis = (struct config *)msort((char *)current,(char **)&(current->bp),Configcmp); + basisend = 0; + return; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_return(){ + struct config *old; + old = current; + current = 0; + currentend = 0; + return old; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_basis(){ + struct config *old; + old = basis; + basis = 0; + basisend = 0; + return old; +} + +/* Free all elements of the given configuration list */ +void Configlist_eat(struct config *cfp) +{ + struct config *nextcfp; + for(; cfp; cfp=nextcfp){ + nextcfp = cfp->next; + assert( cfp->fplp==0 ); + assert( cfp->bplp==0 ); + if( cfp->fws ) SetFree(cfp->fws); + deleteconfig(cfp); + } + return; +} +/***************** From the file "error.c" *********************************/ +/* +** Code for printing error message. +*/ + +void ErrorMsg(const char *filename, int lineno, const char *format, ...){ + va_list ap; + fprintf(stderr, "%s:%d: ", filename, lineno); + va_start(ap, format); + vfprintf(stderr,format,ap); + va_end(ap); + fprintf(stderr, "\n"); +} +/**************** From the file "main.c" ************************************/ +/* +** Main program file for the LEMON parser generator. +*/ + +/* Report an out-of-memory condition and abort. This function +** is used mostly by the "MemoryCheck" macro in struct.h +*/ +void memory_error(){ + fprintf(stderr,"Out of memory. Aborting...\n"); + exit(1); +} + +static int nDefine = 0; /* Number of -D options on the command line */ +static char **azDefine = 0; /* Name of the -D macros */ + +/* This routine is called with the argument to each -D command-line option. +** Add the macro defined to the azDefine array. +*/ +static void handle_D_option(char *z){ + char **paz; + nDefine++; + azDefine = (char **) realloc(azDefine, sizeof(azDefine[0])*nDefine); + if( azDefine==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + paz = &azDefine[nDefine-1]; + *paz = (char *) malloc( lemonStrlen(z)+1 ); + if( *paz==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + strcpy(*paz, z); + for(z=*paz; *z && *z!='='; z++){} + *z = 0; +} + +static char *user_templatename = NULL; +static void handle_T_option(char *z){ + user_templatename = (char *) malloc( lemonStrlen(z)+1 ); + if( user_templatename==0 ){ + memory_error(); + } + strcpy(user_templatename, z); +} + +/* The main program. Parse the command line and do it... */ +int main(int argc, char **argv) +{ + static int version = 0; + static int rpflag = 0; + static int basisflag = 0; + static int compress = 0; + static int quiet = 0; + static int statistics = 0; + static int mhflag = 0; + static int nolinenosflag = 0; + static int noResort = 0; + static struct s_options options[] = { + {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, + {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, + {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."}, + {OPT_FSTR, "T", (char*)handle_T_option, "Specify a template file."}, + {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, + {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file."}, + {OPT_FLAG, "l", (char*)&nolinenosflag, "Do not print #line statements."}, + {OPT_FLAG, "p", (char*)&showPrecedenceConflict, + "Show conflicts resolved by precedence rules"}, + {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."}, + {OPT_FLAG, "r", (char*)&noResort, "Do not sort or renumber states"}, + {OPT_FLAG, "s", (char*)&statistics, + "Print parser stats to standard output."}, + {OPT_FLAG, "x", (char*)&version, "Print the version number."}, + {OPT_FLAG,0,0,0} + }; + int i; + int exitcode; + struct lemon lem; + + OptInit(argv,options,stderr); + if( version ){ + printf("Lemon version 1.0\n"); + exit(0); + } + if( OptNArgs()!=1 ){ + fprintf(stderr,"Exactly one filename argument is required.\n"); + exit(1); + } + memset(&lem, 0, sizeof(lem)); + lem.errorcnt = 0; + + /* Initialize the machine */ + Strsafe_init(); + Symbol_init(); + State_init(); + lem.argv0 = argv[0]; + lem.filename = OptArg(0); + lem.basisflag = basisflag; + lem.nolinenosflag = nolinenosflag; + Symbol_new("$"); + lem.errsym = Symbol_new("error"); + lem.errsym->useCnt = 0; + + /* Parse the input file */ + Parse(&lem); + if( lem.errorcnt ) exit(lem.errorcnt); + if( lem.nrule==0 ){ + fprintf(stderr,"Empty grammar.\n"); + exit(1); + } + + /* Count and index the symbols of the grammar */ + lem.nsymbol = Symbol_count(); + Symbol_new("{default}"); + lem.symbols = Symbol_arrayof(); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*), Symbolcmpp); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + for(i=1; isupper(lem.symbols[i]->name[0]); i++); + lem.nterminal = i; + + /* Generate a reprint of the grammar, if requested on the command line */ + if( rpflag ){ + Reprint(&lem); + }else{ + /* Initialize the size for all follow and first sets */ + SetSize(lem.nterminal+1); + + /* Find the precedence for every production rule (that has one) */ + FindRulePrecedences(&lem); + + /* Compute the lambda-nonterminals and the first-sets for every + ** nonterminal */ + FindFirstSets(&lem); + + /* Compute all LR(0) states. Also record follow-set propagation + ** links so that the follow-set can be computed later */ + lem.nstate = 0; + FindStates(&lem); + lem.sorted = State_arrayof(); + + /* Tie up loose ends on the propagation links */ + FindLinks(&lem); + + /* Compute the follow set of every reducible configuration */ + FindFollowSets(&lem); + + /* Compute the action tables */ + FindActions(&lem); + + /* Compress the action tables */ + if( compress==0 ) CompressTables(&lem); + + /* Reorder and renumber the states so that states with fewer choices + ** occur at the end. This is an optimization that helps make the + ** generated parser tables smaller. */ + if( noResort==0 ) ResortStates(&lem); + + /* Generate a report of the parser generated. (the "y.output" file) */ + if( !quiet ) ReportOutput(&lem); + + /* Generate the source code for the parser */ + ReportTable(&lem, mhflag); + + /* Produce a header file for use by the scanner. (This step is + ** omitted if the "-m" option is used because makeheaders will + ** generate the file for us.) */ + if( !mhflag ) ReportHeader(&lem); + } + if( statistics ){ + printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n", + lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule); + printf(" %d states, %d parser table entries, %d conflicts\n", + lem.nstate, lem.tablesize, lem.nconflict); + } + if( lem.nconflict > 0 ){ + fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); + } + + /* return 0 on success, 1 on failure. */ + exitcode = ((lem.errorcnt > 0) || (lem.nconflict > 0)) ? 1 : 0; + exit(exitcode); + return (exitcode); +} +/******************** From the file "msort.c" *******************************/ +/* +** A generic merge-sort program. +** +** USAGE: +** Let "ptr" be a pointer to some structure which is at the head of +** a null-terminated list. Then to sort the list call: +** +** ptr = msort(ptr,&(ptr->next),cmpfnc); +** +** In the above, "cmpfnc" is a pointer to a function which compares +** two instances of the structure and returns an integer, as in +** strcmp. The second argument is a pointer to the pointer to the +** second element of the linked list. This address is used to compute +** the offset to the "next" field within the structure. The offset to +** the "next" field must be constant for all structures in the list. +** +** The function returns a new pointer which is the head of the list +** after sorting. +** +** ALGORITHM: +** Merge-sort. +*/ + +/* +** Return a pointer to the next structure in the linked list. +*/ +#define NEXT(A) (*(char**)(((char*)A)+offset)) + +/* +** Inputs: +** a: A sorted, null-terminated linked list. (May be null). +** b: A sorted, null-terminated linked list. (May be null). +** cmp: A pointer to the comparison function. +** offset: Offset in the structure to the "next" field. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** of both a and b. +** +** Side effects: +** The "next" pointers for elements in the lists a and b are +** changed. +*/ +static char *merge( + char *a, + char *b, + int (*cmp)(const char*,const char*), + int offset +){ + char *ptr, *head; + + if( a==0 ){ + head = b; + }else if( b==0 ){ + head = a; + }else{ + if( (*cmp)(a,b)<=0 ){ + ptr = a; + a = NEXT(a); + }else{ + ptr = b; + b = NEXT(b); + } + head = ptr; + while( a && b ){ + if( (*cmp)(a,b)<=0 ){ + NEXT(ptr) = a; + ptr = a; + a = NEXT(a); + }else{ + NEXT(ptr) = b; + ptr = b; + b = NEXT(b); + } + } + if( a ) NEXT(ptr) = a; + else NEXT(ptr) = b; + } + return head; +} + +/* +** Inputs: +** list: Pointer to a singly-linked list of structures. +** next: Pointer to pointer to the second element of the list. +** cmp: A comparison function. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** orginally in list. +** +** Side effects: +** The "next" pointers for elements in list are changed. +*/ +#define LISTSIZE 30 +static char *msort( + char *list, + char **next, + int (*cmp)(const char*,const char*) +){ + unsigned long offset; + char *ep; + char *set[LISTSIZE]; + int i; + offset = (unsigned long)next - (unsigned long)list; + for(i=0; istate = WAITING_FOR_DECL_KEYWORD; + }else if( islower(x[0]) ){ + psp->lhs = Symbol_new(x); + psp->nrhs = 0; + psp->lhsalias = 0; + psp->state = WAITING_FOR_ARROW; + }else if( x[0]=='{' ){ + if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"There is no prior rule upon which to attach the code \ +fragment which begins on this line."); + psp->errorcnt++; + }else if( psp->prevrule->code!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Code fragment beginning on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->line = psp->tokenlineno; + psp->prevrule->code = &x[1]; + } + }else if( x[0]=='[' ){ + psp->state = PRECEDENCE_MARK_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Token \"%s\" should be either \"%%\" or a nonterminal name.", + x); + psp->errorcnt++; + } + break; + case PRECEDENCE_MARK_1: + if( !isupper(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The precedence symbol must be a terminal."); + psp->errorcnt++; + }else if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "There is no prior rule to assign precedence \"[%s]\".",x); + psp->errorcnt++; + }else if( psp->prevrule->precsym!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Precedence mark on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->precsym = Symbol_new(x); + } + psp->state = PRECEDENCE_MARK_2; + break; + case PRECEDENCE_MARK_2: + if( x[0]!=']' ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"]\" on precedence mark."); + psp->errorcnt++; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + break; + case WAITING_FOR_ARROW: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else if( x[0]=='(' ){ + psp->state = LHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Expected to see a \":\" following the LHS symbol \"%s\".", + psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->lhsalias = x; + psp->state = LHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the LHS \"%s\"\n", + x,psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = LHS_ALIAS_3; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_3: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"->\" following: \"%s(%s)\".", + psp->lhs->name,psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case IN_RHS: + if( x[0]=='.' ){ + struct rule *rp; + rp = (struct rule *)calloc( sizeof(struct rule) + + sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs, 1); + if( rp==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't allocate enough memory for this rule."); + psp->errorcnt++; + psp->prevrule = 0; + }else{ + int i; + rp->ruleline = psp->tokenlineno; + rp->rhs = (struct symbol**)&rp[1]; + rp->rhsalias = (const char**)&(rp->rhs[psp->nrhs]); + for(i=0; inrhs; i++){ + rp->rhs[i] = psp->rhs[i]; + rp->rhsalias[i] = psp->alias[i]; + } + rp->lhs = psp->lhs; + rp->lhsalias = psp->lhsalias; + rp->nrhs = psp->nrhs; + rp->code = 0; + rp->precsym = 0; + rp->index = psp->gp->nrule++; + rp->nextlhs = rp->lhs->rule; + rp->lhs->rule = rp; + rp->next = 0; + if( psp->firstrule==0 ){ + psp->firstrule = psp->lastrule = rp; + }else{ + psp->lastrule->next = rp; + psp->lastrule = rp; + } + psp->prevrule = rp; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isalpha(x[0]) ){ + if( psp->nrhs>=MAXRHS ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Too many symbols on RHS of rule beginning at \"%s\".", + x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + }else{ + psp->rhs[psp->nrhs] = Symbol_new(x); + psp->alias[psp->nrhs] = 0; + psp->nrhs++; + } + }else if( (x[0]=='|' || x[0]=='/') && psp->nrhs>0 ){ + struct symbol *msp = psp->rhs[psp->nrhs-1]; + if( msp->type!=MULTITERMINAL ){ + struct symbol *origsp = msp; + msp = (struct symbol *) calloc(1,sizeof(*msp)); + memset(msp, 0, sizeof(*msp)); + msp->type = MULTITERMINAL; + msp->nsubsym = 1; + msp->subsym = (struct symbol **) calloc(1,sizeof(struct symbol*)); + msp->subsym[0] = origsp; + msp->name = origsp->name; + psp->rhs[psp->nrhs-1] = msp; + } + msp->nsubsym++; + msp->subsym = (struct symbol **) realloc(msp->subsym, + sizeof(struct symbol*)*msp->nsubsym); + msp->subsym[msp->nsubsym-1] = Symbol_new(&x[1]); + if( islower(x[1]) || islower(msp->subsym[0]->name[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Cannot form a compound containing a non-terminal"); + psp->errorcnt++; + } + }else if( x[0]=='(' && psp->nrhs>0 ){ + psp->state = RHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal character on RHS of rule: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->alias[psp->nrhs-1] = x; + psp->state = RHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n", + x,psp->rhs[psp->nrhs-1]->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case WAITING_FOR_DECL_KEYWORD: + if( isalpha(x[0]) ){ + psp->declkeyword = x; + psp->declargslot = 0; + psp->decllinenoslot = 0; + psp->insertLineMacro = 1; + psp->state = WAITING_FOR_DECL_ARG; + if( strcmp(x,"name")==0 ){ + psp->declargslot = &(psp->gp->name); + psp->insertLineMacro = 0; + }else if( strcmp(x,"include")==0 ){ + psp->declargslot = &(psp->gp->include); + }else if( strcmp(x,"code")==0 ){ + psp->declargslot = &(psp->gp->extracode); + }else if( strcmp(x,"token_destructor")==0 ){ + psp->declargslot = &psp->gp->tokendest; + }else if( strcmp(x,"default_destructor")==0 ){ + psp->declargslot = &psp->gp->vardest; + }else if( strcmp(x,"token_prefix")==0 ){ + psp->declargslot = &psp->gp->tokenprefix; + psp->insertLineMacro = 0; + }else if( strcmp(x,"syntax_error")==0 ){ + psp->declargslot = &(psp->gp->error); + }else if( strcmp(x,"parse_accept")==0 ){ + psp->declargslot = &(psp->gp->accept); + }else if( strcmp(x,"parse_failure")==0 ){ + psp->declargslot = &(psp->gp->failure); + }else if( strcmp(x,"stack_overflow")==0 ){ + psp->declargslot = &(psp->gp->overflow); + }else if( strcmp(x,"extra_argument")==0 ){ + psp->declargslot = &(psp->gp->arg); + psp->insertLineMacro = 0; + }else if( strcmp(x,"token_type")==0 ){ + psp->declargslot = &(psp->gp->tokentype); + psp->insertLineMacro = 0; + }else if( strcmp(x,"default_type")==0 ){ + psp->declargslot = &(psp->gp->vartype); + psp->insertLineMacro = 0; + }else if( strcmp(x,"stack_size")==0 ){ + psp->declargslot = &(psp->gp->stacksize); + psp->insertLineMacro = 0; + }else if( strcmp(x,"start_symbol")==0 ){ + psp->declargslot = &(psp->gp->start); + psp->insertLineMacro = 0; + }else if( strcmp(x,"left")==0 ){ + psp->preccounter++; + psp->declassoc = LEFT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"right")==0 ){ + psp->preccounter++; + psp->declassoc = RIGHT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"nonassoc")==0 ){ + psp->preccounter++; + psp->declassoc = NONE; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"destructor")==0 ){ + psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL; + }else if( strcmp(x,"type")==0 ){ + psp->state = WAITING_FOR_DATATYPE_SYMBOL; + }else if( strcmp(x,"fallback")==0 ){ + psp->fallback = 0; + psp->state = WAITING_FOR_FALLBACK_ID; + }else if( strcmp(x,"wildcard")==0 ){ + psp->state = WAITING_FOR_WILDCARD_ID; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Unknown declaration keyword: \"%%%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal declaration keyword: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_DESTRUCTOR_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %%destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->destructor; + psp->decllinenoslot = &sp->destLineno; + psp->insertLineMacro = 1; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_DATATYPE_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %%type keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_find(x); + if((sp) && (sp->datatype)){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol %%type \"%s\" already defined", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + if (!sp){ + sp = Symbol_new(x); + } + psp->declargslot = &sp->datatype; + psp->insertLineMacro = 0; + psp->state = WAITING_FOR_DECL_ARG; + } + } + break; + case WAITING_FOR_PRECEDENCE_SYMBOL: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isupper(x[0]) ){ + struct symbol *sp; + sp = Symbol_new(x); + if( sp->prec>=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol \"%s\" has already be given a precedence.",x); + psp->errorcnt++; + }else{ + sp->prec = psp->preccounter; + sp->assoc = psp->declassoc; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't assign a precedence to \"%s\".",x); + psp->errorcnt++; + } + break; + case WAITING_FOR_DECL_ARG: + if( x[0]=='{' || x[0]=='\"' || isalnum(x[0]) ){ + const char *zOld, *zNew; + char *zBuf, *z; + int nOld, n, nLine, nNew, nBack; + int addLineMacro; + char zLine[50]; + zNew = x; + if( zNew[0]=='"' || zNew[0]=='{' ) zNew++; + nNew = lemonStrlen(zNew); + if( *psp->declargslot ){ + zOld = *psp->declargslot; + }else{ + zOld = ""; + } + nOld = lemonStrlen(zOld); + n = nOld + nNew + 20; + addLineMacro = !psp->gp->nolinenosflag && psp->insertLineMacro && + (psp->decllinenoslot==0 || psp->decllinenoslot[0]!=0); + if( addLineMacro ){ + for(z=psp->filename, nBack=0; *z; z++){ + if( *z=='\\' ) nBack++; + } + sprintf(zLine, "#line %d ", psp->tokenlineno); + nLine = lemonStrlen(zLine); + n += nLine + lemonStrlen(psp->filename) + nBack; + } + *psp->declargslot = (char *) realloc(*psp->declargslot, n); + zBuf = *psp->declargslot + nOld; + if( addLineMacro ){ + if( nOld && zBuf[-1]!='\n' ){ + *(zBuf++) = '\n'; + } + memcpy(zBuf, zLine, nLine); + zBuf += nLine; + *(zBuf++) = '"'; + for(z=psp->filename; *z; z++){ + if( *z=='\\' ){ + *(zBuf++) = '\\'; + } + *(zBuf++) = *z; + } + *(zBuf++) = '"'; + *(zBuf++) = '\n'; + } + if( psp->decllinenoslot && psp->decllinenoslot[0]==0 ){ + psp->decllinenoslot[0] = psp->tokenlineno; + } + memcpy(zBuf, zNew, nNew); + zBuf += nNew; + *zBuf = 0; + psp->state = WAITING_FOR_DECL_OR_RULE; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal argument to %%%s: %s",psp->declkeyword,x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_FALLBACK_ID: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( !isupper(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%fallback argument \"%s\" should be a token", x); + psp->errorcnt++; + }else{ + struct symbol *sp = Symbol_new(x); + if( psp->fallback==0 ){ + psp->fallback = sp; + }else if( sp->fallback ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "More than one fallback assigned to token %s", x); + psp->errorcnt++; + }else{ + sp->fallback = psp->fallback; + psp->gp->has_fallback = 1; + } + } + break; + case WAITING_FOR_WILDCARD_ID: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( !isupper(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%wildcard argument \"%s\" should be a token", x); + psp->errorcnt++; + }else{ + struct symbol *sp = Symbol_new(x); + if( psp->gp->wildcard==0 ){ + psp->gp->wildcard = sp; + }else{ + ErrorMsg(psp->filename, psp->tokenlineno, + "Extra wildcard to token: %s", x); + psp->errorcnt++; + } + } + break; + case RESYNC_AFTER_RULE_ERROR: +/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; +** break; */ + case RESYNC_AFTER_DECL_ERROR: + if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; + if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD; + break; + } +} + +/* Run the preprocessor over the input file text. The global variables +** azDefine[0] through azDefine[nDefine-1] contains the names of all defined +** macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and +** comments them out. Text in between is also commented out as appropriate. +*/ +static void preprocess_input(char *z){ + int i, j, k, n; + int exclude = 0; + int start = 0; + int lineno = 1; + int start_lineno = 1; + for(i=0; z[i]; i++){ + if( z[i]=='\n' ) lineno++; + if( z[i]!='%' || (i>0 && z[i-1]!='\n') ) continue; + if( strncmp(&z[i],"%endif",6)==0 && isspace(z[i+6]) ){ + if( exclude ){ + exclude--; + if( exclude==0 ){ + for(j=start; jfilename; + ps.errorcnt = 0; + ps.state = INITIALIZE; + + /* Begin by reading the input file */ + fp = fopen(ps.filename,"rb"); + if( fp==0 ){ + ErrorMsg(ps.filename,0,"Can't open this file for reading."); + gp->errorcnt++; + return; + } + fseek(fp,0,2); + filesize = ftell(fp); + rewind(fp); + filebuf = (char *)malloc( filesize+1 ); + if( filebuf==0 ){ + ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.", + filesize+1); + gp->errorcnt++; + fclose(fp); + return; + } + if( fread(filebuf,1,filesize,fp)!=filesize ){ + ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.", + filesize); + free(filebuf); + gp->errorcnt++; + fclose(fp); + return; + } + fclose(fp); + filebuf[filesize] = 0; + + /* Make an initial pass through the file to handle %ifdef and %ifndef */ + preprocess_input(filebuf); + + /* Now scan the text of the input file */ + lineno = 1; + for(cp=filebuf; (c= *cp)!=0; ){ + if( c=='\n' ) lineno++; /* Keep track of the line number */ + if( isspace(c) ){ cp++; continue; } /* Skip all white space */ + if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */ + cp+=2; + while( (c= *cp)!=0 && c!='\n' ) cp++; + continue; + } + if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */ + cp+=2; + while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c ) cp++; + continue; + } + ps.tokenstart = cp; /* Mark the beginning of the token */ + ps.tokenlineno = lineno; /* Linenumber on which token begins */ + if( c=='\"' ){ /* String literals */ + cp++; + while( (c= *cp)!=0 && c!='\"' ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c==0 ){ + ErrorMsg(ps.filename,startline, +"String starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( c=='{' ){ /* A block of C code */ + int level; + cp++; + for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){ + if( c=='\n' ) lineno++; + else if( c=='{' ) level++; + else if( c=='}' ) level--; + else if( c=='/' && cp[1]=='*' ){ /* Skip comments */ + int prevc; + cp = &cp[2]; + prevc = 0; + while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){ + if( c=='\n' ) lineno++; + prevc = c; + cp++; + } + }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */ + cp = &cp[2]; + while( (c= *cp)!=0 && c!='\n' ) cp++; + if( c ) lineno++; + }else if( c=='\'' || c=='\"' ){ /* String a character literals */ + int startchar, prevc; + startchar = c; + prevc = 0; + for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){ + if( c=='\n' ) lineno++; + if( prevc=='\\' ) prevc = 0; + else prevc = c; + } + } + } + if( c==0 ){ + ErrorMsg(ps.filename,ps.tokenlineno, +"C code starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( isalnum(c) ){ /* Identifiers */ + while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */ + cp += 3; + nextcp = cp; + }else if( (c=='/' || c=='|') && isalpha(cp[1]) ){ + cp += 2; + while( (c = *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else{ /* All other (one character) operators */ + cp++; + nextcp = cp; + } + c = *cp; + *cp = 0; /* Null terminate the token */ + parseonetoken(&ps); /* Parse the token */ + *cp = c; /* Restore the buffer */ + cp = nextcp; + } + free(filebuf); /* Release the buffer after parsing */ + gp->rule = ps.firstrule; + gp->errorcnt = ps.errorcnt; +} +/*************************** From the file "plink.c" *********************/ +/* +** Routines processing configuration follow-set propagation links +** in the LEMON parser generator. +*/ +static struct plink *plink_freelist = 0; + +/* Allocate a new plink */ +struct plink *Plink_new(){ + struct plink *newlink; + + if( plink_freelist==0 ){ + int i; + int amt = 100; + plink_freelist = (struct plink *)calloc( amt, sizeof(struct plink) ); + if( plink_freelist==0 ){ + fprintf(stderr, + "Unable to allocate memory for a new follow-set propagation link.\n"); + exit(1); + } + for(i=0; inext; + return newlink; +} + +/* Add a plink to a plink list */ +void Plink_add(struct plink **plpp, struct config *cfp) +{ + struct plink *newlink; + newlink = Plink_new(); + newlink->next = *plpp; + *plpp = newlink; + newlink->cfp = cfp; +} + +/* Transfer every plink on the list "from" to the list "to" */ +void Plink_copy(struct plink **to, struct plink *from) +{ + struct plink *nextpl; + while( from ){ + nextpl = from->next; + from->next = *to; + *to = from; + from = nextpl; + } +} + +/* Delete every plink on the list */ +void Plink_delete(struct plink *plp) +{ + struct plink *nextpl; + + while( plp ){ + nextpl = plp->next; + plp->next = plink_freelist; + plink_freelist = plp; + plp = nextpl; + } +} +/*********************** From the file "report.c" **************************/ +/* +** Procedures for generating reports and tables in the LEMON parser generator. +*/ + +/* Generate a filename with the given suffix. Space to hold the +** name comes from malloc() and must be freed by the calling +** function. +*/ +PRIVATE char *file_makename(struct lemon *lemp, const char *suffix) +{ + char *name; + char *cp; + + name = (char*)malloc( lemonStrlen(lemp->filename) + lemonStrlen(suffix) + 5 ); + if( name==0 ){ + fprintf(stderr,"Can't allocate space for a filename.\n"); + exit(1); + } + strcpy(name,lemp->filename); + cp = strrchr(name,'.'); + if( cp ) *cp = 0; + strcat(name,suffix); + return name; +} + +/* Open a file with a name based on the name of the input file, +** but with a different (specified) suffix, and return a pointer +** to the stream */ +PRIVATE FILE *file_open( + struct lemon *lemp, + const char *suffix, + const char *mode +){ + FILE *fp; + + if( lemp->outname ) free(lemp->outname); + lemp->outname = file_makename(lemp, suffix); + fp = fopen(lemp->outname,mode); + if( fp==0 && *mode=='w' ){ + fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); + lemp->errorcnt++; + return 0; + } + return fp; +} + +/* Duplicate the input file without comments and without actions +** on rules */ +void Reprint(struct lemon *lemp) +{ + struct rule *rp; + struct symbol *sp; + int i, j, maxlen, len, ncolumns, skip; + printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename); + maxlen = 10; + for(i=0; insymbol; i++){ + sp = lemp->symbols[i]; + len = lemonStrlen(sp->name); + if( len>maxlen ) maxlen = len; + } + ncolumns = 76/(maxlen+5); + if( ncolumns<1 ) ncolumns = 1; + skip = (lemp->nsymbol + ncolumns - 1)/ncolumns; + for(i=0; insymbol; j+=skip){ + sp = lemp->symbols[j]; + assert( sp->index==j ); + printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name); + } + printf("\n"); + } + for(rp=lemp->rule; rp; rp=rp->next){ + printf("%s",rp->lhs->name); + /* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */ + printf(" ::="); + for(i=0; inrhs; i++){ + sp = rp->rhs[i]; + printf(" %s", sp->name); + if( sp->type==MULTITERMINAL ){ + for(j=1; jnsubsym; j++){ + printf("|%s", sp->subsym[j]->name); + } + } + /* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */ + } + printf("."); + if( rp->precsym ) printf(" [%s]",rp->precsym->name); + /* if( rp->code ) printf("\n %s",rp->code); */ + printf("\n"); + } +} + +void ConfigPrint(FILE *fp, struct config *cfp) +{ + struct rule *rp; + struct symbol *sp; + int i, j; + rp = cfp->rp; + fprintf(fp,"%s ::=",rp->lhs->name); + for(i=0; i<=rp->nrhs; i++){ + if( i==cfp->dot ) fprintf(fp," *"); + if( i==rp->nrhs ) break; + sp = rp->rhs[i]; + fprintf(fp," %s", sp->name); + if( sp->type==MULTITERMINAL ){ + for(j=1; jnsubsym; j++){ + fprintf(fp,"|%s",sp->subsym[j]->name); + } + } + } +} + +/* #define TEST */ +#if 0 +/* Print a set */ +PRIVATE void SetPrint(out,set,lemp) +FILE *out; +char *set; +struct lemon *lemp; +{ + int i; + char *spacer; + spacer = ""; + fprintf(out,"%12s[",""); + for(i=0; interminal; i++){ + if( SetFind(set,i) ){ + fprintf(out,"%s%s",spacer,lemp->symbols[i]->name); + spacer = " "; + } + } + fprintf(out,"]\n"); +} + +/* Print a plink chain */ +PRIVATE void PlinkPrint(out,plp,tag) +FILE *out; +struct plink *plp; +char *tag; +{ + while( plp ){ + fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->statenum); + ConfigPrint(out,plp->cfp); + fprintf(out,"\n"); + plp = plp->next; + } +} +#endif + +/* Print an action to the given file descriptor. Return FALSE if +** nothing was actually printed. +*/ +int PrintAction(struct action *ap, FILE *fp, int indent){ + int result = 1; + switch( ap->type ){ + case SHIFT: + fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->statenum); + break; + case REDUCE: + fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index); + break; + case ACCEPT: + fprintf(fp,"%*s accept",indent,ap->sp->name); + break; + case ERROR: + fprintf(fp,"%*s error",indent,ap->sp->name); + break; + case SRCONFLICT: + case RRCONFLICT: + fprintf(fp,"%*s reduce %-3d ** Parsing conflict **", + indent,ap->sp->name,ap->x.rp->index); + break; + case SSCONFLICT: + fprintf(fp,"%*s shift %-3d ** Parsing conflict **", + indent,ap->sp->name,ap->x.stp->statenum); + break; + case SH_RESOLVED: + if( showPrecedenceConflict ){ + fprintf(fp,"%*s shift %-3d -- dropped by precedence", + indent,ap->sp->name,ap->x.stp->statenum); + }else{ + result = 0; + } + break; + case RD_RESOLVED: + if( showPrecedenceConflict ){ + fprintf(fp,"%*s reduce %-3d -- dropped by precedence", + indent,ap->sp->name,ap->x.rp->index); + }else{ + result = 0; + } + break; + case NOT_USED: + result = 0; + break; + } + return result; +} + +/* Generate the "y.output" log file */ +void ReportOutput(struct lemon *lemp) +{ + int i; + struct state *stp; + struct config *cfp; + struct action *ap; + FILE *fp; + + fp = file_open(lemp,".out","wb"); + if( fp==0 ) return; + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + fprintf(fp,"State %d:\n",stp->statenum); + if( lemp->basisflag ) cfp=stp->bp; + else cfp=stp->cfp; + while( cfp ){ + char buf[20]; + if( cfp->dot==cfp->rp->nrhs ){ + sprintf(buf,"(%d)",cfp->rp->index); + fprintf(fp," %5s ",buf); + }else{ + fprintf(fp," "); + } + ConfigPrint(fp,cfp); + fprintf(fp,"\n"); +#if 0 + SetPrint(fp,cfp->fws,lemp); + PlinkPrint(fp,cfp->fplp,"To "); + PlinkPrint(fp,cfp->bplp,"From"); +#endif + if( lemp->basisflag ) cfp=cfp->bp; + else cfp=cfp->next; + } + fprintf(fp,"\n"); + for(ap=stp->ap; ap; ap=ap->next){ + if( PrintAction(ap,fp,30) ) fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + } + fprintf(fp, "----------------------------------------------------\n"); + fprintf(fp, "Symbols:\n"); + for(i=0; insymbol; i++){ + int j; + struct symbol *sp; + + sp = lemp->symbols[i]; + fprintf(fp, " %3d: %s", i, sp->name); + if( sp->type==NONTERMINAL ){ + fprintf(fp, ":"); + if( sp->lambda ){ + fprintf(fp, " "); + } + for(j=0; jnterminal; j++){ + if( sp->firstset && SetFind(sp->firstset, j) ){ + fprintf(fp, " %s", lemp->symbols[j]->name); + } + } + } + fprintf(fp, "\n"); + } + fclose(fp); + return; +} + +/* Search for the file "name" which is in the same directory as +** the exacutable */ +PRIVATE char *pathsearch(char *argv0, char *name, int modemask) +{ + const char *pathlist; + char *pathbufptr; + char *pathbuf; + char *path,*cp; + char c; + +#ifdef __WIN32__ + cp = strrchr(argv0,'\\'); +#else + cp = strrchr(argv0,'/'); +#endif + if( cp ){ + c = *cp; + *cp = 0; + path = (char *)malloc( lemonStrlen(argv0) + lemonStrlen(name) + 2 ); + if( path ) sprintf(path,"%s/%s",argv0,name); + *cp = c; + }else{ + pathlist = getenv("PATH"); + if( pathlist==0 ) pathlist = ".:/bin:/usr/bin"; + pathbuf = (char *) malloc( lemonStrlen(pathlist) + 1 ); + path = (char *)malloc( lemonStrlen(pathlist)+lemonStrlen(name)+2 ); + if( (pathbuf != 0) && (path!=0) ){ + pathbufptr = pathbuf; + strcpy(pathbuf, pathlist); + while( *pathbuf ){ + cp = strchr(pathbuf,':'); + if( cp==0 ) cp = &pathbuf[lemonStrlen(pathbuf)]; + c = *cp; + *cp = 0; + sprintf(path,"%s/%s",pathbuf,name); + *cp = c; + if( c==0 ) pathbuf[0] = 0; + else pathbuf = &cp[1]; + if( access(path,modemask)==0 ) break; + } + free(pathbufptr); + } + } + return path; +} + +/* Given an action, compute the integer value for that action +** which is to be put in the action table of the generated machine. +** Return negative if no action should be generated. +*/ +PRIVATE int compute_action(struct lemon *lemp, struct action *ap) +{ + int act; + switch( ap->type ){ + case SHIFT: act = ap->x.stp->statenum; break; + case REDUCE: act = ap->x.rp->index + lemp->nstate; break; + case ERROR: act = lemp->nstate + lemp->nrule; break; + case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break; + default: act = -1; break; + } + return act; +} + +#define LINESIZE 1000 +/* The next cluster of routines are for reading the template file +** and writing the results to the generated parser */ +/* The first function transfers data from "in" to "out" until +** a line is seen which begins with "%%". The line number is +** tracked. +** +** if name!=0, then any word that begin with "Parse" is changed to +** begin with *name instead. +*/ +PRIVATE void tplt_xfer(char *name, FILE *in, FILE *out, int *lineno) +{ + int i, iStart; + char line[LINESIZE]; + while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){ + (*lineno)++; + iStart = 0; + if( name ){ + for(i=0; line[i]; i++){ + if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0 + && (i==0 || !isalpha(line[i-1])) + ){ + if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]); + fprintf(out,"%s",name); + i += 4; + iStart = i+1; + } + } + } + fprintf(out,"%s",&line[iStart]); + } +} + +/* The next function finds the template file and opens it, returning +** a pointer to the opened file. */ +PRIVATE FILE *tplt_open(struct lemon *lemp) +{ + static char templatename[] = "lempar.c"; + char buf[1000]; + FILE *in; + char *tpltname; + char *cp; + + /* first, see if user specified a template filename on the command line. */ + if (user_templatename != 0) { + if( access(user_templatename,004)==-1 ){ + fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", + user_templatename); + lemp->errorcnt++; + return 0; + } + in = fopen(user_templatename,"rb"); + if( in==0 ){ + fprintf(stderr,"Can't open the template file \"%s\".\n",user_templatename); + lemp->errorcnt++; + return 0; + } + return in; + } + + cp = strrchr(lemp->filename,'.'); + if( cp ){ + sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename); + }else{ + sprintf(buf,"%s.lt",lemp->filename); + } + if( access(buf,004)==0 ){ + tpltname = buf; + }else if( access(templatename,004)==0 ){ + tpltname = templatename; + }else{ + tpltname = pathsearch(lemp->argv0,templatename,0); + } + if( tpltname==0 ){ + fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", + templatename); + lemp->errorcnt++; + return 0; + } + in = fopen(tpltname,"rb"); + if( in==0 ){ + fprintf(stderr,"Can't open the template file \"%s\".\n",templatename); + lemp->errorcnt++; + return 0; + } + return in; +} + +/* Print a #line directive line to the output file. */ +PRIVATE void tplt_linedir(FILE *out, int lineno, char *filename) +{ + fprintf(out,"#line %d \"",lineno); + while( *filename ){ + if( *filename == '\\' ) putc('\\',out); + putc(*filename,out); + filename++; + } + fprintf(out,"\"\n"); +} + +/* Print a string to the file and keep the linenumber up to date */ +PRIVATE void tplt_print(FILE *out, struct lemon *lemp, char *str, int *lineno) +{ + if( str==0 ) return; + while( *str ){ + putc(*str,out); + if( *str=='\n' ) (*lineno)++; + str++; + } + if( str[-1]!='\n' ){ + putc('\n',out); + (*lineno)++; + } + if (!lemp->nolinenosflag) { + (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); + } + return; +} + +/* +** The following routine emits code for the destructor for the +** symbol sp +*/ +void emit_destructor_code( + FILE *out, + struct symbol *sp, + struct lemon *lemp, + int *lineno +){ + char *cp = 0; + + if( sp->type==TERMINAL ){ + cp = lemp->tokendest; + if( cp==0 ) return; + fprintf(out,"{\n"); (*lineno)++; + }else if( sp->destructor ){ + cp = sp->destructor; + fprintf(out,"{\n"); (*lineno)++; + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,sp->destLineno,lemp->filename); } + }else if( lemp->vardest ){ + cp = lemp->vardest; + if( cp==0 ) return; + fprintf(out,"{\n"); (*lineno)++; + }else{ + assert( 0 ); /* Cannot happen */ + } + for(; *cp; cp++){ + if( *cp=='$' && cp[1]=='$' ){ + fprintf(out,"(yypminor->yy%d)",sp->dtnum); + cp++; + continue; + } + if( *cp=='\n' ) (*lineno)++; + fputc(*cp,out); + } + fprintf(out,"\n"); (*lineno)++; + if (!lemp->nolinenosflag) { + (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); + } + fprintf(out,"}\n"); (*lineno)++; + return; +} + +/* +** Return TRUE (non-zero) if the given symbol has a destructor. +*/ +int has_destructor(struct symbol *sp, struct lemon *lemp) +{ + int ret; + if( sp->type==TERMINAL ){ + ret = lemp->tokendest!=0; + }else{ + ret = lemp->vardest!=0 || sp->destructor!=0; + } + return ret; +} + +/* +** Append text to a dynamically allocated string. If zText is 0 then +** reset the string to be empty again. Always return the complete text +** of the string (which is overwritten with each call). +** +** n bytes of zText are stored. If n==0 then all of zText up to the first +** \000 terminator is stored. zText can contain up to two instances of +** %d. The values of p1 and p2 are written into the first and second +** %d. +** +** If n==-1, then the previous character is overwritten. +*/ +PRIVATE char *append_str(const char *zText, int n, int p1, int p2){ + static char empty[1] = { 0 }; + static char *z = 0; + static int alloced = 0; + static int used = 0; + int c; + char zInt[40]; + if( zText==0 ){ + used = 0; + return z; + } + if( n<=0 ){ + if( n<0 ){ + used += n; + assert( used>=0 ); + } + n = lemonStrlen(zText); + } + if( (int) (n+sizeof(zInt)*2+used) >= alloced ){ + alloced = n + sizeof(zInt)*2 + used + 200; + z = (char *) realloc(z, alloced); + } + if( z==0 ) return empty; + while( n-- > 0 ){ + c = *(zText++); + if( c=='%' && n>0 && zText[0]=='d' ){ + sprintf(zInt, "%d", p1); + p1 = p2; + strcpy(&z[used], zInt); + used += lemonStrlen(&z[used]); + zText++; + n--; + }else{ + z[used++] = c; + } + } + z[used] = 0; + return z; +} + +/* +** zCode is a string that is the action associated with a rule. Expand +** the symbols in this string so that the refer to elements of the parser +** stack. +*/ +PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ + char *cp, *xp; + int i; + char lhsused = 0; /* True if the LHS element has been used */ + char used[MAXRHS]; /* True for each RHS element which is used */ + + for(i=0; inrhs; i++) used[i] = 0; + lhsused = 0; + + if( rp->code==0 ){ + static char newlinestr[2] = { '\n', '\0' }; + rp->code = newlinestr; + rp->line = rp->ruleline; + } + + append_str(0,0,0,0); + + /* This const cast is wrong but harmless, if we're careful. */ + for(cp=(char *)rp->code; *cp; cp++){ + if( isalpha(*cp) && (cp==rp->code || (!isalnum(cp[-1]) && cp[-1]!='_')) ){ + char saved; + for(xp= &cp[1]; isalnum(*xp) || *xp=='_'; xp++); + saved = *xp; + *xp = 0; + if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){ + append_str("yygotominor.yy%d",0,rp->lhs->dtnum,0); + cp = xp; + lhsused = 1; + }else{ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){ + if( cp!=rp->code && cp[-1]=='@' ){ + /* If the argument is of the form @X then substituted + ** the token number of X, not the value of X */ + append_str("yymsp[%d].major",-1,i-rp->nrhs+1,0); + }else{ + struct symbol *sp = rp->rhs[i]; + int dtnum; + if( sp->type==MULTITERMINAL ){ + dtnum = sp->subsym[0]->dtnum; + }else{ + dtnum = sp->dtnum; + } + append_str("yymsp[%d].minor.yy%d",0,i-rp->nrhs+1, dtnum); + } + cp = xp; + used[i] = 1; + break; + } + } + } + *xp = saved; + } + append_str(cp, 1, 0, 0); + } /* End loop */ + + /* Check to make sure the LHS has been used */ + if( rp->lhsalias && !lhsused ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label \"%s\" for \"%s(%s)\" is never used.", + rp->lhsalias,rp->lhs->name,rp->lhsalias); + lemp->errorcnt++; + } + + /* Generate destructor code for RHS symbols which are not used in the + ** reduce code */ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && !used[i] ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label %s for \"%s(%s)\" is never used.", + rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]); + lemp->errorcnt++; + }else if( rp->rhsalias[i]==0 ){ + if( has_destructor(rp->rhs[i],lemp) ){ + append_str(" yy_destructor(yypParser,%d,&yymsp[%d].minor);\n", 0, + rp->rhs[i]->index,i-rp->nrhs+1); + }else{ + /* No destructor defined for this term */ + } + } + } + if( rp->code ){ + cp = append_str(0,0,0,0); + rp->code = Strsafe(cp?cp:""); + } +} + +/* +** Generate code which executes when the rule "rp" is reduced. Write +** the code to "out". Make sure lineno stays up-to-date. +*/ +PRIVATE void emit_code( + FILE *out, + struct rule *rp, + struct lemon *lemp, + int *lineno +){ + const char *cp; + + /* Generate code to do the reduce action */ + if( rp->code ){ + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,rp->line,lemp->filename); } + fprintf(out,"{%s",rp->code); + for(cp=rp->code; *cp; cp++){ + if( *cp=='\n' ) (*lineno)++; + } /* End loop */ + fprintf(out,"}\n"); (*lineno)++; + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); } + } /* End if( rp->code ) */ + + return; +} + +/* +** Print the definition of the union used for the parser's data stack. +** This union contains fields for every possible data type for tokens +** and nonterminals. In the process of computing and printing this +** union, also set the ".dtnum" field of every terminal and nonterminal +** symbol. +*/ +void print_stack_union( + FILE *out, /* The output stream */ + struct lemon *lemp, /* The main info structure for this parser */ + int *plineno, /* Pointer to the line number */ + int mhflag /* True if generating makeheaders output */ +){ + int lineno = *plineno; /* The line number of the output */ + char **types; /* A hash table of datatypes */ + int arraysize; /* Size of the "types" array */ + int maxdtlength; /* Maximum length of any ".datatype" field. */ + char *stddt; /* Standardized name for a datatype */ + int i,j; /* Loop counters */ + int hash; /* For hashing the name of a type */ + const char *name; /* Name of the parser */ + + /* Allocate and initialize types[] and allocate stddt[] */ + arraysize = lemp->nsymbol * 2; + types = (char**)calloc( arraysize, sizeof(char*) ); + if( types==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + for(i=0; ivartype ){ + maxdtlength = lemonStrlen(lemp->vartype); + } + for(i=0; insymbol; i++){ + int len; + struct symbol *sp = lemp->symbols[i]; + if( sp->datatype==0 ) continue; + len = lemonStrlen(sp->datatype); + if( len>maxdtlength ) maxdtlength = len; + } + stddt = (char*)malloc( maxdtlength*2 + 1 ); + if( stddt==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + + /* Build a hash table of datatypes. The ".dtnum" field of each symbol + ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is + ** used for terminal symbols. If there is no %default_type defined then + ** 0 is also used as the .dtnum value for nonterminals which do not specify + ** a datatype using the %type directive. + */ + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + char *cp; + if( sp==lemp->errsym ){ + sp->dtnum = arraysize+1; + continue; + } + if( sp->type!=NONTERMINAL || (sp->datatype==0 && lemp->vartype==0) ){ + sp->dtnum = 0; + continue; + } + cp = sp->datatype; + if( cp==0 ) cp = lemp->vartype; + j = 0; + while( isspace(*cp) ) cp++; + while( *cp ) stddt[j++] = *cp++; + while( j>0 && isspace(stddt[j-1]) ) j--; + stddt[j] = 0; + if( lemp->tokentype && strcmp(stddt, lemp->tokentype)==0 ){ + sp->dtnum = 0; + continue; + } + hash = 0; + for(j=0; stddt[j]; j++){ + hash = hash*53 + stddt[j]; + } + hash = (hash & 0x7fffffff)%arraysize; + while( types[hash] ){ + if( strcmp(types[hash],stddt)==0 ){ + sp->dtnum = hash + 1; + break; + } + hash++; + if( hash>=arraysize ) hash = 0; + } + if( types[hash]==0 ){ + sp->dtnum = hash + 1; + types[hash] = (char*)malloc( lemonStrlen(stddt)+1 ); + if( types[hash]==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + strcpy(types[hash],stddt); + } + } + + /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */ + name = lemp->name ? lemp->name : "Parse"; + lineno = *plineno; + if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; } + fprintf(out,"#define %sTOKENTYPE %s\n",name, + lemp->tokentype?lemp->tokentype:"void*"); lineno++; + if( mhflag ){ fprintf(out,"#endif\n"); lineno++; } + fprintf(out,"typedef union {\n"); lineno++; + fprintf(out," int yyinit;\n"); lineno++; + fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++; + for(i=0; ierrsym->useCnt ){ + fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++; + } + free(stddt); + free(types); + fprintf(out,"} YYMINORTYPE;\n"); lineno++; + *plineno = lineno; +} + +/* +** Return the name of a C datatype able to represent values between +** lwr and upr, inclusive. +*/ +static const char *minimum_size_type(int lwr, int upr){ + if( lwr>=0 ){ + if( upr<=255 ){ + return "unsigned char"; + }else if( upr<65535 ){ + return "unsigned short int"; + }else{ + return "unsigned int"; + } + }else if( lwr>=-127 && upr<=127 ){ + return "signed char"; + }else if( lwr>=-32767 && upr<32767 ){ + return "short"; + }else{ + return "int"; + } +} + +/* +** Each state contains a set of token transaction and a set of +** nonterminal transactions. Each of these sets makes an instance +** of the following structure. An array of these structures is used +** to order the creation of entries in the yy_action[] table. +*/ +struct axset { + struct state *stp; /* A pointer to a state */ + int isTkn; /* True to use tokens. False for non-terminals */ + int nAction; /* Number of actions */ + int iOrder; /* Original order of action sets */ +}; + +/* +** Compare to axset structures for sorting purposes +*/ +static int axset_compare(const void *a, const void *b){ + struct axset *p1 = (struct axset*)a; + struct axset *p2 = (struct axset*)b; + int c; + c = p2->nAction - p1->nAction; + if( c==0 ){ + c = p2->iOrder - p1->iOrder; + } + assert( c!=0 || p1==p2 ); + return c; +} + +/* +** Write text on "out" that describes the rule "rp". +*/ +static void writeRuleText(FILE *out, struct rule *rp){ + int j; + fprintf(out,"%s ::=", rp->lhs->name); + for(j=0; jnrhs; j++){ + struct symbol *sp = rp->rhs[j]; + fprintf(out," %s", sp->name); + if( sp->type==MULTITERMINAL ){ + int k; + for(k=1; knsubsym; k++){ + fprintf(out,"|%s",sp->subsym[k]->name); + } + } + } +} + + +/* Generate C source code for the parser */ +void ReportTable( + struct lemon *lemp, + int mhflag /* Output in makeheaders format if true */ +){ + FILE *out, *in; + char line[LINESIZE]; + int lineno; + struct state *stp; + struct action *ap; + struct rule *rp; + struct acttab *pActtab; + int i, j, n; + const char *name; + int mnTknOfst, mxTknOfst; + int mnNtOfst, mxNtOfst; + struct axset *ax; + + in = tplt_open(lemp); + if( in==0 ) return; + out = file_open(lemp,".c","wb"); + if( out==0 ){ + fclose(in); + return; + } + lineno = 1; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the include code, if any */ + tplt_print(out,lemp,lemp->include,&lineno); + if( mhflag ){ + char *name = file_makename(lemp, ".h"); + fprintf(out,"#include \"%s\"\n", name); lineno++; + free(name); + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate #defines for all tokens */ + if( mhflag ){ + const char *prefix; + fprintf(out,"#if INTERFACE\n"); lineno++; + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + lineno++; + } + fprintf(out,"#endif\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the defines */ + fprintf(out,"#define YYCODETYPE %s\n", + minimum_size_type(0, lemp->nsymbol+1)); lineno++; + fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++; + fprintf(out,"#define YYACTIONTYPE %s\n", + minimum_size_type(0, lemp->nstate+lemp->nrule+5)); lineno++; + if( lemp->wildcard ){ + fprintf(out,"#define YYWILDCARD %d\n", + lemp->wildcard->index); lineno++; + } + print_stack_union(out,lemp,&lineno,mhflag); + fprintf(out, "#ifndef YYSTACKDEPTH\n"); lineno++; + if( lemp->stacksize ){ + fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++; + }else{ + fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++; + } + fprintf(out, "#endif\n"); lineno++; + if( mhflag ){ + fprintf(out,"#if INTERFACE\n"); lineno++; + } + name = lemp->name ? lemp->name : "Parse"; + if( lemp->arg && lemp->arg[0] ){ + int i; + i = lemonStrlen(lemp->arg); + while( i>=1 && isspace(lemp->arg[i-1]) ) i--; + while( i>=1 && (isalnum(lemp->arg[i-1]) || lemp->arg[i-1]=='_') ) i--; + fprintf(out,"#define %sARG_SDECL %s;\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_PDECL ,%s\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_FETCH %s = yypParser->%s\n", + name,lemp->arg,&lemp->arg[i]); lineno++; + fprintf(out,"#define %sARG_STORE yypParser->%s = %s\n", + name,&lemp->arg[i],&lemp->arg[i]); lineno++; + }else{ + fprintf(out,"#define %sARG_SDECL\n",name); lineno++; + fprintf(out,"#define %sARG_PDECL\n",name); lineno++; + fprintf(out,"#define %sARG_FETCH\n",name); lineno++; + fprintf(out,"#define %sARG_STORE\n",name); lineno++; + } + if( mhflag ){ + fprintf(out,"#endif\n"); lineno++; + } + fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++; + fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++; + if( lemp->errsym->useCnt ){ + fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++; + fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++; + } + if( lemp->has_fallback ){ + fprintf(out,"#define YYFALLBACK 1\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the action table and its associates: + ** + ** yy_action[] A single table containing all actions. + ** yy_lookahead[] A table containing the lookahead for each entry in + ** yy_action. Used to detect hash collisions. + ** yy_shift_ofst[] For each state, the offset into yy_action for + ** shifting terminals. + ** yy_reduce_ofst[] For each state, the offset into yy_action for + ** shifting non-terminals after a reduce. + ** yy_default[] Default action for each state. + */ + + /* Compute the actions on all states and count them up */ + ax = (struct axset *) calloc(lemp->nstate*2, sizeof(ax[0])); + if( ax==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + ax[i*2].stp = stp; + ax[i*2].isTkn = 1; + ax[i*2].nAction = stp->nTknAct; + ax[i*2+1].stp = stp; + ax[i*2+1].isTkn = 0; + ax[i*2+1].nAction = stp->nNtAct; + } + mxTknOfst = mnTknOfst = 0; + mxNtOfst = mnNtOfst = 0; + + /* Compute the action table. In order to try to keep the size of the + ** action table to a minimum, the heuristic of placing the largest action + ** sets first is used. + */ + for(i=0; instate*2; i++) ax[i].iOrder = i; + qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare); + pActtab = acttab_alloc(); + for(i=0; instate*2 && ax[i].nAction>0; i++){ + stp = ax[i].stp; + if( ax[i].isTkn ){ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->index>=lemp->nterminal ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iTknOfst = acttab_insert(pActtab); + if( stp->iTknOfstiTknOfst; + if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst; + }else{ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->indexnterminal ) continue; + if( ap->sp->index==lemp->nsymbol ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iNtOfst = acttab_insert(pActtab); + if( stp->iNtOfstiNtOfst; + if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst; + } + } + free(ax); + + /* Output the yy_action table */ + n = acttab_size(pActtab); + fprintf(out,"#define YY_ACTTAB_COUNT (%d)\n", n); lineno++; + fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++; + for(i=j=0; instate + lemp->nrule + 2; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", action); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_lookahead table */ + fprintf(out,"static const YYCODETYPE yy_lookahead[] = {\n"); lineno++; + for(i=j=0; insymbol; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", la); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_shift_ofst[] table */ + fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++; + n = lemp->nstate; + while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--; + fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n-1); lineno++; + fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); lineno++; + fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); lineno++; + fprintf(out, "static const %s yy_shift_ofst[] = {\n", + minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++; + for(i=j=0; isorted[i]; + ofst = stp->iTknOfst; + if( ofst==NO_OFFSET ) ofst = mnTknOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_reduce_ofst[] table */ + fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++; + n = lemp->nstate; + while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--; + fprintf(out, "#define YY_REDUCE_COUNT (%d)\n", n-1); lineno++; + fprintf(out, "#define YY_REDUCE_MIN (%d)\n", mnNtOfst); lineno++; + fprintf(out, "#define YY_REDUCE_MAX (%d)\n", mxNtOfst); lineno++; + fprintf(out, "static const %s yy_reduce_ofst[] = {\n", + minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++; + for(i=j=0; isorted[i]; + ofst = stp->iNtOfst; + if( ofst==NO_OFFSET ) ofst = mnNtOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the default action table */ + fprintf(out, "static const YYACTIONTYPE yy_default[] = {\n"); lineno++; + n = lemp->nstate; + for(i=j=0; isorted[i]; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", stp->iDflt); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of fallback tokens. + */ + if( lemp->has_fallback ){ + int mx = lemp->nterminal - 1; + while( mx>0 && lemp->symbols[mx]->fallback==0 ){ mx--; } + for(i=0; i<=mx; i++){ + struct symbol *p = lemp->symbols[i]; + if( p->fallback==0 ){ + fprintf(out, " 0, /* %10s => nothing */\n", p->name); + }else{ + fprintf(out, " %3d, /* %10s => %s */\n", p->fallback->index, + p->name, p->fallback->name); + } + lineno++; + } + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate a table containing the symbolic name of every symbol + */ + for(i=0; insymbol; i++){ + sprintf(line,"\"%s\",",lemp->symbols[i]->name); + fprintf(out," %-15s",line); + if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; } + } + if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate a table containing a text string that describes every + ** rule in the rule set of the grammar. This information is used + ** when tracing REDUCE actions. + */ + for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ + assert( rp->index==i ); + fprintf(out," /* %3d */ \"", i); + writeRuleText(out, rp); + fprintf(out,"\",\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes every time a symbol is popped from + ** the stack while processing errors or while destroying the parser. + ** (In other words, generate the %destructor actions) + */ + if( lemp->tokendest ){ + int once = 1; + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type!=TERMINAL ) continue; + if( once ){ + fprintf(out, " /* TERMINAL Destructor */\n"); lineno++; + once = 0; + } + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + } + for(i=0; insymbol && lemp->symbols[i]->type!=TERMINAL; i++); + if( insymbol ){ + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + } + if( lemp->vardest ){ + struct symbol *dflt_sp = 0; + int once = 1; + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || + sp->index<=0 || sp->destructor!=0 ) continue; + if( once ){ + fprintf(out, " /* Default NON-TERMINAL Destructor */\n"); lineno++; + once = 0; + } + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + dflt_sp = sp; + } + if( dflt_sp!=0 ){ + emit_destructor_code(out,dflt_sp,lemp,&lineno); + } + fprintf(out," break;\n"); lineno++; + } + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue; + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + + /* Combine duplicate destructors into a single case */ + for(j=i+1; jnsymbol; j++){ + struct symbol *sp2 = lemp->symbols[j]; + if( sp2 && sp2->type!=TERMINAL && sp2->destructor + && sp2->dtnum==sp->dtnum + && strcmp(sp->destructor,sp2->destructor)==0 ){ + fprintf(out," case %d: /* %s */\n", + sp2->index, sp2->name); lineno++; + sp2->destructor = 0; + } + } + + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes whenever the parser stack overflows */ + tplt_print(out,lemp,lemp->overflow,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of rule information + ** + ** Note: This code depends on the fact that rules are number + ** sequentually beginning with 0. + */ + for(rp=lemp->rule; rp; rp=rp->next){ + fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which execution during each REDUCE action */ + for(rp=lemp->rule; rp; rp=rp->next){ + translate_code(lemp, rp); + } + /* First output rules other than the default: rule */ + for(rp=lemp->rule; rp; rp=rp->next){ + struct rule *rp2; /* Other rules with the same action */ + if( rp->code==0 ) continue; + if( rp->code[0]=='\n' && rp->code[1]==0 ) continue; /* Will be default: */ + fprintf(out," case %d: /* ", rp->index); + writeRuleText(out, rp); + fprintf(out, " */\n"); lineno++; + for(rp2=rp->next; rp2; rp2=rp2->next){ + if( rp2->code==rp->code ){ + fprintf(out," case %d: /* ", rp2->index); + writeRuleText(out, rp2); + fprintf(out," */ yytestcase(yyruleno==%d);\n", rp2->index); lineno++; + rp2->code = 0; + } + } + emit_code(out,rp,lemp,&lineno); + fprintf(out," break;\n"); lineno++; + rp->code = 0; + } + /* Finally, output the default: rule. We choose as the default: all + ** empty actions. */ + fprintf(out," default:\n"); lineno++; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->code==0 ) continue; + assert( rp->code[0]=='\n' && rp->code[1]==0 ); + fprintf(out," /* (%d) ", rp->index); + writeRuleText(out, rp); + fprintf(out, " */ yytestcase(yyruleno==%d);\n", rp->index); lineno++; + } + fprintf(out," break;\n"); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes if a parse fails */ + tplt_print(out,lemp,lemp->failure,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when a syntax error occurs */ + tplt_print(out,lemp,lemp->error,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when the parser accepts its input */ + tplt_print(out,lemp,lemp->accept,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Append any addition code the user desires */ + tplt_print(out,lemp,lemp->extracode,&lineno); + + fclose(in); + fclose(out); + return; +} + +/* Generate a header file for the parser */ +void ReportHeader(struct lemon *lemp) +{ + FILE *out, *in; + const char *prefix; + char line[LINESIZE]; + char pattern[LINESIZE]; + int i; + + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + in = file_open(lemp,".h","rb"); + if( in ){ + for(i=1; interminal && fgets(line,LINESIZE,in); i++){ + sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + if( strcmp(line,pattern) ) break; + } + fclose(in); + if( i==lemp->nterminal ){ + /* No change in the file. Don't rewrite it. */ + return; + } + } + out = file_open(lemp,".h","wb"); + if( out ){ + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + } + fclose(out); + } + return; +} + +/* Reduce the size of the action tables, if possible, by making use +** of defaults. +** +** In this version, we take the most frequent REDUCE action and make +** it the default. Except, there is no default if the wildcard token +** is a possible look-ahead. +*/ +void CompressTables(struct lemon *lemp) +{ + struct state *stp; + struct action *ap, *ap2; + struct rule *rp, *rp2, *rbest; + int nbest, n; + int i; + int usesWildcard; + + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + nbest = 0; + rbest = 0; + usesWildcard = 0; + + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type==SHIFT && ap->sp==lemp->wildcard ){ + usesWildcard = 1; + } + if( ap->type!=REDUCE ) continue; + rp = ap->x.rp; + if( rp->lhsStart ) continue; + if( rp==rbest ) continue; + n = 1; + for(ap2=ap->next; ap2; ap2=ap2->next){ + if( ap2->type!=REDUCE ) continue; + rp2 = ap2->x.rp; + if( rp2==rbest ) continue; + if( rp2==rp ) n++; + } + if( n>nbest ){ + nbest = n; + rbest = rp; + } + } + + /* Do not make a default if the number of rules to default + ** is not at least 1 or if the wildcard token is a possible + ** lookahead. + */ + if( nbest<1 || usesWildcard ) continue; + + + /* Combine matching REDUCE actions into a single default */ + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) break; + } + assert( ap ); + ap->sp = Symbol_new("{default}"); + for(ap=ap->next; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) ap->type = NOT_USED; + } + stp->ap = Action_sort(stp->ap); + } +} + + +/* +** Compare two states for sorting purposes. The smaller state is the +** one with the most non-terminal actions. If they have the same number +** of non-terminal actions, then the smaller is the one with the most +** token actions. +*/ +static int stateResortCompare(const void *a, const void *b){ + const struct state *pA = *(const struct state**)a; + const struct state *pB = *(const struct state**)b; + int n; + + n = pB->nNtAct - pA->nNtAct; + if( n==0 ){ + n = pB->nTknAct - pA->nTknAct; + if( n==0 ){ + n = pB->statenum - pA->statenum; + } + } + assert( n!=0 ); + return n; +} + + +/* +** Renumber and resort states so that states with fewer choices +** occur at the end. Except, keep state 0 as the first state. +*/ +void ResortStates(struct lemon *lemp) +{ + int i; + struct state *stp; + struct action *ap; + + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + stp->nTknAct = stp->nNtAct = 0; + stp->iDflt = lemp->nstate + lemp->nrule; + stp->iTknOfst = NO_OFFSET; + stp->iNtOfst = NO_OFFSET; + for(ap=stp->ap; ap; ap=ap->next){ + if( compute_action(lemp,ap)>=0 ){ + if( ap->sp->indexnterminal ){ + stp->nTknAct++; + }else if( ap->sp->indexnsymbol ){ + stp->nNtAct++; + }else{ + stp->iDflt = compute_action(lemp, ap); + } + } + } + } + qsort(&lemp->sorted[1], lemp->nstate-1, sizeof(lemp->sorted[0]), + stateResortCompare); + for(i=0; instate; i++){ + lemp->sorted[i]->statenum = i; + } +} + + +/***************** From the file "set.c" ************************************/ +/* +** Set manipulation routines for the LEMON parser generator. +*/ + +static int size = 0; + +/* Set the set size */ +void SetSize(int n) +{ + size = n+1; +} + +/* Allocate a new set */ +char *SetNew(){ + char *s; + s = (char*)calloc( size, 1); + if( s==0 ){ + extern void memory_error(); + memory_error(); + } + return s; +} + +/* Deallocate a set */ +void SetFree(char *s) +{ + free(s); +} + +/* Add a new element to the set. Return TRUE if the element was added +** and FALSE if it was already there. */ +int SetAdd(char *s, int e) +{ + int rv; + assert( e>=0 && esize = 1024; + x1a->count = 0; + x1a->tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*1024 ); + if( x1a->tbl==0 ){ + free(x1a); + x1a = 0; + }else{ + int i; + x1a->ht = (x1node**)&(x1a->tbl[1024]); + for(i=0; i<1024; i++) x1a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Strsafe_insert(const char *data) +{ + x1node *np; + int h; + int ph; + + if( x1a==0 ) return 0; + ph = strhash(data); + h = ph & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x1a->count>=x1a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x1 array; + array.size = size = x1a->size*2; + array.count = x1a->count; + array.tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x1node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x1node *oldnp, *newnp; + oldnp = &(x1a->tbl[i]); + h = strhash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x1a->tbl); + *x1a = array; + } + /* Insert the new data */ + h = ph & (x1a->size-1); + np = &(x1a->tbl[x1a->count++]); + np->data = data; + if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next); + np->next = x1a->ht[h]; + x1a->ht[h] = np; + np->from = &(x1a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +const char *Strsafe_find(const char *key) +{ + int h; + x1node *np; + + if( x1a==0 ) return 0; + h = strhash(key) & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return a pointer to the (terminal or nonterminal) symbol "x". +** Create a new symbol if this is the first time "x" has been seen. +*/ +struct symbol *Symbol_new(const char *x) +{ + struct symbol *sp; + + sp = Symbol_find(x); + if( sp==0 ){ + sp = (struct symbol *)calloc(1, sizeof(struct symbol) ); + MemoryCheck(sp); + sp->name = Strsafe(x); + sp->type = isupper(*x) ? TERMINAL : NONTERMINAL; + sp->rule = 0; + sp->fallback = 0; + sp->prec = -1; + sp->assoc = UNK; + sp->firstset = 0; + sp->lambda = LEMON_FALSE; + sp->destructor = 0; + sp->destLineno = 0; + sp->datatype = 0; + sp->useCnt = 0; + Symbol_insert(sp,sp->name); + } + sp->useCnt++; + return sp; +} + +/* Compare two symbols for working purposes +** +** Symbols that begin with upper case letters (terminals or tokens) +** must sort before symbols that begin with lower case letters +** (non-terminals). Other than that, the order does not matter. +** +** We find experimentally that leaving the symbols in their original +** order (the order they appeared in the grammar file) gives the +** smallest parser tables in SQLite. +*/ +int Symbolcmpp(const void *_a, const void *_b) +{ + const struct symbol **a = (const struct symbol **) _a; + const struct symbol **b = (const struct symbol **) _b; + int i1 = (**a).index + 10000000*((**a).name[0]>'Z'); + int i2 = (**b).index + 10000000*((**b).name[0]>'Z'); + assert( i1!=i2 || strcmp((**a).name,(**b).name)==0 ); + return i1-i2; +} + +/* There is one instance of the following structure for each +** associative array of type "x2". +*/ +struct s_x2 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x2node *tbl; /* The data stored here */ + struct s_x2node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x2". +*/ +typedef struct s_x2node { + struct symbol *data; /* The data */ + const char *key; /* The key */ + struct s_x2node *next; /* Next entry with the same hash */ + struct s_x2node **from; /* Previous link */ +} x2node; + +/* There is only one instance of the array, which is the following */ +static struct s_x2 *x2a; + +/* Allocate a new associative array */ +void Symbol_init(){ + if( x2a ) return; + x2a = (struct s_x2*)malloc( sizeof(struct s_x2) ); + if( x2a ){ + x2a->size = 128; + x2a->count = 0; + x2a->tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*128 ); + if( x2a->tbl==0 ){ + free(x2a); + x2a = 0; + }else{ + int i; + x2a->ht = (x2node**)&(x2a->tbl[128]); + for(i=0; i<128; i++) x2a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Symbol_insert(struct symbol *data, const char *key) +{ + x2node *np; + int h; + int ph; + + if( x2a==0 ) return 0; + ph = strhash(key); + h = ph & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x2a->count>=x2a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x2 array; + array.size = size = x2a->size*2; + array.count = x2a->count; + array.tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x2node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x2node *oldnp, *newnp; + oldnp = &(x2a->tbl[i]); + h = strhash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x2a->tbl); + *x2a = array; + } + /* Insert the new data */ + h = ph & (x2a->size-1); + np = &(x2a->tbl[x2a->count++]); + np->key = key; + np->data = data; + if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next); + np->next = x2a->ht[h]; + x2a->ht[h] = np; + np->from = &(x2a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct symbol *Symbol_find(const char *key) +{ + int h; + x2node *np; + + if( x2a==0 ) return 0; + h = strhash(key) & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return the n-th data. Return NULL if n is out of range. */ +struct symbol *Symbol_Nth(int n) +{ + struct symbol *data; + if( x2a && n>0 && n<=x2a->count ){ + data = x2a->tbl[n-1].data; + }else{ + data = 0; + } + return data; +} + +/* Return the size of the array */ +int Symbol_count() +{ + return x2a ? x2a->count : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct symbol **Symbol_arrayof() +{ + struct symbol **array; + int i,size; + if( x2a==0 ) return 0; + size = x2a->count; + array = (struct symbol **)calloc(size, sizeof(struct symbol *)); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Compare two configurations */ +int Configcmp(const char *_a,const char *_b) +{ + const struct config *a = (struct config *) _a; + const struct config *b = (struct config *) _b; + int x; + x = a->rp->index - b->rp->index; + if( x==0 ) x = a->dot - b->dot; + return x; +} + +/* Compare two states */ +PRIVATE int statecmp(struct config *a, struct config *b) +{ + int rc; + for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){ + rc = a->rp->index - b->rp->index; + if( rc==0 ) rc = a->dot - b->dot; + } + if( rc==0 ){ + if( a ) rc = 1; + if( b ) rc = -1; + } + return rc; +} + +/* Hash a state */ +PRIVATE int statehash(struct config *a) +{ + int h=0; + while( a ){ + h = h*571 + a->rp->index*37 + a->dot; + a = a->bp; + } + return h; +} + +/* Allocate a new state structure */ +struct state *State_new() +{ + struct state *newstate; + newstate = (struct state *)calloc(1, sizeof(struct state) ); + MemoryCheck(newstate); + return newstate; +} + +/* There is one instance of the following structure for each +** associative array of type "x3". +*/ +struct s_x3 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x3node *tbl; /* The data stored here */ + struct s_x3node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x3". +*/ +typedef struct s_x3node { + struct state *data; /* The data */ + struct config *key; /* The key */ + struct s_x3node *next; /* Next entry with the same hash */ + struct s_x3node **from; /* Previous link */ +} x3node; + +/* There is only one instance of the array, which is the following */ +static struct s_x3 *x3a; + +/* Allocate a new associative array */ +void State_init(){ + if( x3a ) return; + x3a = (struct s_x3*)malloc( sizeof(struct s_x3) ); + if( x3a ){ + x3a->size = 128; + x3a->count = 0; + x3a->tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*128 ); + if( x3a->tbl==0 ){ + free(x3a); + x3a = 0; + }else{ + int i; + x3a->ht = (x3node**)&(x3a->tbl[128]); + for(i=0; i<128; i++) x3a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int State_insert(struct state *data, struct config *key) +{ + x3node *np; + int h; + int ph; + + if( x3a==0 ) return 0; + ph = statehash(key); + h = ph & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x3a->count>=x3a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x3 array; + array.size = size = x3a->size*2; + array.count = x3a->count; + array.tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x3node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x3node *oldnp, *newnp; + oldnp = &(x3a->tbl[i]); + h = statehash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x3a->tbl); + *x3a = array; + } + /* Insert the new data */ + h = ph & (x3a->size-1); + np = &(x3a->tbl[x3a->count++]); + np->key = key; + np->data = data; + if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next); + np->next = x3a->ht[h]; + x3a->ht[h] = np; + np->from = &(x3a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct state *State_find(struct config *key) +{ + int h; + x3node *np; + + if( x3a==0 ) return 0; + h = statehash(key) & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct state **State_arrayof() +{ + struct state **array; + int i,size; + if( x3a==0 ) return 0; + size = x3a->count; + array = (struct state **)malloc( sizeof(struct state *)*size ); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Hash a configuration */ +PRIVATE int confighash(struct config *a) +{ + int h=0; + h = h*571 + a->rp->index*37 + a->dot; + return h; +} + +/* There is one instance of the following structure for each +** associative array of type "x4". +*/ +struct s_x4 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x4node *tbl; /* The data stored here */ + struct s_x4node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x4". +*/ +typedef struct s_x4node { + struct config *data; /* The data */ + struct s_x4node *next; /* Next entry with the same hash */ + struct s_x4node **from; /* Previous link */ +} x4node; + +/* There is only one instance of the array, which is the following */ +static struct s_x4 *x4a; + +/* Allocate a new associative array */ +void Configtable_init(){ + if( x4a ) return; + x4a = (struct s_x4*)malloc( sizeof(struct s_x4) ); + if( x4a ){ + x4a->size = 64; + x4a->count = 0; + x4a->tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*64 ); + if( x4a->tbl==0 ){ + free(x4a); + x4a = 0; + }else{ + int i; + x4a->ht = (x4node**)&(x4a->tbl[64]); + for(i=0; i<64; i++) x4a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Configtable_insert(struct config *data) +{ + x4node *np; + int h; + int ph; + + if( x4a==0 ) return 0; + ph = confighash(data); + h = ph & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp((const char *) np->data,(const char *) data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x4a->count>=x4a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x4 array; + array.size = size = x4a->size*2; + array.count = x4a->count; + array.tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x4node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x4node *oldnp, *newnp; + oldnp = &(x4a->tbl[i]); + h = confighash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x4a->tbl); + *x4a = array; + } + /* Insert the new data */ + h = ph & (x4a->size-1); + np = &(x4a->tbl[x4a->count++]); + np->data = data; + if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next); + np->next = x4a->ht[h]; + x4a->ht[h] = np; + np->from = &(x4a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct config *Configtable_find(struct config *key) +{ + int h; + x4node *np; + + if( x4a==0 ) return 0; + h = confighash(key) & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp((const char *) np->data,(const char *) key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Remove all data from the table. Pass each data to the function "f" +** as it is removed. ("f" may be null to avoid this step.) */ +void Configtable_clear(int(*f)(struct config *)) +{ + int i; + if( x4a==0 || x4a->count==0 ) return; + if( f ) for(i=0; icount; i++) (*f)(x4a->tbl[i].data); + for(i=0; isize; i++) x4a->ht[i] = 0; + x4a->count = 0; + return; +} diff --git a/sql/parser/lempar-tmpl.c b/sql/parser/lempar-tmpl.c new file mode 100644 index 0000000..3bb7b0f --- /dev/null +++ b/sql/parser/lempar-tmpl.c @@ -0,0 +1,851 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is included that follows the "include" declaration +** in the input grammar file. */ +#include +%% +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +%% +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ParseTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +%% +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* The yyzerominor constant is used to initialize instances of +** YYMINORTYPE objects to zero. */ +static const YYMINORTYPE yyzerominor = { 0 }; + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +%% + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +%% +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyidxMax; /* Maximum value of yyidx */ +#endif + int yyerrcnt; /* Shifts left before out of the error */ + ParseARG_SDECL /* A place to hold %extra_argument */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +#endif +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { +%% +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { +%% +}; +#endif /* NDEBUG */ + + +#if YYSTACKDEPTH<=0 +/* +** Try to increase the size of the parser stack. +*/ +static void yyGrowStack(yyParser *p){ + int newSize; + yyStackEntry *pNew; + + newSize = p->yystksz*2 + 100; + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + if( pNew ){ + p->yystack = pNew; + p->yystksz = newSize; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", + yyTracePrompt, p->yystksz); + } +#endif + } +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; +#ifdef YYTRACKMAXSTACKDEPTH + pParser->yyidxMax = 0; +#endif +#if YYSTACKDEPTH<=0 + pParser->yystack = NULL; + pParser->yystksz = 0; + yyGrowStack(pParser); +#endif + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH; + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ +%% + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; + + if( pParser->yyidx<0 ) return 0; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor(pParser, yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +**
    +**
  • A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +**
  • A pointer to a function used to reclaim memory obtained +** from malloc. +**
+*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==0 ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + free(pParser->yystack); +#endif + (*freeProc)((void*)pParser); +} + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyidxMax; +} +#endif + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + if( stateno>YY_SHIFT_COUNT + || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ + if( iLookAhead>0 ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + if( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + if( +#if YY_SHIFT_MIN+YYWILDCARD<0 + j>=0 && +#endif +#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT + j %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + } + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + int stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_COUNT ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_COUNT ); +#endif + i = yy_reduce_ofst[stateno]; + assert( i!=YY_REDUCE_USE_DFLT ); + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; +#ifdef YYTRACKMAXSTACKDEPTH + if( yypParser->yyidx>yypParser->yyidxMax ){ + yypParser->yyidxMax = yypParser->yyidx; + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yyidx>=YYSTACKDEPTH ){ + yyStackOverflow(yypParser, yypMinor); + return; + } +#else + if( yypParser->yyidx>=yypParser->yystksz ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz ){ + yyStackOverflow(yypParser, yypMinor); + return; + } + } +#endif + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = (YYACTIONTYPE)yyNewState; + yytos->major = (YYCODETYPE)yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static const struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { +%% +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && yyruleno>=0 + && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } +#endif /* NDEBUG */ + + /* Silence complaints from purify about yygotominor being uninitialized + ** in some cases when it is copied into the stack after the following + ** switch. yygotominor is uninitialized when a rule reduces that does + ** not set the value of its left-hand side nonterminal. Leaving the + ** value of the nonterminal uninitialized is utterly harmless as long + ** as the value is never used. So really the only thing this code + ** accomplishes is to quieten purify. + ** + ** 2007-01-16: The wireshark project (www.wireshark.org) reports that + ** without this code, their parser segfaults. I'm not sure what there + ** parser is doing to make this happen. This is the second bug report + ** from wireshark this week. Clearly they are stressing Lemon in ways + ** that it has not been previously stressed... (SQLite ticket #2172) + */ + /*memset(&yygotominor, 0, sizeof(yygotominor));*/ + yygotominor = yyzerominor; + + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ +%% + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); + if( yyact < YYNSTATE ){ +#ifdef NDEBUG + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if( yysize ){ + yypParser->yyidx++; + yymsp -= yysize-1; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yymsp->minor = yygotominor; + }else +#endif + { + yy_shift(yypParser,yyact,yygoto,&yygotominor); + } + }else{ + assert( yyact == YYNSTATE + YYNRULE + 1 ); + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH; +#define TOKEN (yyminor.yy0) +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ +#if YYSTACKDEPTH<=0 + if( yypParser->yystksz <=0 ){ + /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ + yyminorunion = yyzerominor; + yyStackOverflow(yypParser, &yyminorunion); + return; + } +#endif + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ParseARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); + if( yyactyyerrcnt--; + yymajor = YYNOCODE; + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else{ + assert( yyact == YY_ERROR_ACTION ); +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_reduce_action( + yypParser->yystack[yypParser->yyidx].stateno, + YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/sql/parser/scan.rl b/sql/parser/scan.rl new file mode 100644 index 0000000..8b96bc1 --- /dev/null +++ b/sql/parser/scan.rl @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +/* THIS FILE HAS BEEN GENERATED USING THE SOURCE IN parser/scan.rl */ + +#include +#include "sql-parser.h" +#include "parser/gram.c" + +#define PARSE(token, value) \ + state->current = ts; \ + Parse(parser, SQL_PARSER_##token, value, state) + +/** + * SECTION: sql-parser + * @Short_description: a simple DML query parser + * @Title: SqlParser + * + * This is the parser used by Hedera to transform a string containing one or + * more SQL data management queries into an #SqlObject and thus have an idea + * of what must happen when a change is made in the affected data. + **/ +static gchar * get_token (const gchar * ts, const gchar * te) +{ + if (!(ts && te)) return NULL; + gsize len = (gsize) (te-ts); + gchar * str = g_malloc (len+1); + g_memmove (str, ts, len); + str[len] = '\0'; + return str; +} + +%%{ + machine sql_scanner; + +#/*-----------------------# SYMBOL Definition #-----------------------*/# +# Keywords + AS = "AS"i; + UNION = "UNION"i; + ALL = "ALL"i; + ANY = "ANY"i; + EXCEPT = "EXCEPT"i; + INTERSECT = "INTERSECT"i; + SELECT = "SELECT"i; + FROM = "FROM"i; + WHERE = "WHERE"i; + DISTINCT = "DISTINCT"i; + JOIN = "JOIN"i; + INNER = "INNER"i; + LEFT = ("OUTER"i space+|"") "LEFT"i; + RIGHT = ("OUTER"i space+|"") "RIGHT"i; + ON = "ON"i; + USING = "USING"i; + NATURAL = "NATURAL"i; + GROUP = "GROUP" space+ "BY"i; + HAVING = "HAVING"i; + ORDER = "ORDER"i space+ "BY"i; + ASC = "ASC"i; + DESC = "DESC"i; + LIMIT = "LIMIT"i; + OFFSET = "OFFSET"i; + UPDATE = "UPDATE"i; + SET = "SET"i; + INSERT = "INSERT"i space+ "INTO"i; + DEFAULT = "DEFAULT"i; + VALUES = "VALUES"i; + DELETE = "DELETE"i space+ "FROM"i; + RETURNING = "RETURNING"i; +# Parenthesis + LP = "("; + RP = ")"; +# Colon + DOT = "."; +# Semicolon + SC = ";"; +# Comma + CM = ","; +# Identifier quote + IQ = ("\"" | "`"); +# Operators + PLUS = "+"; + MINUS = "-"; + STAR = "*"; + DIV = "/"; + MOD = "%"; + EQ = "="; + NE = ("<>" | "!="); + GT = ">"; + GE = ">="; + LT = "<"; + LE = "<="; +# Operator Keywords + AND = "AND"i; + OR = "OR"i; + NOT = ("NOT"i | "!"); + LIKE = "LIKE"i; + IS = "IS"i; + XOR = "XOR"i; +# Boolean Keywords + BOOLEAN = ("TRUE"i | "FALSE"i); + + Op = (AND | OR | NOT | LIKE | IS | XOR); + keyword = ( Op | BOOLEAN | AS | UNION | ALL | ANY | EXCEPT | INTERSECT | + SELECT | FROM | WHERE | DISTINCT | JOIN | INNER | LEFT| RIGHT | ON | + USING | NATURAL | GROUP | HAVING | ORDER | ASC | DESC | LIMIT | OFFSET | + UPDATE | SET | INSERT | DEFAULT | VALUES | RETURNING | DELETE); + +# Identifiers + Iescape = ("\"\"" | "" ); + IDENTIFIER = ((alpha | "_" | "$") (alnum | "_" | "$")*) - keyword; + QIDENTIFIER = IQ ((any - IQ)* Iescape (any - IQ)*) IQ; + PLACEHOLDER = "#" IDENTIFIER; + +# Escaped string characters + escape = ("\\'"| "''" | ""); + +# Data values + STRING = "'" (any - "'")* escape (any - "'")* "'"; + INTEGER = digit+; + FLOAT = digit+ ("." digit+)?; + +# Comments + comment = ("-- " (any - "\n")* "\n" | + "/*" (any* -- "*/") "*/"); + +#/*-----------------------# SCANNER Definition #-----------------------*/# + main := + |* +#// Keywords + AS => { PARSE (AS, 0); }; + UNION => { PARSE (UNION, 0); }; + ALL => { PARSE (ALL, 0); }; + ANY => { PARSE (ANY, 0); }; + EXCEPT => { PARSE (EXCEPT, 0); }; + INTERSECT => { PARSE (INTERSECT, 0); }; + SELECT => { PARSE (SELECT, 0); }; + DISTINCT => { PARSE (DISTINCT, 0); }; + FROM => { PARSE (FROM, 0); }; + WHERE => { PARSE (WHERE, 0); }; + JOIN => { PARSE (JOIN, 0); }; + INNER => { PARSE (INNER, 0); }; + LEFT => { PARSE (LEFT, 0); }; + RIGHT => { PARSE (RIGHT, 0); }; + ON => { PARSE (ON, 0); }; + USING => { PARSE (USING, 0); }; +#// NATURAL => { PARSE (NATURAL, 0); }; + GROUP => { PARSE (GROUP, 0); }; + HAVING => { PARSE (HAVING, 0); }; + ORDER => { PARSE (ORDER, 0); }; + ASC => { PARSE (ASC, 0); }; + DESC => { PARSE (DESC, 0); }; + LIMIT => { PARSE (LIMIT, 0); }; + OFFSET => { PARSE (OFFSET, 0); }; + UPDATE => { PARSE (UPDATE, 0); }; + SET => { PARSE (SET, 0); }; + INSERT => { PARSE (INSERT, 0); }; + DEFAULT => { PARSE (DEFAULT, 0); }; + VALUES => { PARSE (VALUES, 0); }; + DELETE => { PARSE (DELETE, 0); }; + +#// Punctuation symbols + LP => { PARSE (LP, 0); }; + RP => { PARSE (RP, 0); }; + DOT => { PARSE (DOT, 0); }; + SC => { PARSE (SC, 0); }; + CM => { PARSE (CM, 0); }; + +#// Identifiers + IDENTIFIER => { PARSE (IDENTIFIER, get_token (ts, te)); }; + QIDENTIFIER => { PARSE (IDENTIFIER, get_token (ts+1, te-1)); }; + PLACEHOLDER => { PARSE (PLACEHOLDER, get_token (ts+1, te)); }; + +#// Data values + STRING => { PARSE (STRING, get_token (ts+1, te-1)); }; + INTEGER => { PARSE (INTEGER, get_token (ts, te)); }; + FLOAT => { PARSE (FLOAT, get_token (ts, te)); }; + BOOLEAN => { PARSE (BOOLEAN, get_token(ts, te)); }; + +#// Operators + PLUS => { PARSE (PLUS, 0); }; + MINUS => { PARSE (MINUS, 0); }; + STAR => { PARSE (STAR, 0); }; + DIV => { PARSE (DIV, 0); }; + MOD => { PARSE (MOD, 0); }; + EQ => { PARSE (EQ, 0); }; + NE => { PARSE (NE, 0); }; + GT => { PARSE (GT, 0); }; + GE => { PARSE (GE, 0); }; + LT => { PARSE (LT, 0); }; + LE => { PARSE (LE, 0); }; + +#// Operator Keywords + AND => { PARSE (AND, 0); }; + OR => { PARSE (OR, 0); }; + NOT => { PARSE (NOT, 0); }; + LIKE => { PARSE (LIKE, 0); }; + IS => { PARSE (IS, 0); }; + XOR => { PARSE (XOR, 0); }; + +#// Ignored + space; + comment; + *|; +}%% +%% write data; + +SqlObject * sql_parser_parse (gchar * sql) +{ + gint cs, act; + gchar * p, * pe, * ts, * te; + gpointer eof, parser; + ParseState * state; + SqlObject * object; + + if (!sql) + { + g_log (g_quark_to_string (SQL_PARSER_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING ,"Empty query!\n"); + return NULL; + } + + state = g_new (ParseState, 1); + state->object = NULL; + state->error = FALSE; + state->string = state->current = p = sql; + pe = p + strlen (p) + 1; + eof = pe; + + parser = ParseAlloc (g_malloc); + + %% write init; + %% write exec; + + Parse (parser, 0, 0, state); + ParseFree (parser, g_free); + + if (state->error) + { + if (state->object && G_IS_OBJECT (state->object)) + g_object_unref (g_object_ref_sink (state->object)); + object = NULL; + } + else + object = g_object_ref_sink (state->object); + + g_free (state); + + return object; +} diff --git a/sql/sql-delete.c b/sql/sql-delete.c new file mode 100644 index 0000000..63dc205 --- /dev/null +++ b/sql/sql-delete.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-delete.h" + +/** + * SECTION: sql-delete + * @Short_description: the equivalent of the DELETE statement in SQL. + * @Title: SqlDelete + * + * The #SqlDelete represents a deletion statement. + **/ +G_DEFINE_TYPE (SqlDelete, sql_delete, SQL_TYPE_DML); + +SqlObject * sql_delete_new () +{ + return g_object_new (SQL_TYPE_DELETE, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_delete_render (SqlDelete * obj, SqlRender * render) +{ + sql_render_add_token (render, "DELETE"); + sql_render_add_list (render, TRUE, NULL, obj->tables, ","); + + if (SQL_DML (obj)->targets) + { + sql_render_add_list (render, TRUE, "FROM", SQL_DML (obj)->targets, ","); + sql_render_add_item (render, FALSE, "WHERE", SQL_DML (obj)->where); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_TABLES = 1 +}; + +static void sql_delete_set_property (SqlDelete * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TABLES: + sql_object_remove (obj, obj->tables); + obj->tables = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_delete_get_property (SqlDelete * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TABLES: + g_value_set_object (value, obj->tables); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_delete_init (SqlDelete * obj) +{ + obj->tables = NULL; +} + +static void sql_delete_finalize (SqlDelete * obj) +{ + sql_object_remove (obj, obj->tables); + G_OBJECT_CLASS (sql_delete_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_delete_class_init (SqlDeleteClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_delete_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_delete_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_delete_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_delete_render; + + g_object_class_install_property (k, PROP_TABLES, + sql_param_list ("tables" + ,"Tables" + ,"A list of tables" + ,SQL_TYPE_TABLE + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); +} diff --git a/sql/sql-delete.h b/sql/sql-delete.h new file mode 100644 index 0000000..fcb0783 --- /dev/null +++ b/sql/sql-delete.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_DELETE_H +#define SQL_DELETE_H + +#include "sql-dml.h" +#include "sql-table.h" + +#define SQL_TYPE_DELETE (sql_delete_get_type ()) +#define SQL_DELETE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_DELETE, SqlDelete)) +#define SQL_IS_DELETE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_DELETE)) + +typedef struct _SqlDelete SqlDelete; +typedef struct _SqlDeleteClass SqlDeleteClass; + +/** + * SqlDelete: + * @table: (element-type Sql.Table): list of targeted tables + **/ +struct _SqlDelete +{ + SqlDml parent; + SqlList * tables; +}; + +struct _SqlDeleteClass +{ + /* */ + SqlDmlClass parent; +}; + +GType sql_delete_get_type (); +SqlObject * sql_delete_new (); + +#endif diff --git a/sql/sql-dml.c b/sql/sql-dml.c new file mode 100644 index 0000000..7d61719 --- /dev/null +++ b/sql/sql-dml.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-dml.h" + +/** + * SECTION: sql-dml + * @Short_description: parent class of data manipulation statements. + * @Title: SqlDml + * + * The #SqlDml is the base class for all SQL statements that manipulate data. + **/ +G_DEFINE_ABSTRACT_TYPE (SqlDml, sql_dml, SQL_TYPE_STMT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_dml_set_where (SqlDml * obj, SqlExpr * where) +{ + g_return_if_fail (SQL_IS_DML (obj)); + g_return_if_fail (SQL_IS_EXPR (where) || SQL_IS_HOLDER (where) || !where); + + sql_object_remove (obj, obj->where); + obj->where = sql_object_add (obj, where); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_TARGETS = 1 + ,PROP_WHERE +}; + +static void sql_dml_set_property (SqlDml * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TARGETS: + sql_object_remove (obj, obj->targets); + obj->targets = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_WHERE: + sql_dml_set_where (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_dml_get_property (SqlDml * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TARGETS: + g_value_set_object (value, obj->targets); + break; + case PROP_WHERE: + g_value_set_object (value, obj->where); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_dml_init (SqlDml * obj) +{ + obj->targets = NULL; + obj->where = NULL; +} + +static void sql_dml_finalize (SqlDml * obj) +{ + sql_object_remove (obj, obj->targets); + sql_object_remove (obj, obj->where); + G_OBJECT_CLASS (sql_dml_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_dml_class_init (SqlDmlClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_dml_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_dml_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_dml_get_property; + + g_object_class_install_property (k, PROP_TARGETS, + sql_param_list ("targets" + ,"Targets" + ,"A list of targets" + ,SQL_TYPE_TARGET + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); + g_object_class_install_property (k, PROP_WHERE, + sql_param_object ("where" + ,"Where" + ,"The WHERE section of an statement" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-dml.h b/sql/sql-dml.h new file mode 100644 index 0000000..d7c528f --- /dev/null +++ b/sql/sql-dml.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_DML_H +#define SQL_DML_H + +#include "sql-stmt.h" +#include "sql-expr.h" +#include "sql-target.h" + +#define SQL_TYPE_DML (sql_dml_get_type ()) +#define SQL_DML(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_DML, SqlDml)) +#define SQL_IS_DML(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_DML)) + +typedef struct _SqlDml SqlDml; +typedef struct _SqlDmlClass SqlDmlClass; + +/** + * SqlDml: + * @target: (element-type Sql.Target): list of targets for the DML query + * @where: an #SqlExpr + **/ +struct _SqlDml +{ + SqlStmt parent; + SqlList * targets; + SqlExpr * where; +}; + +struct _SqlDmlClass +{ + /* */ + SqlStmtClass parent; +}; + +GType sql_dml_get_type (); +void sql_dml_set_where (SqlDml * obj, SqlExpr * expr); + +#endif diff --git a/sql/sql-expr.c b/sql/sql-expr.c new file mode 100644 index 0000000..00b9993 --- /dev/null +++ b/sql/sql-expr.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-expr.h" + +/** + * SECTION: sql-expr + * @Short_description: represents any expression used in SQL. + * @Title: SqlExpr + * + * The #SqlExpr is the base class for all SQL expressions such as an + * arithmetic or logical operation, a comparison between values, columns, any + * type of value... + **/ +G_DEFINE_ABSTRACT_TYPE (SqlExpr, sql_expr, SQL_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_expr_init (SqlExpr * obj) {} + +static void sql_expr_class_init (SqlExprClass * klass) {} diff --git a/sql/sql-expr.h b/sql/sql-expr.h new file mode 100644 index 0000000..0a4400d --- /dev/null +++ b/sql/sql-expr.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_EXPR_H +#define SQL_EXPR_H + +#include "sql-object.h" + +#define SQL_TYPE_EXPR (sql_expr_get_type ()) +#define SQL_EXPR(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_EXPR, SqlExpr)) +#define SQL_IS_EXPR(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_EXPR)) + +typedef struct _SqlExpr SqlExpr; +typedef struct _SqlExprClass SqlExprClass; + +struct _SqlExpr +{ + SqlObject parent; +}; + +struct _SqlExprClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_expr_get_type (); + +#endif diff --git a/sql/sql-field.c b/sql/sql-field.c new file mode 100644 index 0000000..8511016 --- /dev/null +++ b/sql/sql-field.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-field.h" + +/** + * SECTION: sql-field + * @Short_description: a column of an SQL target. + * @Title: SqlField + * + * The #SqlField represents the column of an SQL target. + **/ +G_DEFINE_TYPE (SqlField, sql_field, SQL_TYPE_EXPR); + +/** + * sql_field_new: + * @name: the name of the field + * @target: (allow-none): the table from which the field is selected + * @schema: (allow-none): the schema from which the table and field are selected + * + * Creates a new #SqlField. + * + * Return value: an #SqlExpr + */ +SqlObject * sql_field_new (const gchar * name, const gchar * target, const gchar * schema) +{ + return g_object_new (SQL_TYPE_FIELD + ,"name", name + ,"target", target + ,"schema", schema + ,NULL + ); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_field_render (SqlField * obj, SqlRender * render) +{ + if (obj->target) + { + if (obj->schema) + { + sql_render_add_identifier (render, obj->schema); + sql_render_append (render, "."); + } + + sql_render_add_identifier (render, obj->target); + sql_render_append (render, "."); + } + + if (!g_strcmp0 (obj->name, "*")) + { + sql_render_add_espace (render); + sql_render_append (render, "*"); + } + else + sql_render_add_identifier (render, obj->name); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_field_set_name (SqlField * obj, const gchar * name) +{ + g_return_if_fail (SQL_IS_FIELD (obj)); + g_return_if_fail (name); + + g_free (obj->name); + obj->name = g_strdup (name); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_NAME = 1 + ,PROP_TARGET + ,PROP_SCHEMA +}; + +static void sql_field_set_property (SqlField * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + sql_field_set_name (obj, g_value_get_string (value)); + break; + case PROP_TARGET: + g_free (obj->target); + obj->target = g_value_dup_string (value); + break; + case PROP_SCHEMA: + g_free (obj->schema); + obj->schema = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_field_get_property (SqlField * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_value_set_string (value, obj->name); + break; + case PROP_TARGET: + g_value_set_string (value, obj->target); + break; + case PROP_SCHEMA: + g_value_set_string (value, obj->schema); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_field_init (SqlField * obj) +{ + obj->name = NULL; + obj->target = NULL; + obj->schema = NULL; +} + +static void sql_field_finalize (SqlField * obj) +{ + g_free (obj->name); + g_free (obj->target); + g_free (obj->schema); + G_OBJECT_CLASS (sql_field_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_field_class_init (SqlFieldClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_field_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_field_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_field_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_field_render; + + g_object_class_install_property (k, PROP_NAME, + g_param_spec_string ("name" + ,"Name" + ,"The column name" + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_TARGET, + g_param_spec_string ("target" + ,"Target" + ,"The target name" + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SCHEMA, + g_param_spec_string ("schema" + ,"Schema" + ,"The schema name" + ,NULL + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-field.h b/sql/sql-field.h new file mode 100644 index 0000000..c33efe8 --- /dev/null +++ b/sql/sql-field.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_FIELD_H +#define SQL_FIELD_H + +#include "sql-expr.h" +#include "sql-target.h" + +#define SQL_TYPE_FIELD (sql_field_get_type ()) +#define SQL_FIELD(object) (G_TYPE_CHECK_INSTANCE_CAST (object, SQL_TYPE_FIELD, SqlField)) +#define SQL_IS_FIELD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SQL_TYPE_FIELD)) + +typedef struct _SqlField SqlField; +typedef struct _SqlFieldClass SqlFieldClass; + +struct _SqlField +{ + SqlExpr parent; + gchar * name; + gchar * target; + gchar * schema; +}; + +struct _SqlFieldClass +{ + /* */ + SqlExprClass parent; +}; + +GType sql_field_get_type (); +SqlObject * sql_field_new (const gchar * name + ,const gchar * target + ,const gchar * schema); +void sql_field_set_name (SqlField * obj, const gchar * name); + +#endif diff --git a/sql/sql-function.c b/sql/sql-function.c new file mode 100644 index 0000000..d5c7249 --- /dev/null +++ b/sql/sql-function.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-function.h" + +/** + * SECTION: sql-function + * @Short_description: the equivalent of an SQL function. + * @Title: SqlFunction + * + * The #SqlFunction represents a function and its parameters. + **/ +G_DEFINE_TYPE (SqlFunction, sql_function, SQL_TYPE_EXPR); + +SqlFunction * sql_function_new (const gchar * name, const gchar * schema) +{ + return g_object_new (SQL_TYPE_FUNCTION, "name", name, "schema", schema, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_function_render (SqlFunction * obj, SqlRender * render) +{ + if (obj->schema) + { + sql_render_add_identifier (render, obj->schema); + sql_render_append (render, "."); + } + + sql_render_add_identifier (render, obj->name); + + if (obj->name) + { + sql_render_append (render, "("); + sql_render_add_list (render, FALSE, NULL, obj->params, ","); + sql_render_append (render, ")"); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_NAME = 1 + ,PROP_SCHEMA + ,PROP_PARAMS +}; + +static void sql_function_set_property (SqlFunction * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_free (obj->name); + obj->name = g_value_dup_string (value); + break; + case PROP_SCHEMA: + g_free (obj->schema); + obj->schema = g_value_dup_string (value); + break; + case PROP_PARAMS: + sql_object_remove (obj, obj->params); + obj->params = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_function_get_property (SqlFunction * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_value_set_string (value, obj->name); + break; + case PROP_SCHEMA: + g_value_set_string (value, obj->schema); + break; + case PROP_PARAMS: + g_value_set_object (value, obj->params); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_function_init (SqlFunction * obj) +{ + obj->name = NULL; + obj->schema = NULL; + obj->params = NULL; +} + +static void sql_function_finalize (SqlFunction * obj) +{ + g_free (obj->name); + g_free (obj->schema); + sql_object_remove (obj, obj->params); + G_OBJECT_CLASS (sql_function_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_function_class_init (SqlFunctionClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_function_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_function_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_function_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_function_render; + + g_object_class_install_property (k, PROP_NAME, + g_param_spec_string ("name" + ,"Name" + ,"The function name" + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SCHEMA, + g_param_spec_string ("schema" + ,"Schema" + ,"The schema to which the function belongs" + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_PARAMS, + sql_param_list ("params" + ,"Parameters" + ,"The function parameters" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); +} diff --git a/sql/sql-function.h b/sql/sql-function.h new file mode 100644 index 0000000..ac4190c --- /dev/null +++ b/sql/sql-function.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_FUNCTION_H +#define SQL_FUNCTION_H + +#include "sql-expr.h" + +#define SQL_TYPE_FUNCTION (sql_function_get_type ()) +#define SQL_FUNCTION(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_FUNCTION, SqlFunction)) +#define SQL_IS_FUNCTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_FUNCTION)) + +typedef struct _SqlFunction SqlFunction; +typedef struct _SqlFunctionClass SqlFunctionClass; + +/** + * SqlFunction: + * @name: name of the called function + * @param: (element-type Sql.Expr): list of #SqlExpr parameters + **/ +struct _SqlFunction +{ + SqlExpr parent; + gchar * schema; + gchar * name; + SqlList * params; // List of SqlExpr +}; + +struct _SqlFunctionClass +{ + /* */ + SqlExprClass parent; +}; + +GType sql_function_get_type (); +SqlFunction * sql_function_new (const gchar * name, const gchar * schema); + +#endif diff --git a/sql/sql-holder.c b/sql/sql-holder.c new file mode 100644 index 0000000..b3c3fe2 --- /dev/null +++ b/sql/sql-holder.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-holder.h" + +/** + * SECTION: sql-holder + * @Short_description: + * @Title: SqlHolder + **/ +G_DEFINE_TYPE (SqlHolder, sql_holder, SQL_TYPE_OBJECT); + +SqlHolder * sql_holder_new (const gchar * id) +{ + return g_object_new (SQL_TYPE_HOLDER, "id", id, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_holder_render (SqlHolder * obj, SqlRender * render) +{ + SqlObject * held_object = NULL; + GSList * i = sql_render_get_ancestors (render); + + for (; i && !held_object; i = i->next) + held_object = sql_object_get_held_object (held_object, obj->id); + + if (held_object) + sql_render_add_object (render, held_object); + else + sql_render_printf (render, "#%s", obj->id); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * sql_holder_get_id: + * @obj: the #SqlHolder + * + * Gets the identifier assigned to the holder. + * + * Return value: (transfer none): the id + **/ +const gchar * sql_holder_get_id (SqlHolder * obj) +{ + g_return_val_if_fail (SQL_IS_HOLDER (obj), NULL); + + return obj->id; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_ID = 1 +}; + +static void sql_holder_set_property (SqlHolder * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ID: + g_free (obj->id); + obj->id = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_holder_get_property (SqlHolder * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ID: + g_value_set_string (value, sql_holder_get_id (obj)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_holder_init (SqlHolder * obj) +{ + obj->id = NULL; +} + +static void sql_holder_finalize (SqlHolder * obj) +{ + g_free (obj->id); + G_OBJECT_CLASS (sql_holder_finalize)->finalize (G_OBJECT (obj)); +} + +static void sql_holder_class_init (SqlHolderClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) sql_holder_finalize; + klass->set_property = (GObjectSetPropertyFunc) sql_holder_set_property; + klass->get_property = (GObjectGetPropertyFunc) sql_holder_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_holder_render; + + g_object_class_install_property (klass, PROP_ID, + g_param_spec_string ("id" + ,_("Identifier") + ,_("The holder identifier") + ,NULL + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/sql/sql-holder.h b/sql/sql-holder.h new file mode 100644 index 0000000..ab18735 --- /dev/null +++ b/sql/sql-holder.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_HOLDER_H +#define SQL_HOLDER_H + +#include "sql-object.h" + +#define SQL_TYPE_HOLDER (sql_holder_get_type ()) +#define SQL_HOLDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_HOLDER, SqlHolder)) +#define SQL_IS_HOLDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_HOLDER)) + +typedef struct _SqlHolder SqlHolder; +typedef struct _SqlHolderClass SqlHolderClass; + +struct _SqlHolder +{ + SqlObject parent; + gchar * id; +}; + +struct _SqlHolderClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_holder_get_type (); +SqlHolder * sql_holder_new (const gchar * id); +const gchar * sql_holder_get_id (SqlHolder * obj); + +#endif \ No newline at end of file diff --git a/sql/sql-insert.c b/sql/sql-insert.c new file mode 100644 index 0000000..6270dd0 --- /dev/null +++ b/sql/sql-insert.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-insert.h" + +/** + * SECTION: sql-insert + * @Short_description: the equivalent of the INSERT statement in SQL. + * @Title: SqlInsert + * + * The #SqlInsert represents a insertion statement. + **/ +G_DEFINE_TYPE (SqlInsert, sql_insert, SQL_TYPE_STMT); + +SqlObject * sql_insert_new () +{ + return g_object_new (SQL_TYPE_INSERT, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_insert_render (SqlInsert * obj, SqlRender * render) +{ + sql_render_add_item (render, TRUE, "INSERT INTO", obj->table); + + if (obj->table) + { + if (obj->values) + { + sql_render_add_espace (render); + sql_render_append (render, "("); + sql_render_add_list (render, FALSE, NULL, obj->fields, ","); + sql_render_append (render, ")"); + sql_render_add_token (render, "VALUES"); + sql_render_add_list (render, FALSE, NULL, obj->values, ","); + } + else + sql_render_add_token (render, "DEFAULT VALUES"); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_insert_set_table_from_name (SqlInsert * obj, const gchar * table) +{ + g_return_if_fail (SQL_IS_INSERT (obj)); + g_return_if_fail (table); + + sql_object_remove (obj, obj->table); + obj->table = sql_object_add (obj, sql_table_new (table)); +} + +void sql_insert_add_expr (SqlInsert * obj, SqlExpr * expr) +{ + g_return_if_fail (SQL_IS_INSERT (obj)); + g_return_if_fail (SQL_IS_EXPR (expr) || !expr); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_TABLE = 1 + ,PROP_FIELDS + ,PROP_VALUES +}; + +static void sql_insert_set_property (SqlInsert * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TABLE: + sql_object_remove (obj, obj->table); + obj->table = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_FIELDS: + sql_object_remove (obj, obj->fields); + obj->fields = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_VALUES: + sql_object_remove (obj, obj->values); + obj->values = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_insert_get_property (SqlInsert * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TABLE: + g_value_set_object (value, obj->table); + break; + case PROP_FIELDS: + g_value_set_object (value, obj->fields); + break; + case PROP_VALUES: + g_value_set_object (value, obj->values); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_insert_init (SqlInsert * obj) +{ + obj->table = NULL; + obj->fields = NULL; + obj->values = NULL; +} + +static void sql_insert_finalize (SqlInsert * obj) +{ + sql_object_remove (obj, obj->table); + sql_object_remove (obj, obj->fields); + sql_object_remove (obj, obj->values); + G_OBJECT_CLASS (sql_insert_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_insert_class_init (SqlInsertClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_insert_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_insert_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_insert_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_insert_render; + + g_object_class_install_property (k, PROP_TABLE, + sql_param_object ("table" + ,"Table" + ,"The table where the row is inserted" + ,SQL_TYPE_TABLE + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_FIELDS, + sql_param_list ("fields" + ,"Fields" + ,"The list of fields" + ,SQL_TYPE_FIELD + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); + g_object_class_install_property (k, PROP_VALUES, + sql_param_list ("values" + ,"Values" + ,"The list of values" + ,SQL_TYPE_SET + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); +} diff --git a/sql/sql-insert.h b/sql/sql-insert.h new file mode 100644 index 0000000..3be108a --- /dev/null +++ b/sql/sql-insert.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_INSERT_H +#define SQL_INSERT_H + +#define SQL_TYPE_INSERT (sql_insert_get_type ()) +#define SQL_INSERT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_INSERT, SqlInsert)) +#define SQL_IS_INSERT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_INSERT)) + +#include "sql-stmt.h" +#include "sql-field.h" +#include "sql-value.h" +#include "sql-table.h" +#include "sql-set.h" + +typedef struct _SqlInsert SqlInsert; +typedef struct _SqlInsertClass SqlInsertClass; + +/** + * SqlInsert: + * @table: an #SqlTable + * @field: (element-type Sql.Field): list of #SqlField + * @expr: (element-type Sql.Expr): list of #SqlExpr + **/ +struct _SqlInsert +{ + SqlStmt parent; + SqlTable * table; + SqlList * fields; // List of SqlField + SqlList * values; // List of SqlSet +}; + +struct _SqlInsertClass +{ + /* */ + SqlStmtClass parent; +}; + +GType sql_insert_get_type (); +SqlObject * sql_insert_new (); +void sql_insert_set_table_from_name (SqlInsert * obj, const gchar * table); + +#endif \ No newline at end of file diff --git a/sql/sql-join.c b/sql/sql-join.c new file mode 100644 index 0000000..4836962 --- /dev/null +++ b/sql/sql-join.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-join.h" +#include "sql-field.h" + +/** + * SECTION: sql-join + * @Short_description: the equivalent of the SQL JOIN. + * @Title: SqlJoin + * + * The #SqlJoin represents a joins between any targets. + **/ +G_DEFINE_TYPE (SqlJoin, sql_join, SQL_TYPE_TARGET); + +SqlTarget * sql_join_new (SqlTarget * left, SqlTarget * right, SqlJoinType type) +{ + return g_object_new (SQL_TYPE_JOIN + ,"target-left" ,left + ,"target-right" ,right + ,"join-type" ,type + ,NULL + ); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static const gchar * SQL_JOIN_TYPE[] = +{ + "INNER" + ,"LEFT" + ,"RIGHT" +}; + +static void sql_join_render (SqlJoin * obj, SqlRender * render) +{ + sql_render_add_item (render, TRUE, NULL, obj->target_left); + sql_render_add_token (render, SQL_JOIN_TYPE[obj->type]); + sql_render_add_token (render, "JOIN"); + sql_render_add_item (render, TRUE, NULL, obj->target_right); + + if (obj->has_using) + { + sql_render_add_token (render, "USING"); + sql_render_append (render, "("); + sql_render_add_list (render, TRUE, NULL, obj->using_fields, ","); + sql_render_append (render, ")"); + } + else + sql_render_add_item (render, FALSE, "ON", obj->condition); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_join_set_target_left (SqlJoin * obj, SqlTarget * target) +{ + g_return_if_fail (SQL_IS_JOIN (obj)); + g_return_if_fail (SQL_IS_TARGET (target) || SQL_IS_HOLDER (target) || !target); + + sql_object_remove (obj, obj->target_left); + obj->target_left = sql_object_add (obj, target); +} + +void sql_join_set_target_right (SqlJoin * obj, SqlTarget * target) +{ + g_return_if_fail (SQL_IS_JOIN (obj)); + g_return_if_fail (SQL_IS_TARGET (target) || SQL_IS_HOLDER (target) || !target); + + sql_object_remove (obj, obj->target_right); + obj->target_right = sql_object_add (obj, target); +} + +void sql_join_set_condition (SqlJoin * obj, SqlExpr * condition) +{ + g_return_if_fail (SQL_IS_JOIN (obj)); + g_return_if_fail (SQL_IS_EXPR (condition) || SQL_IS_HOLDER (condition) || !condition); + + sql_object_remove (obj, obj->condition); + obj->condition = sql_object_add (obj, condition); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_TARGET_LEFT = 1 + ,PROP_TARGET_RIGHT + ,PROP_TYPE + ,PROP_CONDITION + ,PROP_HAS_USING + ,PROP_USING_FIELDS +}; + +static void sql_join_set_property (SqlJoin * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TARGET_LEFT: + sql_join_set_target_left (obj, g_value_get_object (value)); + break; + case PROP_TARGET_RIGHT: + sql_join_set_target_right (obj, g_value_get_object (value)); + break; + case PROP_TYPE: + obj->type = g_value_get_enum (value); + break; + case PROP_CONDITION: + sql_join_set_condition (obj, g_value_get_object (value)); + break; + case PROP_HAS_USING: + obj->has_using = g_value_get_boolean (value); + break; + case PROP_USING_FIELDS: + sql_object_remove (obj, obj->using_fields); + obj->using_fields = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_join_get_property (SqlJoin * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TARGET_LEFT: + g_value_set_object (value, obj->target_left); + break; + case PROP_TARGET_RIGHT: + g_value_set_object (value, obj->target_right); + break; + case PROP_TYPE: + g_value_set_enum (value, obj->type); + break; + case PROP_CONDITION: + g_value_set_object (value, obj->condition); + break; + case PROP_HAS_USING: + g_value_set_boolean (value, obj->has_using); + break; + case PROP_USING_FIELDS: + g_value_set_object (value, obj->using_fields); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_join_init (SqlJoin * obj) +{ + obj->target_left = NULL; + obj->target_right = NULL; + obj->condition = NULL; + obj->using_fields = NULL; +} + +static void sql_join_finalize (SqlJoin * obj) +{ + sql_object_remove (obj, obj->target_left); + sql_object_remove (obj, obj->target_right); + sql_object_remove (obj, obj->condition); + sql_object_remove (obj, obj->using_fields); + G_OBJECT_CLASS (sql_join_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_join_class_init (SqlJoinClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_join_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_join_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_join_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_join_render; + + g_object_class_install_property (k, PROP_TARGET_LEFT, + sql_param_list ("target-left" + ,"Left target" + ,"The left target in the join" + ,SQL_TYPE_TARGET + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_TARGET_RIGHT, + sql_param_list ("target-right" + ,"Right target" + ,"The right target in the join" + ,SQL_TYPE_TARGET + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_TYPE, + g_param_spec_enum ("join-type" + ,"Type" + ,"The type of join" + ,SQL_TYPE_JOIN_TYPE + ,SQL_JOIN_TYPE_INNER + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_CONDITION, + sql_param_list ("condition" + ,"Condition" + ,"The condition used for the join" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_HAS_USING, + g_param_spec_boolean ("has-using" + ,"Has using" + ,"Wether the condition is a USING" + ,FALSE + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); + g_object_class_install_property (k, PROP_CONDITION, + sql_param_list ("using-fields" + ,"Using fields" + ,"The list of fields of the USING" + ,SQL_TYPE_FIELD + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); +} + +GType sql_join_type_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GEnumValue values[] = + { + {SQL_JOIN_TYPE_INNER ,"SQL_JOIN_TYPE_INNER" ,"inner" + },{SQL_JOIN_TYPE_LEFT ,"SQL_JOIN_TYPE_LEFT" ,"left" + },{SQL_JOIN_TYPE_RIGHT ,"SQL_JOIN_TYPE_RIGHT" ,"right" + },{0, NULL, NULL} + }; + + type = g_enum_register_static + (g_intern_static_string ("SqlJoinType"), values); + } + + return type; +} diff --git a/sql/sql-join.h b/sql/sql-join.h new file mode 100644 index 0000000..91a929c --- /dev/null +++ b/sql/sql-join.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_JOIN_H +#define SQL_JOIN_H + +#include "sql-target.h" +#include "sql-expr.h" + +#define SQL_TYPE_JOIN (sql_join_get_type ()) +#define SQL_IS_JOIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_JOIN)) +#define SQL_JOIN(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_JOIN, SqlJoin)) + +#define SQL_TYPE_JOIN_TYPE (sql_join_type_get_type ()) + +typedef struct _SqlJoin SqlJoin; +typedef struct _SqlJoinClass SqlJoinClass; + +typedef enum +{ + SQL_JOIN_TYPE_INNER + ,SQL_JOIN_TYPE_LEFT + ,SQL_JOIN_TYPE_RIGHT + ,SQL_JOIN_TYPE_COUNT +} +SqlJoinType; + +struct _SqlJoin +{ + SqlTarget parent; + SqlTarget * target_left; + SqlTarget * target_right; + SqlJoinType type; + SqlExpr * condition; + gboolean has_using; + SqlList * using_fields; +}; + +struct _SqlJoinClass +{ + /* */ + SqlTargetClass parent; +}; + +GType sql_join_get_type (); +GType sql_join_type_get_type (); + +SqlTarget * sql_join_new (SqlTarget * left, SqlTarget * right, SqlJoinType type); +void sql_join_set_condition (SqlJoin * obj, SqlExpr * condition); +void sql_join_set_target_right (SqlJoin * obj, SqlTarget * target); +void sql_join_set_target_left (SqlJoin * obj, SqlTarget * target); + +#endif \ No newline at end of file diff --git a/sql/sql-list.c b/sql/sql-list.c new file mode 100644 index 0000000..0b52408 --- /dev/null +++ b/sql/sql-list.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-list.h" + +/** + * SECTION: sql-list + * @Short_description: Container for a list of #SqlObject of a certain type + * @Title: SqlList + **/ +G_DEFINE_TYPE (SqlList, sql_list, SQL_TYPE_OBJECT); + +SqlList * sql_list_new (GType gtype) +{ + return g_object_new (SQL_TYPE_LIST, "gtype", gtype, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_list_render (SqlList * obj, SqlRender * render) +{ + GList * i; + + for (i = obj->items.tail; i; i = i->next) + { + sql_render_add_object (render, i->data); + + if (i->next) + sql_render_append (render, ", "); + } +} + +static gboolean sql_list_is_ready (SqlList * obj) +{ + GList * i; + + for (i = obj->items.tail; i; i = i->next) + if (!sql_object_is_ready (i->data)) + return FALSE; + + return TRUE; +} + +static void sql_list_item_changed (SqlObject * item, SqlObject * obj) +{ + sql_object_changed (obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * sql_list_add: + * @obj: the #SqlList + * @item: (allow-none): the #SqlObject + * + * Adds an item to the end of the list. + **/ +void sql_list_add (SqlList * obj, gpointer item) +{ + g_return_if_fail (SQL_IS_LIST (obj)); + + sql_list_insert (obj, item, -1); +} + +/** + * sql_list_insert: + * @obj: the #SqlList + * @item: (allow-none): the #SqlObject + * @n: the position in which to place the item + * + * Adds an item to the list. If position is a negative number the element will + * be added to the end of the list. + **/ +void sql_list_insert (SqlList * obj, gpointer item, guint n) +{ + g_return_if_fail (SQL_IS_LIST (obj)); + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (item, obj->gtype) || SQL_IS_HOLDER (item) || !item); + + if (n > 0) + g_queue_push_nth (&obj->items, g_object_ref_sink (item), n); + else + g_queue_push_head (&obj->items, g_object_ref_sink (item)); + + g_signal_connect (item, "changed", + G_CALLBACK (sql_list_item_changed), obj + ); +} + +/** + * sql_list_get: + * @obj: the #SqlList + * + * Gets an item from the list. + * + * Return value: (transfer none) (allow-none): the selected #SqlObject + **/ +gpointer sql_list_get (SqlList * obj, guint n) +{ + g_return_val_if_fail (SQL_IS_LIST (obj), NULL); + + return g_queue_peek_nth (&obj->items, n); +} + +/** + * sql_list_remove: + * @obj: the #SqlList + * + * Removes an item from the list. + * + * Return value: (transfer none) (allow-none): the removed #SqlObject + **/ +gpointer sql_list_remove (SqlList * obj, guint n) +{ + SqlObject * item; + + g_return_val_if_fail (SQL_IS_LIST (obj), NULL); + + item = g_queue_pop_nth (&obj->items, n); + g_signal_handlers_disconnect_by_func (item, + sql_list_item_changed, obj); + + return item; +} + +/** + * sql_list_get_items: + * @obj: the #SqlList + * + * Gets all list elements. + * + * Return value: (transfer none) (allow-none): the list items + **/ +GList * sql_list_get_items (SqlList * obj) +{ + g_return_val_if_fail (SQL_IS_LIST (obj), NULL); + + return obj->items.tail; +} + +/** + * sql_list_get_items_type: + * @obj: the #SqlList + * + * Gets the allowed type for the list elements. + * + * Return value: the #GType + **/ +GType sql_list_get_items_type (SqlList * obj) +{ + g_return_val_if_fail (SQL_IS_LIST (obj), 0); + + return obj->gtype; +} + +/** + * sql_list_lenght: + * @obj: the #SqlList + * + * Gets the number of items in the list. + * + * Return value: the number of items + **/ +guint sql_list_length (SqlList * obj) +{ + g_return_val_if_fail (SQL_IS_LIST (obj), 0); + + return obj->items.length; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_GTYPE = 1 + ,PROP_LENGTH +}; + +static void sql_list_set_property (SqlList * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_GTYPE: + obj->gtype = g_value_get_gtype (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_list_get_property (SqlList * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_GTYPE: + g_value_set_gtype (value, obj->gtype); + break; + case PROP_LENGTH: + g_value_set_uint (value, sql_list_length (obj)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_list_init (SqlList * obj) +{ + g_queue_init (&obj->items); +} + +static void sql_list_finalize (SqlList * obj) +{ + GList * i; + + for (i = obj->items.tail; i; i = i->next) + { + g_signal_handlers_disconnect_by_func (i->data, + sql_list_item_changed, obj); + g_object_unref (i->data); + } + + g_queue_clear (&obj->items); + G_OBJECT_CLASS (sql_list_finalize)->finalize (G_OBJECT (obj)); +} + +static void sql_list_class_init (SqlListClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) sql_list_finalize; + klass->set_property = (GObjectSetPropertyFunc) sql_list_set_property; + klass->get_property = (GObjectGetPropertyFunc) sql_list_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_list_render; + SQL_OBJECT_CLASS (klass)->is_ready = (SqlObjectIsReadyFunc) sql_list_is_ready; + + g_object_class_install_property (klass, PROP_LENGTH, + g_param_spec_uint ("length" + ,_("Length") + ,_("The length of the list") + ,0, G_MAXUINT, 0 + ,G_PARAM_READABLE + )); + g_object_class_install_property (klass, PROP_GTYPE, + g_param_spec_gtype ("gtype" + ,_("GType") + ,_("The allowed type for the items") + ,SQL_TYPE_OBJECT + ,G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/sql/sql-list.h b/sql/sql-list.h new file mode 100644 index 0000000..d1db8af --- /dev/null +++ b/sql/sql-list.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_LIST_H +#define SQL_LIST_H + +#define SQL_TYPE_LIST (sql_list_get_type ()) +#define SQL_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_LIST, SqlList)) +#define SQL_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_LIST)) + +typedef struct _SqlList SqlList; +typedef struct _SqlListClass SqlListClass; + +#include "sql-object.h" + +struct _SqlList +{ + SqlObject parent; + GType gtype; + GQueue items; +}; + +struct _SqlListClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_list_get_type (); +SqlList * sql_list_new (GType gtype); +void sql_list_add (SqlList * obj, gpointer item); +void sql_list_insert (SqlList * obj, gpointer item, guint n); +gpointer sql_list_get (SqlList * obj, guint n); +gpointer sql_list_remove (SqlList * obj, guint n); +GList * sql_list_get_items (SqlList * obj); +GType sql_list_get_items_type (SqlList * obj); +guint sql_list_length (SqlList * obj); + +#endif \ No newline at end of file diff --git a/sql/sql-multi-stmt.c b/sql/sql-multi-stmt.c new file mode 100644 index 0000000..2f7047e --- /dev/null +++ b/sql/sql-multi-stmt.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-multi-stmt.h" + +/** + * SECTION: sql-multi-stmt + * @Short_description: the equivalent multiple statements separated by semicolon. + * @Title: SqlMultiStmt + * + * The #SqlMultiStmt represents multiple SQL statements separated by semicolon. + **/ +G_DEFINE_TYPE (SqlMultiStmt, sql_multi_stmt, SQL_TYPE_STMT); + +SqlMultiStmt * sql_multi_stmt_new () +{ + return g_object_new (SQL_TYPE_MULTI_STMT, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_multi_stmt_render (SqlMultiStmt * obj, SqlRender * render) +{ + sql_render_add_list (render, TRUE, NULL, obj->stmts, ";"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_STMTS = 1 +}; + +static void sql_mutli_stmt_set_property (SqlMultiStmt * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_STMTS: + sql_object_remove (obj, obj->stmts); + obj->stmts = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_mutli_stmt_get_property (SqlMultiStmt * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_STMTS: + g_value_set_object (value, obj->stmts); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_multi_stmt_init (SqlMultiStmt * obj) +{ + obj->stmts = NULL; +} + +static void sql_multi_stmt_finalize (SqlMultiStmt * obj) +{ + sql_object_remove (obj, obj->stmts); + G_OBJECT_CLASS (sql_multi_stmt_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_multi_stmt_class_init (SqlMultiStmtClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_multi_stmt_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_mutli_stmt_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_mutli_stmt_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_multi_stmt_render; + + g_object_class_install_property (k, PROP_STMTS, + sql_param_list ("stmts" + ,"Statements" + ,"The list of statements" + ,SQL_TYPE_STMT + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); +} diff --git a/sql/sql-multi-stmt.h b/sql/sql-multi-stmt.h new file mode 100644 index 0000000..2b544be --- /dev/null +++ b/sql/sql-multi-stmt.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_MULTI_STMT_H +#define SQL_MULTI_STMT_H + +#include "sql-stmt.h" + +#define SQL_TYPE_MULTI_STMT (sql_multi_stmt_get_type ()) +#define SQL_MULTI_STMT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_MULTI_STMT, SqlMultiStmt)) +#define SQL_IS_MULTI_STMT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_MULTI_STMT)) + +typedef struct _SqlMultiStmt SqlMultiStmt; +typedef struct _SqlMultiStmtClass SqlMultiStmtClass; + +/** + * SqlMultiStmt: + * @stmt: (element-type Sql.Stmt): list of #SqlStmt + **/ +struct _SqlMultiStmt +{ + SqlStmt parent; + SqlList * stmts; +}; + +struct _SqlMultiStmtClass +{ + /* */ + SqlStmtClass parent; +}; + +GType sql_multi_stmt_get_type (); +SqlMultiStmt * sql_multi_stmt_new (); + +#endif diff --git a/sql/sql-object.c b/sql/sql-object.c new file mode 100644 index 0000000..d3363b0 --- /dev/null +++ b/sql/sql-object.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-object.h" + +/** + * SECTION: sql-object + * @Short_description: represents an SQL statement or a part of it + * @Title: SqlObject + * + * The #SqlObject is the base class for all objects in SqlLib. + **/ +G_DEFINE_ABSTRACT_TYPE (SqlObject, sql_object, G_TYPE_INITIALLY_UNOWNED); + +enum { + CHANGED + ,LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_object_child_changed (SqlObject * child, SqlObject * obj) +{ + sql_object_changed (obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Protected + +/** + * sql_object_add: + * @obj: #SqlObject where the new item will be added + * @child: #SqlObject the object is added + * + * Note that this function is considered a protected method and should only + * be used in classes that inherit from #SqlObjectClass. + **/ +gpointer sql_object_add (gpointer obj, gpointer child) +{ + if (child) + { + g_object_ref_sink (child); + g_signal_connect (child, "changed", + G_CALLBACK (sql_object_child_changed), obj); + } + + return child; +} + +/** + * sql_object_remove: + * @obj: #SqlObject where the new item will be added + * @child: #SqlObject the object is added + * + * Note that this function is considered a protected method and should only + * be used in classes that inherit from #SqlObjectClass. + **/ +void sql_object_remove (gpointer obj, gpointer child) +{ + if (child) + { + g_signal_handlers_disconnect_by_func (child, + sql_object_child_changed, obj); + g_object_unref (child); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * sql_object_render: + * @obj: a #SqlObject + * @render: an #SqlRender + * + * Renders the object into a SQL string and stores it into the renderer. + **/ +void sql_object_render (SqlObject * obj, SqlRender * render) +{ + g_return_if_fail (SQL_IS_OBJECT (obj)); + g_return_if_fail (SQL_IS_RENDER (render)); + +// SQL_OBJECT_GET_CLASS (obj)->render (obj, render); +} + +/** + * sql_object_is_ready: + * @obj: a #SqlObject + * + * Checks if @obj and all of its elemens are ready to be rendered. + * + * Return value: %TRUE if ready, %FALSE otherwise. + **/ +gboolean sql_object_is_ready (SqlObject * obj) +{ + SqlObjectClass * klass = SQL_OBJECT_GET_CLASS (obj); + + g_return_val_if_fail (SQL_IS_OBJECT (obj), FALSE); + + if (obj->held_objects) + { + GList * i; + GList * held_objects = g_hash_table_get_values (obj->held_objects); + gboolean is_ready = TRUE; + + for (i = held_objects; i; i = i->next) + if (!sql_object_is_ready (i->data)) + { + is_ready = FALSE; + break; + } + + g_list_free (held_objects); + + if (!is_ready) + return FALSE; + } + + if (klass->is_ready) + return klass->is_ready (obj); + else + return TRUE; +} + +void sql_object_set (SqlObject * obj, const gchar * property, SqlObject * value) +{ + g_return_if_fail (SQL_IS_OBJECT (obj)); + g_return_if_fail (SQL_IS_OBJECT (value)); + + g_object_set (obj, property, value, NULL); +} + + +SqlObject * sql_object_get (SqlObject * obj, const gchar * property) +{ + SqlObject * value; + + g_return_val_if_fail (SQL_IS_OBJECT (obj), NULL); + + g_object_get (obj, property, &value, NULL); + return value; +} + +void sql_object_add_child (SqlObject * obj, const gchar * property, SqlObject * child) +{ + SqlList * list; + + g_return_if_fail (SQL_IS_OBJECT (obj)); + g_return_if_fail (SQL_IS_OBJECT (child)); + + g_object_get (obj, property, &list, NULL); + sql_list_add (list, child); +} + +void sql_object_remove_child (SqlObject * obj, const gchar * property, guint n) +{ + SqlList * list; + + g_return_if_fail (SQL_IS_OBJECT (obj)); + + g_object_get (obj, property, &list, NULL); + sql_list_remove (list, n); +} + +/** + * sql_object_add_held_object: + * @obj: a #SqlObject + * @id: the id of the #SqlHolder + * @held_object: the held object + * + * Adds a held object. + **/ +void sql_object_add_held_object (SqlObject * obj, const gchar * id, SqlObject * held_object) +{ + g_return_if_fail (SQL_IS_OBJECT (obj)); + g_return_if_fail (id); + + if (!obj->held_objects) + obj->held_objects = g_hash_table_new_full ( + g_str_hash + ,g_str_equal + ,g_free + ,g_object_unref + ); + + g_signal_connect (held_object, "changed", + G_CALLBACK (sql_object_child_changed), obj + ); + g_hash_table_replace (obj->held_objects, + g_strdup (id), g_object_ref_sink (held_object)); +} + +/** + * sql_object_get_held_object: + * @obj: a #SqlObject + * @id: the id of the #SqlHolder + * + * Gets a held object by its id. + * + * Return value: the #SqlObject if an object with that id exists, %NULL otherwise + **/ +SqlObject * sql_object_get_held_object (SqlObject * obj, const gchar * id) +{ + g_return_val_if_fail (SQL_IS_OBJECT (obj), NULL); + g_return_val_if_fail (id, NULL); + + if (obj->held_objects) + return g_hash_table_lookup (obj->held_objects, id); + + return NULL; +} + +/** + * sql_object_changed: + * @obj: a #SqlObject + * + * Emits the changed signal on #SqlObject. + **/ +void sql_object_changed (SqlObject * obj) +{ + g_signal_emit (obj, signals[CHANGED], 0); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_object_init (SqlObject * obj) +{ + obj->child_groups = NULL; + obj->held_objects = NULL; +} + +static void sql_object_finalize (SqlObject * obj) +{ + if (obj->child_groups) + g_hash_table_destroy (obj->child_groups); + if (obj->held_objects) + g_hash_table_destroy (obj->held_objects); + + G_OBJECT_CLASS (sql_object_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_object_class_init (SqlObjectClass * klass) +{ + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) sql_object_finalize; + klass->is_ready = NULL; + klass->render = NULL; + + /** + * SqlObject::changed: + * @obj: the object which emit the signal. + * + * This signal is emitted every time the statement or one of its attributes + * is modified. + */ + signals[CHANGED] = g_signal_new ("changed", SQL_TYPE_OBJECT, + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); +} diff --git a/sql/sql-object.h b/sql/sql-object.h new file mode 100644 index 0000000..fc81513 --- /dev/null +++ b/sql/sql-object.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_OBJECT_H +#define SQL_OBJECT_H + +#include +#include "sql-param-object.h" +#include "sql-param-list.h" + +#define SQL_TYPE_OBJECT (sql_object_get_type ()) +#define SQL_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_OBJECT, SqlObject)) +#define SQL_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_OBJECT)) +#define SQL_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, SQL_TYPE_OBJECT, SqlObjectClass)) +#define SQL_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, SQL_TYPE_OBJECT, SqlObjectClass)) + +typedef struct _SqlObject SqlObject; +typedef struct _SqlObjectClass SqlObjectClass; + +typedef gboolean (* SqlObjectIsReadyFunc) (SqlObject * obj); + +struct _SqlObject +{ + GInitiallyUnowned parent; + GHashTable * child_groups; + GHashTable * held_objects; +}; + +struct _SqlObjectClass +{ + /* */ + GInitiallyUnownedClass parent; + gpointer /* SqlRenderFunc */ render; + SqlObjectIsReadyFunc is_ready; +}; + +#include "sql-holder.h" +#include "sql-list.h" +#include "sql-render.h" + +GType sql_object_get_type (); +void sql_object_render (SqlObject * obj, SqlRender * render); +gboolean sql_object_is_ready (SqlObject * obj); + +void sql_object_set (SqlObject * obj, const gchar * property, SqlObject * set); +SqlObject * sql_object_get (SqlObject * obj, const gchar * property); +void sql_object_add_child (SqlObject * obj, const gchar * property, SqlObject * child); +void sql_object_remove_child (SqlObject * obj, const gchar * property, guint n); + +void sql_object_add_held_object (SqlObject * obj, const gchar * id, SqlObject * held_object); +SqlObject * sql_object_get_held_object (SqlObject * obj, const gchar * id); +void sql_object_changed (SqlObject * obj); + +gpointer sql_object_add (gpointer obj, gpointer child); +void sql_object_remove (gpointer obj, gpointer child); + +#endif diff --git a/sql/sql-operation.c b/sql/sql-operation.c new file mode 100644 index 0000000..b0fa96f --- /dev/null +++ b/sql/sql-operation.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-operation.h" +#include "sql-value.h" + +/** + * SECTION: sql-operation + * @Short_description: operations between #SqlExpr + * @Title: SqlOperation + * + * Any operation over or between one or more #SqlExpr is represented by the + * #SqlOperation of the corresponding type. To see the different types of + * #SqlOperation see #SqlOperationType. + **/ +G_DEFINE_TYPE (SqlOperation, sql_operation, SQL_TYPE_EXPR); + +SqlObject * sql_operation_new (SqlOperationType type) +{ + return g_object_new (SQL_TYPE_OPERATION, "type", type, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static const gchar * SQL_OPERATION_TYPE[] = +{ + "NOT" + ,"-" + ,"+" + + ,"*" + ,"/" + ,"+" + ,"-" + ,"IS" + ,"=" + ,"!=" + ,">=" + ,">" + ,"<=" + ,"<" + ,"LIKE" + ,"AND" + ,"OR" + ,"XOR" + ,"%" + ,"IN" +}; + +static void sql_operation_render (SqlOperation * obj, SqlRender * render) +{ + sql_render_add_espace (render); + sql_render_append (render, "("); + sql_render_add_list (render, TRUE, NULL, obj->operators, + SQL_OPERATION_TYPE[obj->type]); + sql_render_append (render, ")"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_TYPE = 1 + ,PROP_OPERATORS +}; + +static void sql_operation_set_property (SqlOperation * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TYPE: + obj->type = g_value_get_enum (value); + break; + case PROP_OPERATORS: + sql_object_remove (obj, obj->operators); + obj->operators = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_operation_get_property (SqlOperation * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TYPE: + g_value_set_enum (value, obj->type); + break; + case PROP_OPERATORS: + g_value_set_object (value, obj->operators); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_operation_init (SqlOperation * obj) +{ + obj->operators = NULL; +} + +static void sql_operation_finalize (SqlOperation * obj) +{ + sql_object_remove (obj, obj->operators); + G_OBJECT_CLASS (sql_operation_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_operation_class_init (SqlOperationClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_operation_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_operation_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_operation_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_operation_render; + + g_object_class_install_property (k, PROP_TYPE, + g_param_spec_enum ("type" + ,"Type" + ,"The type of the operation" + ,SQL_TYPE_OPERATION_TYPE + ,SQL_OPERATION_TYPE_AND + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_OPERATORS, + sql_param_list ("operators" + ,"Operators" + ,"The list of operators" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); +} + +GType sql_operation_type_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GEnumValue values[] = + { + {SQL_OPERATION_TYPE_NOT ,"SQL_OPERATION_TYPE_NOT" ,"not" + },{SQL_OPERATION_TYPE_MINUS ,"SQL_OPERATION_TYPE_MINUS" ,"minus" + },{SQL_OPERATION_TYPE_PLUS ,"SQL_OPERATION_TYPE_PLUS" ,"plus" + },{SQL_OPERATION_TYPE_MULTIPLICATION ,"SQL_OPERATION_TYPE_MULTIPLICATION" ,"multiplication" + },{SQL_OPERATION_TYPE_DIVISION ,"SQL_OPERATION_TYPE_DIVISION" ,"division" + },{SQL_OPERATION_TYPE_SUM ,"SQL_OPERATION_TYPE_SUM" ,"sum" + },{SQL_OPERATION_TYPE_SUBTRACTION ,"SQL_OPERATION_TYPE_SUBTRACTION" ,"subtraction" + },{SQL_OPERATION_TYPE_IS ,"SQL_OPERATION_TYPE_IS" ,"is" + },{SQL_OPERATION_TYPE_EQUAL ,"SQL_OPERATION_TYPE_EQUAL" ,"equal" + },{SQL_OPERATION_TYPE_NOT_EQUAL ,"SQL_OPERATION_TYPE_NOT_EQUAL" ,"not-equal" + },{SQL_OPERATION_TYPE_GREATER_EQUAL ,"SQL_OPERATION_TYPE_GREATER_EQUAL" ,"greater-equal" + },{SQL_OPERATION_TYPE_GREATER ,"SQL_OPERATION_TYPE_GREATER" ,"greater" + },{SQL_OPERATION_TYPE_LOWER_EQUAL ,"SQL_OPERATION_TYPE_LOWER_EQUAL" ,"lower-equal" + },{SQL_OPERATION_TYPE_LOWER ,"SQL_OPERATION_TYPE_LOWER" ,"lower" + },{SQL_OPERATION_TYPE_LIKE ,"SQL_OPERATION_TYPE_LIKE" ,"like" + },{SQL_OPERATION_TYPE_AND ,"SQL_OPERATION_TYPE_AND" ,"and" + },{SQL_OPERATION_TYPE_OR ,"SQL_OPERATION_TYPE_OR" ,"or" + },{SQL_OPERATION_TYPE_XOR ,"SQL_OPERATION_TYPE_XOR" ,"xor" + },{SQL_OPERATION_TYPE_MOD ,"SQL_OPERATION_TYPE_MOD" ,"mod" + },{SQL_OPERATION_TYPE_IN ,"SQL_OPERATION_TYPE_IN" ,"in" + },{0, NULL, NULL} + }; + + type = g_enum_register_static + (g_intern_static_string ("SqlOperationType"), values); + } + + return type; +} diff --git a/sql/sql-operation.h b/sql/sql-operation.h new file mode 100644 index 0000000..78da757 --- /dev/null +++ b/sql/sql-operation.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_OPERATION_H +#define SQL_OPERATION_H + +#include "sql-expr.h" + +#define SQL_TYPE_OPERATION (sql_operation_get_type ()) +#define SQL_OPERATION(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_OPERATION, SqlOperation)) +#define SQL_IS_OPERATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_OPERATION)) + +#define SQL_TYPE_OPERATION_TYPE (sql_operation_type_get_type ()) + +typedef struct _SqlOperation SqlOperation; +typedef struct _SqlOperationClass SqlOperationClass; + +typedef enum +{ + // Unary + SQL_OPERATION_TYPE_NOT + ,SQL_OPERATION_TYPE_MINUS + ,SQL_OPERATION_TYPE_PLUS + + // Binary + ,SQL_OPERATION_TYPE_MULTIPLICATION + ,SQL_OPERATION_TYPE_DIVISION + ,SQL_OPERATION_TYPE_SUM + ,SQL_OPERATION_TYPE_SUBTRACTION + ,SQL_OPERATION_TYPE_IS + ,SQL_OPERATION_TYPE_EQUAL + ,SQL_OPERATION_TYPE_NOT_EQUAL + ,SQL_OPERATION_TYPE_GREATER_EQUAL + ,SQL_OPERATION_TYPE_GREATER + ,SQL_OPERATION_TYPE_LOWER_EQUAL + ,SQL_OPERATION_TYPE_LOWER + ,SQL_OPERATION_TYPE_LIKE + ,SQL_OPERATION_TYPE_AND + ,SQL_OPERATION_TYPE_OR + ,SQL_OPERATION_TYPE_XOR + ,SQL_OPERATION_TYPE_MOD + ,SQL_OPERATION_TYPE_IN + ,SQL_OPERATION_TYPE_COUNT +} +SqlOperationType; + +/** + * SqlOperation: + * @expr: (element-type Sql.Expr): list of #SqlExpr + * @type: the type of operation. See #SqlOperationType for the possible values + **/ +struct _SqlOperation +{ + SqlExpr parent; + SqlList * operators; // List of SqlExpr + SqlOperationType type; +}; + +struct _SqlOperationClass +{ + /* */ + SqlExprClass parent; +}; + +GType sql_operation_get_type (); +GType sql_operation_type_get_type (); + +SqlObject * sql_operation_new (SqlOperationType type); + +#endif diff --git a/sql/sql-param-list.c b/sql/sql-param-list.c new file mode 100644 index 0000000..61b5963 --- /dev/null +++ b/sql/sql-param-list.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-param-list.h" +#include "sql-holder.h" + +GParamSpec * +sql_param_list (const gchar * name, const gchar * nick, + const gchar * blurb, GType items_type, GParamFlags flags) +{ + SqlParamList * pspec; + + g_return_val_if_fail (g_type_is_a (items_type, SQL_TYPE_OBJECT), NULL); + + pspec = g_param_spec_internal (SQL_TYPE_PARAM_OBJECT + ,name + ,nick + ,blurb + ,flags + ); + + pspec->items_type = items_type; + G_PARAM_SPEC (pspec)->value_type = SQL_TYPE_LIST; + return G_PARAM_SPEC (pspec); +} + +static void +sql_param_list_init (GParamSpec * pspec) {} + +static void +sql_param_list_set_default (GParamSpec * pspec, GValue * value) +{ + SqlList * list = sql_list_new (SQL_PARAM_LIST (pspec)->items_type); + g_value_set_object (value, list); +} + +static gboolean +sql_param_list_validate (GParamSpec * pspec, GValue * value) +{ + GType type; + gboolean change; + gpointer object = g_value_get_object (value); + + if (!object) + return TRUE; + + type = G_OBJECT_TYPE (object); + + change = !( + (g_value_type_compatible (type, SQL_TYPE_LIST) + && SQL_PARAM_LIST (pspec)->items_type == sql_list_get_items_type (object)) + || g_value_type_compatible (type, SQL_TYPE_HOLDER) + ); + + if (change) + g_value_set_object (value, NULL); + + return change; +} + +static gint +sql_param_list_values_cmp (GParamSpec * pspec, const GValue * a, const GValue * b) +{ + guint8 * pa = g_value_get_object (a); + guint8 * pb = g_value_get_object (b); + + return pa < pb ? -1 : pa > pb; +} + +GType +sql_param_list_get_type () +{ + static GType type = 0; + + if (!type) + { + GParamSpecTypeInfo pspec_info = + { + sizeof (SqlParamList) + ,16 + ,sql_param_list_init + ,SQL_TYPE_LIST + ,NULL + ,sql_param_list_set_default + ,sql_param_list_validate + ,sql_param_list_values_cmp + }; + + type = g_param_type_register_static ( + g_intern_static_string ("SqlParamList"), &pspec_info); + } + + return type; +} + diff --git a/sql/sql-param-list.h b/sql/sql-param-list.h new file mode 100644 index 0000000..0067b6f --- /dev/null +++ b/sql/sql-param-list.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_PARAM_LIST_H +#define SQL_PARAM_LIST_H + +#include "sql-object.h" + +#define SQL_TYPE_PARAM_LIST (sql_param_list_get_type ()) +#define SQL_PARAM_LIST(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), SQL_TYPE_PARAM_LIST, SqlParamList)) + + +typedef struct +{ + GParamSpec parent; + GType items_type; +} +SqlParamList; + +GType sql_param_list_get_type (); +GParamSpec * sql_param_list (const gchar * name + ,const gchar * nick + ,const gchar * blurb + ,GType items_type + ,GParamFlags flags); + +#endif + diff --git a/sql/sql-param-object.c b/sql/sql-param-object.c new file mode 100644 index 0000000..490956e --- /dev/null +++ b/sql/sql-param-object.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-param-object.h" +#include "sql-holder.h" + +GParamSpec * +sql_param_object (const gchar * name, const gchar * nick, + const gchar * blurb, GType object_type, GParamFlags flags) +{ + GParamSpec * pspec; + + g_return_val_if_fail (g_type_is_a (object_type, SQL_TYPE_OBJECT), NULL); + + pspec = g_param_spec_internal (SQL_TYPE_PARAM_OBJECT + ,name + ,nick + ,blurb + ,flags + ); + + pspec->value_type = object_type; + return pspec; +} + +static void +sql_param_object_init (GParamSpec * pspec) {} + +static void +sql_param_object_set_default (GParamSpec * pspec, GValue * value) +{ + g_value_set_object (value, NULL); +} + +static gboolean +sql_param_object_validate (GParamSpec * pspec, GValue * value) +{ + GType type; + gboolean change; + GObject * object = g_value_get_object (value); + + if (!object) + return TRUE; + + type = G_OBJECT_TYPE (object); + + change = !( + g_value_type_compatible (type, G_PARAM_SPEC_VALUE_TYPE (pspec)) + || g_value_type_compatible (type, SQL_TYPE_HOLDER) + ); + + if (change) + g_value_set_object (value, NULL); + + return change; +} + +static gint +sql_param_object_values_cmp (GParamSpec * pspec, const GValue * a, const GValue * b) +{ + guint8 * pa = g_value_get_object (a); + guint8 * pb = g_value_get_object (b); + + return pa < pb ? -1 : pa > pb; +} + +GType +sql_param_object_get_type () +{ + static GType type = 0; + + if (!type) + { + GParamSpecTypeInfo pspec_info = + { + sizeof (SqlParamObject) + ,16 + ,sql_param_object_init + ,SQL_TYPE_OBJECT + ,NULL + ,sql_param_object_set_default + ,sql_param_object_validate + ,sql_param_object_values_cmp + }; + + type = g_param_type_register_static ( + g_intern_static_string ("SqlParamObject"), &pspec_info); + } + + return type; +} + diff --git a/sql/sql-param-object.h b/sql/sql-param-object.h new file mode 100644 index 0000000..72a5f07 --- /dev/null +++ b/sql/sql-param-object.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_PARAM_OBJECT_H +#define SQL_PARAM_OBJECT_H + +#include "sql-object.h" + +#define SQL_TYPE_PARAM_OBJECT (sql_param_object_get_type ()) +#define SQL_PARAM_OBJECT(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), SQL_TYPE_PARAM_OBJECT, SqlParamObject)) + +typedef struct +{ + GParamSpec parent; +} +SqlParamObject; + +GType sql_param_object_get_type (); +GParamSpec * sql_param_object (const gchar * name + ,const gchar * nick + ,const gchar * blurb + ,GType object_type + ,GParamFlags flags); + +#endif diff --git a/sql/sql-parser.h b/sql/sql-parser.h new file mode 100644 index 0000000..9c68dd2 --- /dev/null +++ b/sql/sql-parser.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_PARSER_H +#define SQL_PARSER_H + +#include "sql-object.h" + +#define SQL_PARSER_LOG_DOMAIN (g_quark_from_string ("SqlParser")) + +/** + * sql_parser_parse: + * @sql: An string containing an SQL query. + * + * Parses @sql and makes an #SqlObject from the query on it. + * + * Return value: (transfer full): an #SqlObject. + */ +SqlObject * sql_parser_parse (gchar * sql) G_GNUC_WARN_UNUSED_RESULT; + +#endif \ No newline at end of file diff --git a/sql/sql-render.c b/sql/sql-render.c new file mode 100644 index 0000000..ce7dfa9 --- /dev/null +++ b/sql/sql-render.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-render.h" +#include + +/** + * SECTION: sql-render + * @Short_description: renders an #SqlObject to a string + * @Title: SqlRender + * + * #SqlRender takes care of rendering the different types of #SqlObject to a + * valid SQL string, ready to pass to a Database. In most cases the user won't + * need to use any method besides the creation and destruction ones and + * sql_render_get_string(). + **/ +G_DEFINE_TYPE (SqlRender, sql_render, G_TYPE_OBJECT); + +/** + * sql_render_new: + * @delimiter: the delimiter character + * + * Creates an #SqlRender object + * + * Return value: the new #SqlRender + **/ +SqlRender * sql_render_new (gchar delimiter) +{ + return g_object_new (SQL_TYPE_RENDER, "delimiter", delimiter, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * sql_render_get_string: + * @obj: a #SqlRender + * @object: the #SqlObject + * @data: pointer to custom data + * @err: (out) (allow-none): the return location for a #GError or %NULL + * + * Transforms the #SqlObject into a SQL string. + * + * Return value: a string with the rendered statement of %NULL if error. + **/ +gchar * sql_render_get_string (SqlRender * obj, gpointer object, gpointer data, GError ** err) +{ + gchar * sql; + + g_return_val_if_fail (SQL_IS_RENDER (obj), NULL); + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + + obj->data = data; + obj->object = g_object_ref (object); + obj->buffer = g_string_sized_new (SQL_BUFFER_SIZE); + obj->ancestors = NULL; + + sql_render_add_object (obj, object); + + if (obj->error) + { + if (!err) + g_warning ("%s",obj->error->message); + else + g_propagate_error (err, obj->error); + + g_clear_error (&obj->error); + sql = g_string_free (obj->buffer, TRUE); + } + else + sql = g_string_free (obj->buffer, FALSE); + + g_clear_object (&obj->object); + g_slist_free (obj->ancestors); + obj->ancestors = NULL; + obj->buffer = NULL; + obj->data = NULL; + return sql; +} + +/** + * sql_render_register_function: + * @obj: a #SqlRender + * @type: #GType for which the function will be used + * @function: (scope async): render function to use with @type + * + * Registers @function as the function to be used to render the objects of type + * @type. Users most likely won't need to use it. + **/ +void sql_render_register_function (SqlRender * obj, GType type, SqlRenderFunc function) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + g_hash_table_insert (obj->custom_renderers, GUINT_TO_POINTER (type), function); +} + +/** + * sql_render_get_ancestors: + * @obj: the #SqlRender + * + * Obtains a list of parents of the currently rendered object, including it. + * + * Return value: (transfer none): the #GSList with the parents, the list should + * not be edited or freed. + **/ +GSList * sql_render_get_ancestors (SqlRender * obj) +{ + g_return_val_if_fail (SQL_IS_RENDER (obj), NULL); + + return obj->ancestors; +} + +/** + * sql_render_add_object: + * @obj: a #SqlRender + * @object: a #gpointer to an object + * + * Adds an object to a render. + **/ +void sql_render_add_object (SqlRender * obj, gpointer object) +{ + SqlRenderFunc function; + + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (G_IS_OBJECT (object) || !object); + + if (object) + { + function = g_hash_table_lookup (obj->custom_renderers, + GUINT_TO_POINTER (G_OBJECT_TYPE (object))); + obj->ancestors = g_slist_prepend (obj->ancestors, object); + + if (function) + function (object, obj); + else + sql_object_render (object, obj); + + obj->ancestors = g_slist_delete_link (obj->ancestors, obj->ancestors); + } +} + +/** + * sql_render_add_espace: + * @obj: a #SqlRender + * + * Adds an space character to a render. + **/ +void sql_render_add_espace (SqlRender * obj) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + gsize len = obj->buffer->len; + + if (len > 0) + switch (obj->buffer->str[len-1]) + { + case ' ': + case '(': + case '.': + return; + default: + g_string_append_c (obj->buffer, ' '); + } +} + +/** + * sql_render_printf: + * @obj: a #SqlRender + * @string: a format string. + * @...: a %NULL terminated list of variables. + * + * Creates an string from a format string. + **/ +void sql_render_printf (SqlRender * obj, const gchar * string, ...) +{ + va_list vl; + + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (string); + + va_start (vl, string); + sql_render_add_espace (obj); + g_string_append_vprintf (obj->buffer, string, vl); + va_end (vl); +} + +/** + * sql_render_append: + * @obj: a #SqlRender + * @string: a character string. + * + * Appends @string to the current contents of @obj. + **/ +void sql_render_append (SqlRender * obj, const gchar * string) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (string); + + g_string_append (obj->buffer, string); +} + +/** + * sql_render_append_with_delimiter: + * @obj: a #SqlRender + * @string: a character string. + * @delimiter: the delimiter character to use. + * + * Appends @string to the current contents of @obj. @delimiter is used to add + * spaces into the string depending where required. + **/ +void sql_render_append_with_delimiter (SqlRender * obj, const gchar * string, gchar delimiter) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + if (string) + { + sql_render_add_espace (obj); + g_string_append_c (obj->buffer, delimiter); + g_string_append (obj->buffer, string); + g_string_append_c (obj->buffer, delimiter); + } +} + +/** + * sql_render_add_token: + * @obj: a #SqlRender + * @token: an SQL reserved keyword + * + * Adds @token to @obj. Note that @token must be a reserved SQL keyword. + **/ +void sql_render_add_token (SqlRender * obj, const gchar * token) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + if (token) + { + sql_render_add_espace (obj); + g_string_append (obj->buffer, token); + } +} + +/** + * sql_render_add_identifier: + * @obj: a #SqlRender + * @identifier: a character string. + * + * Adds @identifier to @obj. @identifier is taken as the name of an identifier + * for an expression in an SQL string. + **/ +void sql_render_add_identifier (SqlRender * obj, const gchar * identifier) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + sql_render_append_with_delimiter (obj, identifier, + obj->delimiter); +} + +/** + * sql_render_add_item: + * @obj: a #SqlRender + * @required: + * @token: + * @item: + * + * + **/ +void sql_render_add_item (SqlRender * obj, gboolean required, const gchar * token, gpointer item) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (G_IS_OBJECT (item) || !item); + + if (item) + { + sql_render_add_token (obj, token); + sql_render_add_object (obj, item); + } + else if (required) + sql_render_set_error (obj); +} + +/** + * sql_render_add_list: + * @obj: a #SqlRender + * @required: + * @token: + * @list: (element-type GObject.Object): a list of objects to add + * @separator: + * + * + **/ +void sql_render_add_list (SqlRender * obj, gboolean required, const gchar * token, + SqlList * list, const gchar * separator) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (SQL_IS_LIST (list)); + + if (list) + { + GList * n; + sql_render_add_token (obj, token); + + for (n = sql_list_get_items (list); n; n = n->next) + { + sql_render_add_object (obj, n->data); + + if (n->next) + g_string_append_printf (obj->buffer, " %s", separator); + } + } + else if (required) + sql_render_set_error (obj); +} + +/** + * sql_render_add_list_with_func: + * @obj: a #SqlRender + * @required: + * @token: + * @list: (element-type GObject.Object): + * @separator: + * @function: (scope call): + * + * + **/ +void sql_render_add_list_with_func (SqlRender * obj, gboolean required, const gchar * token, + SqlList * list, const gchar * separator, SqlRenderFunc function) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (SQL_IS_LIST (list)); + + if (list) + { + GList * n; + sql_render_add_token (obj, token); + + for (n = sql_list_get_items (list); n; n = n->next) + { + function (n->data, obj); + + if (n->next) + g_string_append_printf (obj->buffer, " %s", separator); + } + } + else if (required) + sql_render_set_error (obj); +} + +/** + * sql_render_set_error: + * @obj: a #SqlRender + **/ +void sql_render_set_error (SqlRender * obj) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + obj->error = g_error_new ( + SQL_RENDER_LOG_DOMAIN + ,SQL_RENDER_ERROR + ,"An error ocurred during query render: [ %s ] <-- Error is here" + ,obj->buffer->str + ); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_DELIMITER = 1 +}; + +static void sql_render_set_property (SqlRender * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DELIMITER: + obj->delimiter = g_value_get_schar (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_render_get_property (SqlRender * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DELIMITER: + g_value_set_schar (value, obj->delimiter); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_render_init (SqlRender * obj) +{ + obj->buffer = NULL; + obj->error = NULL; + obj->custom_renderers = g_hash_table_new ( + g_direct_hash + ,g_direct_equal + ); +} + +static void sql_render_finalize (SqlRender * obj) +{ + g_hash_table_unref (obj->custom_renderers); + G_OBJECT_CLASS (sql_render_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_render_class_init (SqlRenderClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_render_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_render_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_render_get_property; + + g_object_class_install_property (k, PROP_DELIMITER, + g_param_spec_char ("delimiter" + ,_("Delimiter") + ,_("The character used for delimite the name of fields, tables...") + ,G_MININT8 ,G_MAXINT8 ,'`' + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); +} diff --git a/sql/sql-render.h b/sql/sql-render.h new file mode 100644 index 0000000..da29abf --- /dev/null +++ b/sql/sql-render.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_RENDER_H +#define SQL_RENDER_H + +#include + +#define SQL_RENDER_LOG_DOMAIN (g_quark_from_string ("SqlRender")) + +#define SQL_TYPE_RENDER (sql_render_get_type ()) +#define SQL_RENDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_RENDER, SqlRender)) +#define SQL_IS_RENDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_RENDER)) + +#define SQL_BUFFER_SIZE 500 + +typedef struct _SqlRender SqlRender; +typedef struct _SqlRenderClass SqlRenderClass; + +typedef void (* SqlRenderFunc) (gpointer obj, SqlRender * render); + +#include "sql-object.h" + +/** + * SqlRender: + * @custom_renderers: (element-type GType Sql.RenderFunc): + **/ +struct _SqlRender +{ + GObject parent; + GError * error; + GString * buffer; + gpointer object; + GSList * ancestors; + gpointer data; + GHashTable * custom_renderers; + gint8 delimiter; +}; + +struct _SqlRenderClass +{ + /* */ + GObjectClass parent; +}; + +/** + * SqlRenderError: + * @SQL_RENDER_ERROR: an error from an #SqlRender + * + * Identifies the error codes for #SqlRender + **/ +typedef enum +{ + SQL_RENDER_ERROR +} +SqlRenderError; + +GType sql_render_get_type (); +SqlRender * sql_render_new (gchar delimiter); +gchar * sql_render_get_string (SqlRender * obj, gpointer object, gpointer data, GError ** err); +void sql_render_register_function (SqlRender * obj, GType type, SqlRenderFunc function); +GSList * sql_render_get_ancestors (SqlRender * obj); +void sql_render_add_espace (SqlRender * obj); +void sql_render_printf (SqlRender * obj, const gchar * string, ...); +void sql_render_append (SqlRender * obj, const gchar * string); +void sql_render_append_with_delimiter (SqlRender * obj, const gchar * string, gchar delimiter); +void sql_render_add_token (SqlRender * obj, const gchar * token); +void sql_render_add_identifier (SqlRender * obj, const gchar * identifier); +void sql_render_add_object (SqlRender * obj, gpointer object); +void sql_render_add_item (SqlRender * obj, gboolean required, const gchar * token, gpointer item); +void sql_render_add_list (SqlRender * obj, gboolean required, const gchar * token, SqlList * list, const gchar * separator); +void sql_render_add_list_with_func (SqlRender * obj, gboolean required, const gchar * token, SqlList * list, const gchar * separator, SqlRenderFunc function); +void sql_render_set_error (SqlRender * obj); + +#endif diff --git a/sql/sql-select-field.c b/sql/sql-select-field.c new file mode 100644 index 0000000..f6915d9 --- /dev/null +++ b/sql/sql-select-field.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-select-field.h" + +/** + * SECTION: sql-select_field + * @Short_description: + * @Title: SqlSelectField + **/ +G_DEFINE_TYPE (SqlSelectField, sql_select_field, SQL_TYPE_OBJECT); + +SqlSelectField * sql_select_field_new () +{ + return g_object_new (SQL_TYPE_SELECT_FIELD, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_select_field_render (SqlSelectField * obj, SqlRender * render) +{ + sql_render_add_object (render, obj->expr); + + if (obj->alias) + { + sql_render_add_token (render, "AS"); + sql_render_add_identifier (render, obj->alias); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_EXPR = 1 + ,PROP_ALIAS +}; + +static void sql_select_field_set_property (SqlSelectField * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPR: + sql_object_remove (obj, obj->expr); + obj->expr = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_ALIAS: + g_free (obj->alias); + obj->alias = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_select_field_get_property (SqlSelectField * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPR: + g_value_set_object (value, obj->expr); + break; + case PROP_ALIAS: + g_value_set_string (value, obj->alias); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_select_field_init (SqlSelectField * obj) +{ + obj->expr = NULL; + obj->alias = NULL; +} + +static void sql_select_field_finalize (SqlSelectField * obj) +{ + g_free (obj->alias); + sql_object_remove (obj, obj->expr); + G_OBJECT_CLASS (sql_select_field_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_select_field_class_init (SqlSelectFieldClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_select_field_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_select_field_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_select_field_get_property; + SQL_OBJECT_CLASS (k)->render = (SqlRenderFunc) sql_select_field_render; + + g_object_class_install_property (k, PROP_EXPR, + sql_param_object ("expr" + ,"Expression" + ,"The expression" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_ALIAS, + g_param_spec_string ("alias" + ,"Alias" + ,"The alias for the expression" + ,NULL + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-select-field.h b/sql/sql-select-field.h new file mode 100644 index 0000000..61ffd7d --- /dev/null +++ b/sql/sql-select-field.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_SELECT_FIELD_H +#define SQL_SELECT_FIELD_H + +#include "sql-object.h" +#include "sql-expr.h" + +#define SQL_TYPE_SELECT_FIELD (sql_select_field_get_type ()) +#define SQL_SELECT_FIELD(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_SELECT_FIELD, SqlSelectField)) +#define SQL_IS_SELECT_FIELD(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_SELECT_FIELD)) + +typedef struct _SqlSelectField SqlSelectField; +typedef struct _SqlSelectFieldClass SqlSelectFieldClass; + +struct _SqlSelectField +{ + SqlObject parent; + SqlExpr * expr; + gchar * alias; +}; + +struct _SqlSelectFieldClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_select_field_get_type (); +SqlSelectField * sql_select_field_new (); + +#endif diff --git a/sql/sql-select-order.c b/sql/sql-select-order.c new file mode 100644 index 0000000..8735789 --- /dev/null +++ b/sql/sql-select-order.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-select-order.h" + +/** + * SECTION: sql-select-order + * @Short_description: + * @Title: SqlSelectOrder + **/ +G_DEFINE_TYPE (SqlSelectOrder, sql_select_order, SQL_TYPE_OBJECT); + +SqlSelectOrder * sql_select_order_new () +{ + return g_object_new (SQL_TYPE_SELECT_ORDER, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_select_order_render (SqlSelectOrder * obj, SqlRender * render) +{ + sql_render_add_object (render, obj->expr); + + if (obj->way == SQL_SELECT_ORDER_DESC) + sql_render_add_token (render, "DESC"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_EXPR = 1 + ,PROP_WAY +}; + +static void sql_select_order_set_property (SqlSelectOrder * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPR: + sql_object_remove (obj, obj->expr); + obj->expr = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_WAY: + obj->way = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_select_order_get_property (SqlSelectOrder * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPR: + g_value_set_object (value, obj->expr); + break; + case PROP_WAY: + g_value_set_enum (value, obj->way); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_select_order_init (SqlSelectOrder * obj) +{ + obj->expr = NULL; +} + +static void sql_select_order_finalize (SqlSelectOrder * obj) +{ + sql_object_remove (obj, obj->expr); + G_OBJECT_CLASS (sql_select_order_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_select_order_class_init (SqlSelectOrderClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_select_order_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_select_order_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_select_order_get_property; + SQL_OBJECT_CLASS (k)->render = (SqlRenderFunc) sql_select_order_render; + + g_object_class_install_property (k, PROP_EXPR, + sql_param_object ("expr" + ,"Expression" + ,"The expression" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_WAY, + g_param_spec_enum ("way" + ,"Way" + ,"The order way" + ,SQL_TYPE_SELECT_ORDER_WAY + ,SQL_SELECT_ORDER_ASC + ,G_PARAM_READWRITE + )); +} + +GType sql_select_order_way_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GEnumValue values[] = + { + {SQL_SELECT_ORDER_ASC ,"SQL_SELECT_ORDER_ASC" ,"asc" + },{SQL_SELECT_ORDER_DESC ,"SQL_SELECT_ORDER_DESC" ,"desc" + },{0, NULL, NULL} + }; + + type = g_enum_register_static + (g_intern_static_string ("SqlSelectOrderWay"), values); + } + + return type; +} diff --git a/sql/sql-select-order.h b/sql/sql-select-order.h new file mode 100644 index 0000000..6454c79 --- /dev/null +++ b/sql/sql-select-order.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_SELECT_ORDER_H +#define SQL_SELECT_ORDER_H + +#include "sql-object.h" +#include "sql-expr.h" + +#define SQL_TYPE_SELECT_ORDER (sql_select_order_get_type ()) +#define SQL_SELECT_ORDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_SELECT_ORDER, SqlSelectOrder)) +#define SQL_IS_SELECT_ORDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_SELECT_ORDER)) + +#define SQL_TYPE_SELECT_ORDER_WAY (sql_select_order_way_get_type ()) + +typedef struct _SqlSelectOrder SqlSelectOrder; +typedef struct _SqlSelectOrderClass SqlSelectOrderClass; + +/** + * SqlSelectOrderWay: + * @SQL_SELECT_ORDER_ASC: ascendent order + * @SQL_SELECT_ORDER_DESC: descendent order + **/ +typedef enum +{ + SQL_SELECT_ORDER_ASC + ,SQL_SELECT_ORDER_DESC +} +SqlSelectOrderWay; + +struct _SqlSelectOrder +{ + SqlObject parent; + SqlExpr * expr; + SqlSelectOrderWay way; +}; + +struct _SqlSelectOrderClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_select_order_get_type (); +GType sql_select_order_way_get_type (); + +SqlSelectOrder * sql_select_order_new (); + +#endif diff --git a/sql/sql-select.c b/sql/sql-select.c new file mode 100644 index 0000000..dfc4179 --- /dev/null +++ b/sql/sql-select.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-select.h" + +/** + * SECTION: sql-select + * @Short_description: an SQL SELECT statement + * @Title: SqlSelect + * + * This object represents a SELECT SQL statement + **/ +G_DEFINE_TYPE (SqlSelect, sql_select, SQL_TYPE_DML); + +SqlObject * sql_select_new () +{ + return g_object_new (SQL_TYPE_SELECT, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static const char * SQL_SELECT_TYPE[] = +{ + "" + ,"UNION ALL" + ,"UNION ANY" + ,"INTERSECT" + ,"EXCEPT" +}; + +static void sql_select_render (SqlSelect * obj, SqlRender * render) +{ + sql_render_add_list (render, TRUE, "SELECT", obj->fields, ","); + sql_render_add_list (render, FALSE, "FROM", SQL_DML (obj)->targets, ","); + + if (SQL_DML (obj)->targets) + { + sql_render_add_item (render, FALSE, "WHERE", SQL_DML (obj)->where); + sql_render_add_list (render, FALSE, "GROUP BY", obj->group, ","); + sql_render_add_item (render, FALSE, "HAVING", obj->having); + sql_render_add_list (render, FALSE, "ORDER", obj->order, ","); + + if (obj->limit_count) + sql_render_printf (render, "LIMIT %u OFFSET %u" + ,obj->limit_count + ,obj->limit_offset + ); + } + + if (obj->next) + sql_render_add_item (render, FALSE, SQL_SELECT_TYPE[obj->type], obj->next); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_select_add_expr (SqlSelect * obj, SqlExpr * expr) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + g_return_if_fail (SQL_IS_EXPR (expr)); +} + +void sql_select_add_group (SqlSelect * obj, SqlExpr * expr) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + g_return_if_fail (SQL_IS_EXPR (expr)); +} + +void sql_select_set_having (SqlSelect * obj, SqlExpr * having) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + g_return_if_fail (SQL_IS_EXPR (having) || having); + + sql_object_remove (obj, obj->having); + obj->having = sql_object_add (obj, having); +} + +void sql_select_add_order (SqlSelect * obj, SqlExpr * expr, SqlSelectOrderWay way) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + g_return_if_fail (SQL_IS_EXPR (expr)); +} + +void sql_select_set_distinct (SqlSelect * obj, gboolean distinct) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + + obj->distinct = distinct; +} + +void sql_select_set_limit (SqlSelect * obj, guint count, guint offset) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + + obj->limit_count = count; + obj->limit_offset = offset; +} + +void sql_select_set_next (SqlSelect * obj, SqlSelect * next, SqlSelectType type) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + g_return_if_fail (SQL_IS_SELECT (next) || !next); + + sql_object_remove (obj, obj->next); + obj->next = sql_object_add (obj, next); + obj->type = next ? type : SQL_SELECT_NONE; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_DISTINCT = 1 + ,PROP_FIELDS + ,PROP_GROUP + ,PROP_HAVING + ,PROP_ORDER + ,PROP_LIMIT_COUNT + ,PROP_LIMIT_OFFSET + ,PROP_TYPE + ,PROP_NEXT +}; + +static void sql_select_set_property (SqlSelect * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DISTINCT: + obj->distinct = g_value_get_boolean (value); + break; + case PROP_FIELDS: + sql_object_remove (obj, obj->fields); + obj->fields = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_GROUP: + sql_object_remove (obj, obj->group); + obj->group = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_HAVING: + sql_select_set_having (obj, g_value_get_object (value)); + break; + case PROP_ORDER: + sql_object_remove (obj, obj->order); + obj->order = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_LIMIT_COUNT: + obj->limit_count = g_value_get_uint (value); + break; + case PROP_LIMIT_OFFSET: + obj->limit_offset = g_value_get_uint (value); + break; + case PROP_NEXT: + sql_select_set_next (obj, g_value_get_object (value), obj->type); + break; + case PROP_TYPE: + obj->type = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_select_get_property (SqlSelect * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DISTINCT: + g_value_set_boolean (value, obj->distinct); + break; + case PROP_FIELDS: + g_value_set_object (value, obj->fields); + break; + case PROP_GROUP: + g_value_set_object (value, obj->group); + break; + case PROP_HAVING: + g_value_set_object (value, obj->having); + break; + case PROP_ORDER: + g_value_set_object (value, obj->order); + break; + case PROP_LIMIT_COUNT: + g_value_set_uint (value, obj->limit_count); + break; + case PROP_LIMIT_OFFSET: + g_value_set_uint (value, obj->limit_offset); + break; + case PROP_NEXT: + g_value_set_object (value, obj->next); + break; + case PROP_TYPE: + g_value_set_int (value, obj->type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_select_init (SqlSelect * obj) +{ + obj->distinct = FALSE; + obj->fields = NULL; + obj->group = NULL; + obj->having = NULL; + obj->order = NULL; + obj->next = NULL; + obj->type = SQL_SELECT_NONE; +} + +static void sql_select_finalize (SqlSelect * obj) +{ + g_object_unref (obj->group); + g_object_unref (obj->order); + sql_object_remove (obj, obj->fields); + sql_object_remove (obj, obj->having); + sql_object_remove (obj, obj->next); + G_OBJECT_CLASS (sql_select_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_select_class_init (SqlSelectClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_select_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_select_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_select_get_property; + SQL_OBJECT_CLASS (k)->render = (SqlRenderFunc) sql_select_render; + + g_object_class_install_property (k, PROP_LIMIT_COUNT, + g_param_spec_boolean ("distinct" + ,"Distinct" + ,"Determines if the #SqlSelect uses the DISTINCT clause" + ,FALSE + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_FIELDS, + sql_param_list ("fields" + ,"Fields" + ,"The list of fields" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_GROUP, + sql_param_object ("group" + ,"Group" + ,"The GROUP BY section" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_HAVING, + sql_param_object ("having" + ,"Having" + ,"The HAVING clause" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_ORDER, + sql_param_list ("order" + ,"Order" + ,"The ORDER BY section" + ,SQL_TYPE_SELECT_ORDER + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_LIMIT_COUNT, + g_param_spec_uint ("limit-count" + ,"Limit count" + ,"The COUNT field of the LIMIT clause" + ,0, G_MAXUINT, 0 + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_LIMIT_OFFSET, + g_param_spec_uint ("limit-offset" + ,"Limit offset" + ,"The OFFSET field of the LIMIT clause" + ,0, G_MAXUINT, 0 + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_NEXT, + sql_param_object ("next" + ,"Next" + ,"The next #SqlSelect in case of a statement with more than one" + ,SQL_TYPE_SELECT + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_TYPE, + g_param_spec_int ("type" + ,"Type" + ,"One of the possible options of #SqlSelectType" + ,0, SQL_SELECT_COUNT - 1, 0 + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-select.h b/sql/sql-select.h new file mode 100644 index 0000000..420deb3 --- /dev/null +++ b/sql/sql-select.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_SELECT_H +#define SQL_SELECT_H + +#include "sql-dml.h" +#include "sql-select-field.h" +#include "sql-select-order.h" + +#define SQL_TYPE_SELECT (sql_select_get_type ()) +#define SQL_SELECT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_SELECT, SqlSelect)) +#define SQL_IS_SELECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_SELECT)) + +typedef struct _SqlSelect SqlSelect; +typedef struct _SqlSelectClass SqlSelectClass; + +typedef enum +{ + SQL_SELECT_NONE + ,SQL_SELECT_UNION_ALL + ,SQL_SELECT_UNION_ANY + ,SQL_SELECT_INTERSECT + ,SQL_SELECT_EXCEPT + ,SQL_SELECT_COUNT +} +SqlSelectType; + +/** + * SqlSelect: + * @expr: (element-type Sql.Expr): + * @group: (element-type Sql.Expr): + * @order: (element-type Sql.Expr): + **/ +struct _SqlSelect +{ + SqlDml parent; + gboolean distinct; + SqlList * fields; + SqlList * group; + SqlExpr * having; + SqlList * order; + guint limit_offset; + guint limit_count; + + SqlSelect * next; + SqlSelectType type; +}; + +struct _SqlSelectClass +{ + /* */ + SqlDmlClass parent; +}; + +GType sql_select_get_type (); +SqlObject * sql_select_new (); +void sql_select_set_distinct (SqlSelect * obj, gboolean distinct); +void sql_select_add_expr (SqlSelect * obj, SqlExpr * expr); +void sql_select_set_alias (SqlSelect * obj, SqlExpr * expr, const gchar * alias); +void sql_select_add_group (SqlSelect * obj, SqlExpr * expr); +void sql_select_set_having (SqlSelect * obj, SqlExpr * expr); +void sql_select_add_order (SqlSelect * obj, SqlExpr * expr, SqlSelectOrderWay way); +void sql_select_set_limit (SqlSelect * obj, guint count, guint offset); +void sql_select_set_next (SqlSelect * obj, SqlSelect * next, SqlSelectType type); + +#endif diff --git a/sql/sql-set.c b/sql/sql-set.c new file mode 100644 index 0000000..521b6fc --- /dev/null +++ b/sql/sql-set.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-set.h" + +/** + * SECTION: sql-set + * @Short_description: represents any set used in SQL. + * @Title: SqlSet + **/ +G_DEFINE_TYPE (SqlSet, sql_set, SQL_TYPE_EXPR); + +SqlSet * sql_set_new () +{ + return g_object_new (SQL_TYPE_SET, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_set_render (SqlSet * obj, SqlRender * render) +{ +/* if (obj) + sql_render_add_object (render, obj); + else + sql_render_add_token (render, "DEFAULT"); +*/ + sql_render_append (render, "("); + sql_render_add_list (render, FALSE, NULL, obj->exprs, ","); + sql_render_append (render, ")"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_EXPRS = 1 +}; + +static void sql_set_set_property (SqlSet * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPRS: + sql_object_remove (obj, obj->exprs); + obj->exprs = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_set_get_property (SqlSet * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPRS: + g_value_set_object (value, obj->exprs); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_set_init (SqlSet * obj) +{ + obj->exprs = NULL; +} + +static void sql_set_finalize (SqlSet * obj) +{ + sql_object_remove (obj, obj->exprs); + G_OBJECT_CLASS (sql_set_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_set_class_init (SqlSetClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_set_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_set_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_set_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_set_render; + + g_object_class_install_property (k, PROP_EXPRS, + sql_param_list ("exprs" + ,"Expressions" + ,"The list of expressions" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-set.h b/sql/sql-set.h new file mode 100644 index 0000000..14419e6 --- /dev/null +++ b/sql/sql-set.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_SET_H +#define SQL_SET_H + +#include "sql-expr.h" + +#define SQL_TYPE_SET (sql_set_get_type ()) +#define SQL_SET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_SET, SqlSet)) +#define SQL_IS_SET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_SET)) + +typedef struct _SqlSet SqlSet; +typedef struct _SqlSetClass SqlSetClass; + +struct _SqlSet +{ + SqlExpr parent; + SqlList * exprs; +}; + +struct _SqlSetClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_set_get_type (); +SqlSet * sql_set_new (); + +#endif diff --git a/sql/sql-stmt.c b/sql/sql-stmt.c new file mode 100644 index 0000000..712a089 --- /dev/null +++ b/sql/sql-stmt.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-stmt.h" + +/** + * SECTION: sql-stmt + * @Short_description: any kind of SQL statement + * @Title: SqlStmt + * + * Any kind of SQL statement including groups of multiple #SqlStmt as an + * @SqlMultiStmt, all the SQL DML statements (#SqlDml) including but as a + * separated object the INSERT statement (#SqlStmt) and arbitrary SQL strings + * in the form of #SqlString. + **/ +G_DEFINE_ABSTRACT_TYPE (SqlStmt, sql_stmt, SQL_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_stmt_init (SqlStmt * obj) {} + +static void sql_stmt_class_init (SqlStmtClass * klass) {} diff --git a/sql/sql-stmt.h b/sql/sql-stmt.h new file mode 100644 index 0000000..adaf930 --- /dev/null +++ b/sql/sql-stmt.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_STMT_H +#define SQL_STMT_H + +#include "sql-object.h" + +#define SQL_TYPE_STMT (sql_stmt_get_type ()) +#define SQL_STMT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_STMT, SqlStmt)) +#define SQL_IS_STMT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_STMT)) +#define SQL_STMT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, SQL_TYPE_STMT, SqlStmtClass)) +#define SQL_STMT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, SQL_TYPE_STMT, SqlStmtClass)) + +typedef struct _SqlStmt SqlStmt; +typedef struct _SqlStmtClass SqlStmtClass; + +struct _SqlStmt +{ + SqlObject parent; +}; + +struct _SqlStmtClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_stmt_get_type (); + +#endif diff --git a/sql/sql-string.c b/sql/sql-string.c new file mode 100644 index 0000000..8d119b1 --- /dev/null +++ b/sql/sql-string.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-string.h" +#include "sql-holder.h" + +typedef struct +{ + gchar * start; + gchar * end; + SqlHolder * holder; +} +HolderData; + +/** + * SECTION: sql-string + * @Short_description: an arbitrary SQL string + * @Title: SqlString + * + * An arbitrary SQL string. + **/ +G_DEFINE_TYPE (SqlString, sql_string, SQL_TYPE_STMT); + +SqlString * sql_string_new (const gchar * sql) +{ + return g_object_new (SQL_TYPE_STRING, "sql", sql, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_string_render (SqlString * obj, SqlRender * render) +{ + GSList * i; + gchar * ptr = obj->sql; + + for (i = obj->holders; i; i = i->next) + { + HolderData * holder_data = i->data; + g_string_append_len (render->buffer, ptr, holder_data->start - ptr - 1); + ptr = holder_data->end; + + sql_render_add_object (render, holder_data->holder); + } + + sql_render_append (render, ptr); +} + +static void sql_string_free_holder_data (HolderData * holder_data) +{ + g_object_unref (holder_data->holder); + g_free (holder_data); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * sql_string_add_param: + * @obj: the #SqlString + * @id: the id assigned to the item + **/ +void sql_string_add_param (SqlString * obj, const gchar * id, GvnParam * param) +{ + g_return_if_fail (SQL_IS_STRING (obj)); + g_return_if_fail (GVN_IS_PARAM (param)); + + SqlExpr * value = sql_value_new (); + sql_value_set_param (SQL_VALUE (value), param); + sql_object_add_held_object (SQL_OBJECT (obj), id, SQL_OBJECT (value)); + g_object_unref (value); +} + +/** + * sql_string_add_value: + * @obj: the #SqlString + * @id: the id assigned to the item + **/ +void sql_string_add_value (SqlString * obj, const gchar * id, GType type, gpointer content) +{ + g_return_if_fail (SQL_IS_STRING (obj)); + + GValue gvalue = {0}; + SqlExpr * value; + + gvn_value_new_with_content (&gvalue, type, content); + value = sql_value_new (); + sql_value_set_value (SQL_VALUE (value), &gvalue); + sql_object_add_held_object (SQL_OBJECT (obj), id, SQL_OBJECT (value)); + g_object_unref (value); + g_value_unset (&gvalue); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_SQL = 1 +}; + +static void sql_string_set_property (SqlString * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SQL: + { + gchar * i; + + obj->sql = g_value_dup_string (value); + + if (obj->sql) + for (i = obj->sql; ; i++) + { + switch (*i) + { + case '#': + { + gchar * ptr = ++i; + gchar * holder_id; + HolderData * holder_data; + + while (g_ascii_isalpha (*i)) + i++; + + holder_data = g_new (HolderData, 1); + holder_data->start = ptr; + holder_data->end = i; + obj->holders = g_slist_prepend (obj->holders, holder_data); + + holder_id = g_strndup (ptr, i - ptr); + holder_data->holder = sql_holder_new (holder_id); + g_free (holder_id); + + break; + } + case '\'': + case '"': + case '`': + { + gchar delimiter = *i; + + while (*(++i) != delimiter) + if (*i == '\\') + i++; + + break; + } + } + + if (*i == '\0') + break; + } + + obj->holders = g_slist_reverse (obj->holders); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_string_get_property (SqlString * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SQL: + g_value_set_string (value, obj->sql); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_string_init (SqlString * obj) +{ + obj->holders = NULL; +} + +static void sql_string_finalize (SqlString * obj) +{ + g_free (obj->sql); + g_slist_free_full (obj->holders, + (GDestroyNotify) sql_string_free_holder_data); + G_OBJECT_CLASS (sql_string_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_string_class_init (SqlStringClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) sql_string_finalize; + klass->set_property = (GObjectSetPropertyFunc) sql_string_set_property; + klass->get_property = (GObjectGetPropertyFunc) sql_string_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_string_render; + + g_object_class_install_property (klass, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("An arbitrary SQL string") + ,NULL + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/sql/sql-string.h b/sql/sql-string.h new file mode 100644 index 0000000..75193d5 --- /dev/null +++ b/sql/sql-string.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_STRING_H +#define SQL_STRING_H + +#include +#include +#include "sql-stmt.h" +#include "sql-value.h" + +#define SQL_TYPE_STRING (sql_string_get_type ()) +#define SQL_IS_STRING(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_STRING)) +#define SQL_STRING(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_STRING, SqlString)) + +typedef struct _SqlString SqlString; +typedef struct _SqlStringClass SqlStringClass; + +/** + * SqlString: + * @expr: (element-type Sql.Expr): + **/ +struct _SqlString +{ + SqlStmt parent; + gchar * sql; + GSList * holders; +}; + +struct _SqlStringClass +{ + /* */ + SqlStmtClass parent; +}; + +GType sql_string_get_type (); +SqlString * sql_string_new (const gchar * sql); +void sql_string_add_param (SqlString * obj, const gchar * id, GvnParam * param); +void sql_string_add_value (SqlString * obj, const gchar * id, GType type, gpointer content); + +#endif diff --git a/sql/sql-subquery.c b/sql/sql-subquery.c new file mode 100644 index 0000000..c7cdbf0 --- /dev/null +++ b/sql/sql-subquery.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-subquery.h" + +/** + * SECTION: sql-subquery + * @Short_description: an #SqlTarget containing an #SqlSelect + * @Title: SqlSubquery + * + * An #SqlTarget containing an #SqlSelect, used to add a sub-query to the + * targets field in an SQL query. + **/ +G_DEFINE_TYPE (SqlSubquery, sql_subquery, SQL_TYPE_TARGET); + +SqlSubquery * sql_subquery_new (SqlSelect * select) +{ + return g_object_new (SQL_TYPE_SUBQUERY, "select", select, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_subquery_render (SqlSubquery * obj, SqlRender * render) +{ + if (obj->select) + { + sql_render_append (render, "("); + sql_render_add_item (render, TRUE, NULL, obj->select); + sql_render_append (render, ")"); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_subquery_set_select (SqlSubquery * obj, SqlSelect * select) +{ + g_return_if_fail (SQL_IS_SUBQUERY (obj)); + g_return_if_fail (SQL_IS_SELECT (select) || !select); + + sql_object_remove (obj, obj->select); + obj->select = sql_object_add (obj, select); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_SELECT = 1 +}; + +static void sql_subquery_set_property (SqlSubquery * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SELECT: + sql_subquery_set_select (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_subquery_get_property (SqlSubquery * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SELECT: + g_value_set_object (value, obj->select); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_subquery_init (SqlSubquery * obj) +{ + obj->select = NULL; +} + +static void sql_subquery_finalize (SqlSubquery * obj) +{ + sql_object_remove (obj, obj->select); + G_OBJECT_CLASS (sql_subquery_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_subquery_class_init (SqlSubqueryClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_subquery_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_subquery_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_subquery_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_subquery_render; + + g_object_class_install_property (k, PROP_SELECT, + sql_param_object ("select" + ,"Select" + ,"The SELECT statement" + ,SQL_TYPE_SELECT + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-subquery.h b/sql/sql-subquery.h new file mode 100644 index 0000000..24581fc --- /dev/null +++ b/sql/sql-subquery.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_SUBQUERY_H +#define SQL_SUBQUERY_H + +#include "sql-target.h" +#include "sql-select.h" + +#define SQL_TYPE_SUBQUERY (sql_subquery_get_type ()) +#define SQL_IS_SUBQUERY(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_SUBQUERY)) +#define SQL_SUBQUERY(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_SUBQUERY, SqlSubquery)) + +typedef struct _SqlSubquery SqlSubquery; +typedef struct _SqlSubqueryClass SqlSubqueryClass; + +struct _SqlSubquery +{ + SqlTarget parent; + SqlSelect * select; +}; + +struct _SqlSubqueryClass +{ + /* */ + SqlTargetClass parent; +}; + +GType sql_subquery_get_type (); +SqlSubquery * sql_subquery_new (SqlSelect * select); +void sql_subquery_set_select (SqlSubquery * obj, SqlSelect * select); + +#endif + diff --git a/sql/sql-table.c b/sql/sql-table.c new file mode 100644 index 0000000..6ef7056 --- /dev/null +++ b/sql/sql-table.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-table.h" + +/** + * SECTION: sql-table + * @Short_description: a qualified table name. + * @Title: SqlTable + * + * A qualified table name. + **/ +G_DEFINE_TYPE (SqlTable, sql_table, SQL_TYPE_TARGET); + +SqlObject * sql_table_new (const gchar * name) +{ + return g_object_new (SQL_TYPE_TABLE, "name", name, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_table_render (SqlTable * obj, SqlRender * render) +{ + if (obj->schema) + { + sql_render_add_identifier (render, obj->schema); + sql_render_append (render, "."); + } + + sql_render_add_identifier (render, obj->name); + + if (g_strcmp0 (obj->name, SQL_TARGET (obj)->alias)) + sql_render_add_identifier (render, SQL_TARGET (obj)->alias); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_NAME = 1 + ,PROP_SCHEMA +}; + +static void sql_table_set_property (SqlTable * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_free (obj->name); + obj->name = g_value_dup_string (value); + break; + case PROP_SCHEMA: + g_free (obj->schema); + obj->schema = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_table_get_property (SqlTable * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_value_set_string (value, obj->name); + break; + case PROP_SCHEMA: + g_value_set_string (value, obj->schema); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_table_init (SqlTable * obj) +{ + obj->name = NULL; + obj->schema = NULL; +} + +static void sql_table_finalize (SqlTable * obj) +{ + g_free (obj->name); + g_free (obj->schema); + G_OBJECT_CLASS (sql_table_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_table_class_init (SqlTableClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_table_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_table_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_table_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_table_render; + + g_object_class_install_property (k, PROP_NAME, + g_param_spec_string ("name" + , "Name" + , "The table name" + , NULL, G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_SCHEMA, + g_param_spec_string ("schema" + ,"Schema" + ,"The schema where the table is" + ,NULL, G_PARAM_READWRITE + )); +} diff --git a/sql/sql-table.h b/sql/sql-table.h new file mode 100644 index 0000000..9544872 --- /dev/null +++ b/sql/sql-table.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_TABLE_H +#define SQL_TABLE_H + +#include "sql-target.h" + +#define SQL_TYPE_TABLE (sql_table_get_type ()) +#define SQL_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_TABLE, SqlTable)) +#define SQL_IS_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_TABLE)) + +typedef struct _SqlTable SqlTable; +typedef struct _SqlTableClass SqlTableClass; + +struct _SqlTable +{ + SqlTarget parent; + gchar * name; + gchar * schema; +}; + +struct _SqlTableClass +{ + /* */ + SqlTargetClass parent; +}; + +GType sql_table_get_type (); +SqlObject * sql_table_new (const gchar * name); + +#endif diff --git a/sql/sql-target.c b/sql/sql-target.c new file mode 100644 index 0000000..2dd8ace --- /dev/null +++ b/sql/sql-target.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-target.h" + +/** + * SECTION: sql-target + * @Short_description: any target for an SQL statement + * @Title: SqlTarget + * + * A target that can be used as such in any SQL statement (here represented by + * the #SqlStmt class). + **/ +G_DEFINE_ABSTRACT_TYPE (SqlTarget, sql_target, SQL_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_target_set_alias (SqlTarget * obj, const gchar * alias) +{ + g_return_if_fail (SQL_IS_TARGET (obj)); + + g_free (obj->alias); + obj->alias = g_strdup (alias); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_ALIAS = 1 +}; + +static void sql_target_set_property (SqlTarget * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ALIAS: + sql_target_set_alias (obj, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_target_get_property (SqlTarget * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ALIAS: + g_value_set_string (value, obj->alias); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_target_init (SqlTarget * obj) +{ + obj->alias = NULL; +} + +static void sql_target_finalize (SqlTarget * obj) +{ + g_free (obj->alias); + G_OBJECT_CLASS (sql_target_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_target_class_init (SqlTargetClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) sql_target_finalize; + klass->set_property = (GObjectSetPropertyFunc) sql_target_set_property; + klass->get_property = (GObjectGetPropertyFunc) sql_target_get_property; + + g_object_class_install_property (klass, PROP_ALIAS, + g_param_spec_string ("alias" + ,"Alias" + ,"The alias for the target" + ,NULL + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-target.h b/sql/sql-target.h new file mode 100644 index 0000000..43a20e4 --- /dev/null +++ b/sql/sql-target.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_TARGET_H +#define SQL_TARGET_H + +#include "sql-object.h" + +#define SQL_TYPE_TARGET (sql_target_get_type ()) +#define SQL_TARGET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_TARGET, SqlTarget)) +#define SQL_IS_TARGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_TARGET)) + +typedef struct _SqlTarget SqlTarget; +typedef struct _SqlTargetClass SqlTargetClass; + +struct _SqlTarget +{ + SqlObject parent; + gchar * alias; +}; + +struct _SqlTargetClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_target_get_type (); +void sql_target_set_alias (SqlTarget * obj, const gchar * alias); + +#endif \ No newline at end of file diff --git a/sql/sql-update-set.c b/sql/sql-update-set.c new file mode 100644 index 0000000..2b204e1 --- /dev/null +++ b/sql/sql-update-set.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-update-set.h" + +/** + * SECTION: sql-update_set + * @Short_description: Defines a field for the SET clause for an #SqlUpdate. + * @Title: SqlUpdateSet + **/ +G_DEFINE_TYPE (SqlUpdateSet, sql_update_set, SQL_TYPE_OBJECT); + +SqlUpdateSet * sql_update_set_new () +{ + return g_object_new (SQL_TYPE_UPDATE_SET, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_update_set_render (SqlUpdateSet * set, SqlRender * render) +{ + sql_render_add_object (render, set->field); + sql_render_add_token (render, "="); + sql_render_add_object (render, set->expr); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_FIELD = 1 + ,PROP_EXPR +}; + +static void sql_update_set_set_property (SqlUpdateSet * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_FIELD: + sql_object_remove (obj, obj->field); + obj->field = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_EXPR: + sql_object_remove (obj, obj->expr); + obj->expr = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_update_set_get_property (SqlUpdateSet * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_FIELD: + g_value_set_object (value, obj->field); + break; + case PROP_EXPR: + g_value_set_object (value, obj->expr); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_update_set_init (SqlUpdateSet * obj) +{ + obj->field = NULL; + obj->expr = NULL; +} + +static void sql_update_set_finalize (SqlUpdateSet * obj) +{ + sql_object_remove (obj, obj->field); + sql_object_remove (obj, obj->expr); + G_OBJECT_CLASS (sql_update_set_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_update_set_class_init (SqlUpdateSetClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_update_set_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_update_set_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_update_set_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_update_set_render; + + g_object_class_install_property (k, PROP_FIELD, + g_param_spec_object ("field" + ,"Field" + ,"The field" + ,SQL_TYPE_FIELD + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_EXPR, + g_param_spec_object ("expr" + ,"Expression" + ,"The expression" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-update-set.h b/sql/sql-update-set.h new file mode 100644 index 0000000..ffa1037 --- /dev/null +++ b/sql/sql-update-set.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_UPDATE_SET_H +#define SQL_UPDATE_SET_H + +#include "sql-object.h" +#include "sql-field.h" +#include "sql-expr.h" + +#define SQL_TYPE_UPDATE_SET (sql_update_set_get_type ()) +#define SQL_UPDATE_SET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_UPDATE_SET, SqlUpdateSet)) +#define SQL_IS_UPDATE_SET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_UPDATE_SET)) + +typedef struct _SqlUpdateSet SqlUpdateSet; +typedef struct _SqlUpdateSetClass SqlUpdateSetClass; + +struct _SqlUpdateSet +{ + SqlObject parent; + SqlField * field; + SqlExpr * expr; +}; + +struct _SqlUpdateSetClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_update_set_get_type (); +SqlUpdateSet * sql_update_set_new (); + +#endif diff --git a/sql/sql-update.c b/sql/sql-update.c new file mode 100644 index 0000000..b582722 --- /dev/null +++ b/sql/sql-update.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "sql-update.h" + +/** + * SECTION: sql-update + * @Short_description: an SQL UPDATE statement + * @Title: SqlUpdate + * + * Represents an SQL UPDATE statement. + **/ +G_DEFINE_TYPE (SqlUpdate, sql_update, SQL_TYPE_DML); + +SqlObject * sql_update_new () +{ + return g_object_new (SQL_TYPE_UPDATE, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_update_render (SqlUpdate * obj, SqlRender * render) +{ + sql_render_add_list (render, TRUE, "UPDATE", SQL_DML (obj)->targets, ","); + + if (SQL_DML (obj)->targets) + { + sql_render_add_list (render, TRUE, "SET", obj->sets, ","); + sql_render_add_item (render, FALSE, "WHERE", SQL_DML (obj)->where); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_update_add_set (SqlUpdate * obj, SqlField * field, SqlExpr * expr) +{ + g_return_if_fail (SQL_IS_UPDATE (obj)); + g_return_if_fail (SQL_IS_FIELD (field) && SQL_IS_EXPR (expr)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_SETS = 1 +}; + +static void sql_update_set_property (SqlUpdate * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SETS: + sql_object_remove (obj, obj->sets); + obj->sets = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_update_get_property (SqlUpdate * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SETS: + g_value_set_object (value, obj->sets); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_update_init (SqlUpdate * obj) +{ + obj->sets = NULL; +} + +static void sql_update_finalize (SqlUpdate * obj) +{ + sql_object_remove (obj, obj->sets); + G_OBJECT_CLASS (sql_update_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_update_class_init (SqlUpdateClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_update_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_update_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_update_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_update_render; + + g_object_class_install_property (k, PROP_SETS, + sql_param_list ("sets" + ,"Sets" + ,"A list of sets" + ,SQL_TYPE_UPDATE_SET + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-update.h b/sql/sql-update.h new file mode 100644 index 0000000..cb2d472 --- /dev/null +++ b/sql/sql-update.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_UPDATE_H +#define SQL_UPDATE_H + +#include "sql-dml.h" +#include "sql-field.h" +#include "sql-update-set.h" + +#define SQL_TYPE_UPDATE (sql_update_get_type ()) +#define SQL_UPDATE(object) (G_TYPE_CHECK_INSTANCE_CAST (object, SQL_TYPE_UPDATE, SqlUpdate)) +#define SQL_IS_UPDATE(object) (G_TYPE_CHECK_INSTANCE_TYPE (object, SQL_TYPE_UPDATE)) + +typedef struct _SqlUpdate SqlUpdate; +typedef struct _SqlUpdateClass SqlUpdateClass; + +/** + * SqlUpdate: + **/ +struct _SqlUpdate +{ + SqlDml parent; + SqlList * sets; // List of SqlUpdateSet +}; + +struct _SqlUpdateClass +{ + /* */ + SqlDmlClass parent; +}; + + +GType sql_update_get_type (); +SqlObject * sql_update_new (); +void sql_update_add_set (SqlUpdate * obj, SqlField * field, SqlExpr * expr); + +#endif diff --git a/sql/sql-value.c b/sql/sql-value.c new file mode 100644 index 0000000..7940aa5 --- /dev/null +++ b/sql/sql-value.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include +#include "sql-value.h" + +/** + * SECTION: sql-value + * @Short_description: generic value + * @Title: SqlValue + * + * A generic value that can be used in operations (#SqlOperation) and of course + * in any place where an #SqlExpr can be used. + **/ +G_DEFINE_TYPE (SqlValue, sql_value, SQL_TYPE_EXPR); + +SqlObject * sql_value_new () +{ + return g_object_new (SQL_TYPE_VALUE, NULL); +} + +SqlObject * sql_value_new_with_value (const GValue * value) +{ + g_return_val_if_fail (value, NULL); + g_return_val_if_fail (G_IS_VALUE (value), NULL); + + return g_object_new (SQL_TYPE_VALUE, "value", value, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_value_render (SqlValue * obj, SqlRender * render) +{ + GValue * value = obj->value; + GType type = G_VALUE_TYPE (value); + + switch (type) + { + case G_TYPE_BOOLEAN: + sql_render_add_token (render, g_value_get_boolean (value) ? "TRUE" : "FALSE"); + break; + case G_TYPE_CHAR: + sql_render_printf (render, "'%c'", g_value_get_schar (value)); + break; + case G_TYPE_INT: + sql_render_printf (render, "%i", g_value_get_int (value)); + break; + case G_TYPE_UINT: + sql_render_printf (render, "%u", g_value_get_uint (value)); + break; + case G_TYPE_LONG: + sql_render_printf (render, "%ld", g_value_get_long (value)); + break; + case G_TYPE_ULONG: + sql_render_printf (render, "%lu", g_value_get_ulong (value)); + break; + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + { + gchar buffer[G_ASCII_DTOSTR_BUF_SIZE]; + g_ascii_dtostr (buffer, G_ASCII_DTOSTR_BUF_SIZE, + (type == G_TYPE_FLOAT) + ?(gdouble) g_value_get_float (value) + :g_value_get_double (value) + ); + sql_render_add_espace (render); + sql_render_append (render, buffer); + break; + } + case G_TYPE_STRING: + sql_render_append_with_delimiter (render, g_value_get_string (value), '\''); + break; + default: + if (type == G_TYPE_DATE) + { + GDate * date = g_value_get_boxed (value); + sql_render_printf (render, "'%04i-%02i-%02i'" + ,g_date_get_year (date) + ,g_date_get_month (date) + ,g_date_get_day (date) + ); + } + else if (type == G_TYPE_DATE_TIME) + { + GDateTime * date_time = g_value_get_boxed (value); + sql_render_printf (render, g_date_time_format (date_time, "'%Y-%m-%d %T'")); + } + else if (type == GVN_TYPE_NULL) + sql_render_add_token (render, "NULL"); + else + sql_render_set_error (render); + } +} + +static gboolean sql_value_is_ready (SqlValue * obj) +{ + return !gvn_value_is_null (obj->value); +} + +static void sql_value_set_real_value (SqlValue * obj, const GValue * value) +{ + if (gvn_value_ccopy (value, obj->value)) + g_signal_emit_by_name (obj, "changed"); +} + +static void sql_value_cb_value_changed (GvnParam * param, const GValue * value, SqlValue * obj) +{ + sql_value_set_real_value (obj, value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +const GValue * sql_value_get_value (SqlValue * obj) +{ + g_return_val_if_fail (SQL_IS_VALUE (obj), NULL); + + return obj->value; +} + +void sql_value_set_value (SqlValue * obj, const GValue * value) +{ + g_return_if_fail (SQL_IS_VALUE (obj)); + g_return_if_fail (!obj->param); + + sql_value_set_real_value (obj, value); +} + +void sql_value_set_param (SqlValue * obj, GvnParam * param) +{ + g_return_if_fail (SQL_IS_VALUE (obj)); + g_return_if_fail (GVN_IS_PARAM (param) || !param); + + if (obj->param) + { + g_signal_handlers_disconnect_by_func (obj->param, + sql_value_cb_value_changed, obj); + g_clear_object (&obj->param); + } + if (param) + { + obj->param = g_object_ref_sink (param); + g_signal_connect (param, "value-changed", + G_CALLBACK (sql_value_cb_value_changed), obj); + sql_value_set_real_value (obj, gvn_param_get_value (param)); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_PARAM = 1 + ,PROP_VALUE +}; + +static void sql_value_set_property (SqlValue * obj, guint id, const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PARAM: + sql_value_set_param (obj, g_value_get_object (value)); + break; + case PROP_VALUE: + sql_value_set_value (obj, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_value_get_property (SqlValue * obj, guint id, GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PARAM: + g_value_set_object (value, obj->param); + break; + case PROP_VALUE: + g_value_set_boxed (value, obj->value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_value_init (SqlValue * obj) +{ + obj->param = NULL; + obj->value = g_new0 (GValue, 1); + g_value_init (obj->value, GVN_TYPE_NULL); +} + +static void sql_value_finalize (SqlValue * obj) +{ + sql_value_set_param (obj, NULL); + g_value_unset (obj->value); + g_free (obj->value); + G_OBJECT_CLASS (sql_value_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_value_class_init (SqlValueClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_value_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_value_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_value_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_value_render; + SQL_OBJECT_CLASS (klass)->is_ready = (SqlObjectIsReadyFunc) sql_value_is_ready; + + g_object_class_install_property (k, PROP_PARAM, + g_param_spec_object ("param" + ,_("Param") + ,_("The param which is linked") + ,GVN_TYPE_PARAM, G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_VALUE, + g_param_spec_boxed ("value" + ,_("Value") + ,_("The value") + ,G_TYPE_VALUE, G_PARAM_READWRITE + )); +} diff --git a/sql/sql-value.h b/sql/sql-value.h new file mode 100644 index 0000000..6b75448 --- /dev/null +++ b/sql/sql-value.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_VALUE_H +#define SQL_VALUE_H + +#include +#include "sql-expr.h" + +#define SQL_TYPE_VALUE (sql_value_get_type ()) +#define SQL_VALUE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_VALUE, SqlValue)) +#define SQL_IS_VALUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_VALUE)) + +typedef struct _SqlValue SqlValue; +typedef struct _SqlValueClass SqlValueClass; + +struct _SqlValue +{ + SqlExpr parent; + GValue * value; + GvnParam * param; +}; + +struct _SqlValueClass +{ + /* */ + SqlExprClass parent; +}; + +GType sql_value_get_type (); +SqlObject * sql_value_new (); +SqlObject * sql_value_new_with_value (const GValue * value); +const GValue * sql_value_get_value (SqlValue * obj); +void sql_value_set_value (SqlValue * obj, const GValue * value); +void sql_value_set_param (SqlValue * obj, GvnParam * param); + +#endif diff --git a/sql/sql.h b/sql/sql.h new file mode 100644 index 0000000..91c5d28 --- /dev/null +++ b/sql/sql.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef SQL_H +#define SQL_H + +#include +#include "sql-object.h" +#include "sql-holder.h" +#include "sql-list.h" +#include "sql-set.h" +#include "sql-string.h" +#include "sql-stmt.h" +#include "sql-multi-stmt.h" +#include "sql-select.h" +#include "sql-select-field.h" +#include "sql-select-order.h" +#include "sql-update.h" +#include "sql-update-set.h" +#include "sql-insert.h" +#include "sql-delete.h" +#include "sql-expr.h" +#include "sql-field.h" +#include "sql-value.h" +#include "sql-operation.h" +#include "sql-function.h" +#include "sql-target.h" +#include "sql-table.h" +#include "sql-join.h" +#include "sql-subquery.h" +#include "sql-render.h" +#include "sql-parser.h" + +#endif \ No newline at end of file diff --git a/sql/sql.pc.in b/sql/sql.pc.in new file mode 100644 index 0000000..ad446c8 --- /dev/null +++ b/sql/sql.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/hedera +includedir=@includedir@/hedera + +Name: Sql +Description: Sql Classes Module for Hedera Library +Version: @VERSION@ +Requires: gvn glib-2.0 gobject-2.0 +Libs: -L${libdir} -lsql +Cflags: -I${includedir}/sql diff --git a/template/lib-object.c b/template/lib-object.c new file mode 100644 index 0000000..2b488d9 --- /dev/null +++ b/template/lib-object.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "lib-object.h" + +/** + * SECTION: lib-object + * @Short_description: a template object + * @Title: LibObject + * + * Long description of the object. + */ +G_DEFINE_TYPE (LibObject, lib_object, G_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +/** + * lib_object_method: + * @obj: a #LibObject + * + * Description of method here. + * + * Return value: #void + **/ +void lib_object_method (LibObject * obj) +{ + +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_PROPERTY = 1 +}; + +static void lib_object_set_property (LibObject * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PROPERTY: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void lib_object_get_property (LibObject * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PROPERTY: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void lib_object_init (LibObject * obj) +{ + +} + +static void lib_object_finalize (LibObject * obj) +{ + G_OBJECT_CLASS (lib_object_parent_class)->finalize (G_OBJECT (obj)); +} + +static void lib_object_class_init (LibObjectClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) lib_object_finalize; + k->set_property = (GObjectSetPropertyFunc) lib_object_set_property; + k->get_property = (GObjectGetPropertyFunc) lib_object_get_property; + + g_object_class_install_property (k, PROP_FIRST, + g_param_spec_string ("property" + ,"Property" + ,"Description" + ,NULL + ,G_PARAM_READWRITE + )); +} diff --git a/template/lib-object.h b/template/lib-object.h new file mode 100644 index 0000000..6a47b47 --- /dev/null +++ b/template/lib-object.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef LIB_OBJECT_H +#define LIB_OBJECT_H + +#include + +#define LIB_TYPE_OBJECT (lib_object_get_type ()) +#define LIB_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, LIB_TYPE_OBJECT, LibObject)) +#define LIB_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, LIB_TYPE_OBJECT)) +#define LIB_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, LIB_TYPE_OBJECT, LibObjectClass)) +#define LIB_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, LIB_TYPE_OBJECT)) +#define LIB_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, LIB_TYPE_OBJECT, LibObjectClass)) + +typedef struct _LibObject LibObject; +typedef struct _LibObjectClass LibObjectClass; + +struct _LibObject +{ + GObject parent; +}; + +struct _LibObjectClass +{ + GObjectClass parent; +}; + +GType lib_object_get_type (); + +#endif \ No newline at end of file diff --git a/vapi/Db-1.0.metadata b/vapi/Db-1.0.metadata new file mode 100644 index 0000000..e4c464e --- /dev/null +++ b/vapi/Db-1.0.metadata @@ -0,0 +1,10 @@ +Db cheader_filename="db/db.h" + +Iter struct +Iter.*#method skip + +Row struct + +Model.get skip=false +Model.set skip=false +Model.search skip=false \ No newline at end of file diff --git a/vapi/Gvn-1.0.metadata b/vapi/Gvn-1.0.metadata new file mode 100644 index 0000000..0c3e898 --- /dev/null +++ b/vapi/Gvn-1.0.metadata @@ -0,0 +1,9 @@ +Gvn cheader_filename="gvn/gvn.h" + +value_new_with_content skip=false +value_get_valist skip=false + +Param.value_changed#method name="emit_value_changed" + +//FIXME +ParamSpec struct \ No newline at end of file diff --git a/vapi/Makefile.am b/vapi/Makefile.am new file mode 100644 index 0000000..d235411 --- /dev/null +++ b/vapi/Makefile.am @@ -0,0 +1,17 @@ +include $(top_srcdir)/Makefile.decl + +if ENABLE_VALA +vapi_DATA = \ + gvn.deps \ + sql.deps \ + db.deps \ + vn.deps \ + hedera.deps \ + hedera.vapi +EXTRA_DIST = \ + Db.metadata \ + Gvn.metadata \ + Sql.metadata \ + Vn.metadata \ + $(vapi_DATA) +endif diff --git a/vapi/Sql-1.0.metadata b/vapi/Sql-1.0.metadata new file mode 100644 index 0000000..aa7ddbf --- /dev/null +++ b/vapi/Sql-1.0.metadata @@ -0,0 +1 @@ +Sql cheader_filename="sql/sql.h" diff --git a/vapi/Vn-1.0.metadata b/vapi/Vn-1.0.metadata new file mode 100644 index 0000000..e931908 --- /dev/null +++ b/vapi/Vn-1.0.metadata @@ -0,0 +1,8 @@ +Vn cheader_filename="vn/vn.h" + +Builder.bind_fields skip=false +Builder.bind_columns skip=false + +Grid.append_columns skip=false + +Batch.objects skip \ No newline at end of file diff --git a/vapi/db.deps b/vapi/db.deps new file mode 100644 index 0000000..0272653 --- /dev/null +++ b/vapi/db.deps @@ -0,0 +1,2 @@ +sql +glib-2.0 \ No newline at end of file diff --git a/vapi/gvn.deps b/vapi/gvn.deps new file mode 100644 index 0000000..aaded57 --- /dev/null +++ b/vapi/gvn.deps @@ -0,0 +1 @@ +glib-2.0 \ No newline at end of file diff --git a/vapi/hedera.deps b/vapi/hedera.deps new file mode 100644 index 0000000..fb3fcbc --- /dev/null +++ b/vapi/hedera.deps @@ -0,0 +1 @@ +vn \ No newline at end of file diff --git a/vapi/hedera.vapi b/vapi/hedera.vapi new file mode 100644 index 0000000..e69de29 diff --git a/vapi/sql.deps b/vapi/sql.deps new file mode 100644 index 0000000..85946d5 --- /dev/null +++ b/vapi/sql.deps @@ -0,0 +1,2 @@ +gvn +glib-2.0 \ No newline at end of file diff --git a/vapi/vn.deps b/vapi/vn.deps new file mode 100644 index 0000000..47a66cf --- /dev/null +++ b/vapi/vn.deps @@ -0,0 +1,2 @@ +db +gtk+-3.0 \ No newline at end of file diff --git a/vn/Makefile.am b/vn/Makefile.am new file mode 100644 index 0000000..909060e --- /dev/null +++ b/vn/Makefile.am @@ -0,0 +1,137 @@ +include $(top_srcdir)/Makefile.decl + +SUBDIRS = \ + field \ + column + +vn_lib_LTLIBRARIES = libvn.la +vn_include_HEADERS = \ + vn.h \ + vn-builder.h \ + vn-field.h \ + vn-column.h \ + vn-login.h \ + vn-gui.h \ + vn-mod.h \ + vn-form.h \ + vn-model.h \ + vn-grid.h \ + vn-handler.h \ + vn-batch.h \ + field/field.h \ + column/column.h + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(DEFINES) \ + $(gdome_CFLAGS) \ + $(gtk_CFLAGS) +libvn_la_LIBADD = \ + $(gdome_LIBS) \ + $(gtk_LIBS) \ + $(top_builddir)/db/libdb.la \ + $(top_builddir)/vn/field/libvnfield.la \ + $(top_builddir)/vn/column/libvncolumn.la +libvn_la_SOURCES = \ + $(vn_include_HEADERS) \ + vn-builder.c \ + vn-field.c \ + vn-column.c \ + vn-login.c \ + vn-gui.c \ + vn-mod.c \ + vn-form.c \ + vn-model.c \ + vn-grid.c \ + vn-handler.c \ + vn-batch.c + +pkgconfig_DATA = vn.pc + +vn_xml_DATA = \ + schema/module.dtd +vn_image_DATA = \ + image/hedera16x16.xpm \ + image/hedera32x32.xpm \ + image/icon.svg \ + image/logo.svg \ + image/load.gif +vn_gui_DATA = \ + gui/login.glade \ + gui/main.glade \ + gui/child-window.glade \ + gui/actions.glade \ + gui/menubar.ui + +gsettings_SCHEMAS = $(top_srcdir)/vn/schema/$(PACKAGE).gschema.xml +@GSETTINGS_RULES@ + +DEFINES = \ + -D_GUI_DIR=\"$(vn_guidir)\" \ + -D_VN_MODULE_QUERY_DIR=\"$(module_querydir)/sql\" \ + -D_VN_MODULE_LIB_DIR=\"$(module_libdir)\" \ + -D_VN_MODULE_DATA_DIR=\"$(module_datadir)\" \ + -D_HEDERA_LOCALE_DIR=\"$(datadir)/locale\" \ + -D_DTD_DIR=\"$(vn_xmldir)\" + +EXTRA_DIST = vn.pc.in + +DISTCLEANFILES = vn.pc + +if ENABLE_VALA +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_SCANNER_ARGS = $(GIR_SCANNER_ARGS) + +INTROSPECTION_COMPILER_ARGS = \ + --includedir=$(top_builddir)/gvn \ + --includedir=$(top_builddir)/sql \ + --includedir=$(top_builddir)/db + +introspection_sources = \ + $(libvn_la_SOURCES) \ + $(top_srcdir)/vn/field/vn-*.h \ + $(top_srcdir)/vn/column/vn-*.h + +introspection_libs = \ + libvn.la \ + field/libvnfield.la \ + column/libvncolumn.la + +Vn-$(VERSION).gir: $(introspection_libs) $(top_builddir)/db/Db-$(VERSION).gir + Vn_@uVERSION@_gir_SCANNERFLAGS = \ + --include-uninstalled=$(top_builddir)/gvn/Gvn-$(VERSION).gir \ + --include-uninstalled=$(top_builddir)/sql/Sql-$(VERSION).gir \ + --include-uninstalled=$(top_builddir)/db/Db-$(VERSION).gir + Vn_@uVERSION@_gir_INCLUDES = Gtk-3.0 + Vn_@uVERSION@_gir_CFLAGS = -I$(top_srcdir) + Vn_@uVERSION@_gir_LIBS = $(introspection_libs) + Vn_@uVERSION@_gir_FILES = $(introspection_sources) + Vn_@uVERSION@_gir_EXPORT_PACKAGES = vn + INTROSPECTION_GIRS = Vn-$(VERSION).gir + +gir_DATA = $(INTROSPECTION_GIRS) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES = $(gir_DATA) $(typelib_DATA) + +$(vapis)/vn.vapi: $(INTROSPECTION_GIRS) $(vapidata)/Vn-$(VERSION).metadata + $(vapigen_v)$(VAPIGEN) -q \ + --pkg gtk+-3.0 \ + --directory $(vapis) \ + --vapidir $(vapis) \ + --girdir $(top_builddir)/gvn \ + --girdir $(top_builddir)/sql \ + --girdir $(top_builddir)/db \ + --metadatadir $(vapidata) \ + --library vn \ + Vn-$(VERSION).gir + +vapi_DATA = $(vapis)/vn.vapi + +CLEANFILES += $(vapis)/$(vapi_DATA) + +endif +endif diff --git a/vn/column/Makefile.am b/vn/column/Makefile.am new file mode 100644 index 0000000..64943e8 --- /dev/null +++ b/vn/column/Makefile.am @@ -0,0 +1,23 @@ +include $(top_srcdir)/Makefile.decl + +column_lib_LTLIBRARIES = libvncolumn.la +column_include_HEADERS = \ + column.h \ + vn-column-check.h \ + vn-column-combo.h \ + vn-column-entry.h \ + vn-column-image.h \ + vn-column-spin.h + +AM_CPPFLAGS = \ + -D_IMAGE_DIR=\"$(vn_imagedir)\" \ + -I$(top_srcdir) \ + $(gtk_CFLAGS) +libvncolumn_la_LIBADD = $(gtk_LIBS) +libvncolumn_la_SOURCES = \ + $(column_include_HEADERS) \ + vn-column-check.c \ + vn-column-combo.c \ + vn-column-entry.c \ + vn-column-image.c \ + vn-column-spin.c diff --git a/vn/column/column.h b/vn/column/column.h new file mode 100644 index 0000000..64d7e40 --- /dev/null +++ b/vn/column/column.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef COLUMN_H +#define COLUMN_H + +#include "vn-column-check.h" +#include "vn-column-combo.h" +#include "vn-column-entry.h" +#include "vn-column-image.h" +#include "vn-column-spin.h" + +#endif \ No newline at end of file diff --git a/vn/column/vn-column-check.c b/vn/column/vn-column-check.c new file mode 100644 index 0000000..ee2ccd3 --- /dev/null +++ b/vn/column/vn-column-check.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-column-check.h" + +G_DEFINE_TYPE (VnColumnCheck, vn_column_check, VN_TYPE_COLUMN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_column_check_cb_toggled (GtkCellRendererToggle * cell, + const gchar * path, VnColumn * obj) +{ + gint col; + DbIter iter; + DbModel * model; + GValue new_value = {0}; + const GValue * old_value; + + col = vn_column_get_column_index (obj); + model = vn_column_get_model (obj); + + vn_column_get_iter (obj, path, &iter); + old_value = db_model_get_value (model, &iter, col, NULL); + + g_value_init (&new_value, G_TYPE_BOOLEAN); + g_value_set_boolean (&new_value, + gvn_value_is_null (old_value) || !g_value_get_boolean (old_value)); + db_model_set_value (model, &iter, col, &new_value, NULL); +} + +static void vn_column_check_set_editable (VnColumn * obj, gboolean editable) +{ + if (editable) + g_signal_connect (obj->cell, "toggled", + G_CALLBACK (vn_column_check_cb_toggled), obj); + else + g_signal_handlers_disconnect_by_func (obj->cell, + vn_column_check_cb_toggled, obj); +} + +static void vn_column_check_set_value (VnColumnCheck * obj, GtkTreeModel * model, + GtkTreeIter * iter, GtkCellRendererToggle * cell, const GValue * value) +{ + GValue new_value = {0}; + g_value_init (&new_value, G_TYPE_BOOLEAN); + + if (!gvn_value_is_null (value)) + g_value_transform (value, &new_value); + else + g_value_set_boolean (&new_value, FALSE); + + gtk_cell_renderer_toggle_set_active (cell, + g_value_get_boolean (&new_value)); + + g_value_unset (&new_value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_check_init (VnColumnCheck * obj) +{ + GtkCellRenderer * cell = gtk_cell_renderer_toggle_new (); + VN_COLUMN_GET_CLASS (obj)->set_renderer (VN_COLUMN (obj), cell); +} + +static void vn_column_check_finalize (VnColumnCheck * obj) +{ + G_OBJECT_CLASS (vn_column_check_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_check_class_init (VnColumnCheckClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_check_finalize; + VN_COLUMN_CLASS (klass)->set_value = (VnColumnSetValueFunc) vn_column_check_set_value; + VN_COLUMN_CLASS (klass)->set_editable = (VnColumnSetEditableFunc) vn_column_check_set_editable; +} diff --git a/vn/column/vn-column-check.h b/vn/column/vn-column-check.h new file mode 100644 index 0000000..b25bb54 --- /dev/null +++ b/vn/column/vn-column-check.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_COLUMN_CHECK_H +#define VN_COLUMN_CHECK_H + +#include + +#define VN_TYPE_COLUMN_CHECK (vn_column_check_get_type ()) +#define VN_COLUMN_CHECK(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN_CHECK, VnColumnCheck)) +#define VN_IS_COLUMN_CHECK(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN_CHECK)) +#define VN_COLUMN_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN_CHECK, VnColumnCheckClass)) +#define VN_IS_COLUMN_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN_CHECK)) +#define VN_COLUMN_CHECK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN_CHECK, VnColumnCheckClass)) + +typedef struct _VnColumnCheck VnColumnCheck; +typedef struct _VnColumnCheckClass VnColumnCheckClass; + +struct _VnColumnCheck +{ + VnColumn parent; +}; + +struct _VnColumnCheckClass +{ + /* */ + VnColumnClass parent; +}; + +GType vn_column_check_get_type (); + +#endif \ No newline at end of file diff --git a/vn/column/vn-column-combo.c b/vn/column/vn-column-combo.c new file mode 100644 index 0000000..ce17af7 --- /dev/null +++ b/vn/column/vn-column-combo.c @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-column-combo.h" +#include "../vn-model.h" + +G_DEFINE_TYPE (VnColumnCombo, vn_column_combo, VN_TYPE_COLUMN); + +static void vn_column_combo_on_status_changed (DbModel * model, + DbModelStatus status, VnColumn * obj) +{ + if (status == DB_MODEL_STATUS_READY) + g_object_set (obj->cell, "model", VN_COLUMN_COMBO (obj)->tree_model, NULL); + else + g_object_set (obj->cell, "model", NULL, NULL); +} + +static void vn_column_combo_set_model (VnColumnCombo * obj, DbModel * model) +{ + g_return_if_fail (!model || DB_IS_MODEL (model)); + + if (obj->model) + { + g_signal_handlers_disconnect_by_func (obj->model, + vn_column_combo_on_status_changed, obj); + g_clear_object (&obj->model); + g_clear_object (&obj->tree_model); + } + + if (model) + { + obj->model = g_object_ref (model); + obj->tree_model = GTK_TREE_MODEL (vn_model_new (model)); + g_signal_connect (model, "status-changed", + G_CALLBACK (vn_column_combo_on_status_changed), obj); + } +} + +static void vn_column_combo_set_value (VnColumnCombo * obj, GtkTreeModel * model, + GtkTreeIter * tree_iter, GObject * cell, const GValue * value) +{ + if (obj->model && db_model_get_status (obj->model) == DB_MODEL_STATUS_READY) + { + if (gvn_value_is_null (value)) + { + g_object_set_property (cell, "text", value); + } + else + { + DbIter iter; + + if (db_model_search_value (obj->model, obj->index_column, &iter, value)) + g_object_set_property (cell, "text", + db_model_get_value (obj->model, &iter, obj->show_column, NULL)); + } + } +} + +/* + * Restores the focus to the tree view after its edition. + */ +static void vn_column_combo_restore_focus (VnColumnCombo * obj) +{ + gtk_widget_grab_focus + (gtk_tree_view_column_get_tree_view (GTK_TREE_VIEW_COLUMN (obj))); +} + +/* + * Sets the autocompletion functionality to the cell being edited. + */ +static void vn_column_combo_on_editing_started (GtkCellRenderer * cell, + GtkCellEditable * editable, const gchar * path, VnColumnCombo * obj) +{ + GtkEntry * entry; + + if (!GTK_IS_COMBO_BOX (editable) || !obj->tree_model) + return; + + entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (editable))); + + if (!obj->completion_ready) + { + gtk_entry_completion_set_model (obj->completion, obj->tree_model); + g_object_set (obj->completion, "text-column", obj->show_column, NULL); + obj->completion_ready = TRUE; + + g_signal_connect_swapped (editable, "editing-done", + G_CALLBACK (vn_column_combo_restore_focus), obj); + obj->editable = editable; + } + + gtk_entry_set_completion (entry, obj->completion); +} + +/* + * When the text is edited using an entry, tries to set the corresponding + * underlying value. + */ +static void vn_column_combo_on_edited (GtkCellRendererText *renderer, + gchar * path, gchar * new_text, VnColumnCombo * obj) +{ + DbIter iter; + GValue value = {0}; + gvn_value_new_with_content (&value, G_TYPE_STRING, new_text); + + if (db_model_search_value (obj->model, obj->show_column, &iter, &value)) + { + const GValue * value; + + if ((value = db_model_get_value (obj->model, &iter, obj->index_column, NULL))) + VN_COLUMN_GET_CLASS (obj)->value_changed (VN_COLUMN (obj), path, value); + } + + g_value_unset (&value); + + vn_column_combo_restore_focus (obj); +} + +static void vn_column_combo_set_editable (VnColumnCombo * obj, gboolean editable) +{ + VnColumn * parent = VN_COLUMN (obj); + g_object_set (parent->cell, "editable", editable, NULL); + + if (editable) + { + g_signal_connect (parent->cell, "editing-started", + G_CALLBACK (vn_column_combo_on_editing_started), obj); + g_signal_connect (parent->cell, "edited", + G_CALLBACK (vn_column_combo_on_edited), obj); + g_signal_connect_swapped (parent->cell, "editing-canceled", + G_CALLBACK (vn_column_combo_restore_focus), obj); + + if (!obj->completion) + obj->completion = gtk_entry_completion_new (); + } + else + { + g_signal_handlers_disconnect_by_func (parent->cell, + vn_column_combo_on_editing_started, obj); + g_signal_handlers_disconnect_by_func (parent->cell, + vn_column_combo_on_edited, obj); + g_signal_handlers_disconnect_by_func (parent->cell, + vn_column_combo_restore_focus, obj); + + if (obj->editable) + g_signal_handlers_disconnect_by_func (obj->editable, + vn_column_combo_restore_focus, obj); + + obj->completion = NULL; + obj->completion_ready = FALSE; + obj->editable = NULL; + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_INDEX_COLUMN = 1 + ,PROP_SHOW_COLUMN + ,PROP_MODEL + ,PROP_CONN + ,PROP_SQL +}; + +static void vn_column_combo_set_property (VnColumnCombo * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_INDEX_COLUMN: + obj->index_column = g_value_get_uint (value); + break; + case PROP_SHOW_COLUMN: + obj->show_column = g_value_get_uint (value); + g_object_set (VN_COLUMN (obj)->cell, "text-column", obj->show_column, + NULL); + break; + case PROP_MODEL: + vn_column_combo_set_model (obj, g_value_get_object (value)); + break; + case PROP_CONN: + db_model_set_conn (obj->model, g_value_get_object (value)); + break; + case PROP_SQL: + { + const gchar * sql = g_value_get_string (value); + + if (sql) + { + DbModel * model = db_model_new_with_sql (NULL, g_value_get_string (value)); + vn_column_combo_set_model (obj, model); + g_object_unref (model); + } + + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_column_combo_get_property (VnColumnCombo * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_INDEX_COLUMN: + g_value_set_uint (value, obj->index_column); + break; + case PROP_SHOW_COLUMN: + g_value_set_uint (value, obj->show_column); + break; + case PROP_MODEL: + g_value_set_object (value, obj->model); + break; + case PROP_CONN: + g_value_set_object (value, db_model_get_conn (obj->model)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_combo_init (VnColumnCombo * obj) +{ + GtkCellRenderer * cell = gtk_cell_renderer_combo_new (); + obj->model = NULL; + obj->tree_model = NULL; + obj->completion = NULL; + obj->completion_ready = FALSE; + obj->editable = NULL; + VN_COLUMN_GET_CLASS (obj)->set_renderer (VN_COLUMN (obj), cell); +} + +static void vn_column_combo_finalize (VnColumnCombo * obj) +{ + vn_column_combo_set_model (obj, NULL); + G_OBJECT_CLASS (vn_column_combo_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_combo_class_init (VnColumnComboClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_combo_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_column_combo_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_column_combo_get_property; + VN_COLUMN_CLASS (klass)->set_value = (VnColumnSetValueFunc) vn_column_combo_set_value; + VN_COLUMN_CLASS (klass)->set_editable = (VnColumnSetEditableFunc) vn_column_combo_set_editable; + + g_object_class_install_property (k, PROP_INDEX_COLUMN, + g_param_spec_uint ("index-column" + ,_("Index column") + ,_("The column index of the model") + ,0, 255, 0 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SHOW_COLUMN, + g_param_spec_uint ("show-column" + ,_("Show column") + ,_("The column of the model shown by combo") + ,0, 255, 1 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_MODEL, + g_param_spec_object ("model" + ,_("Model") + ,_("The model from which the combo takes the values shown in the list") + ,DB_TYPE_MODEL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by the model") + ,DB_TYPE_CONN + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("The SQL query used to create the model") + , NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE + )); +} diff --git a/vn/column/vn-column-combo.h b/vn/column/vn-column-combo.h new file mode 100644 index 0000000..4ae712f --- /dev/null +++ b/vn/column/vn-column-combo.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_COLUMN_COMBO_H +#define VN_COLUMN_COMBO_H + +#include + +#define VN_TYPE_COLUMN_COMBO (vn_column_combo_get_type ()) +#define VN_COLUMN_COMBO(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN_COMBO, VnColumnCombo)) +#define VN_IS_COLUMN_COMBO(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN_COMBO)) +#define VN_COLUMN_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN_COMBO, VnColumnComboClass)) +#define VN_IS_COLUMN_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN_COMBO)) +#define VN_COLUMN_COMBO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN_COMBO, VnColumnComboClass)) + +typedef struct _VnColumnCombo VnColumnCombo; +typedef struct _VnColumnComboClass VnColumnComboClass; + +struct _VnColumnCombo +{ + VnColumn parent; + /* */ + DbModel * model; + GtkTreeModel * tree_model; + guint index_column; + guint show_column; + GtkEntryCompletion * completion; + gboolean completion_ready; + GtkCellEditable * editable; +}; + +struct _VnColumnComboClass +{ + /* */ + VnColumnClass parent; +}; + +GType vn_column_combo_get_type (); + +#endif \ No newline at end of file diff --git a/vn/column/vn-column-entry.c b/vn/column/vn-column-entry.c new file mode 100644 index 0000000..c388018 --- /dev/null +++ b/vn/column/vn-column-entry.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-column-entry.h" + +G_DEFINE_TYPE (VnColumnEntry, vn_column_entry, VN_TYPE_COLUMN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_column_entry_cb_edited (GtkCellRendererText * cell, + const gchar * path, gchar * text, VnColumnEntry * obj) +{ + GValue value = {0}; + + if (g_strcmp0 (text, "")) + { + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, text); + } + else + g_value_init (&value, GVN_TYPE_NULL); + + VN_COLUMN_GET_CLASS (obj)->value_changed (VN_COLUMN (obj), path, &value); + g_value_unset (&value); +} + +static void vn_column_entry_set_editable (VnColumn * obj, gboolean editable) +{ + g_object_set (obj->cell, "editable", editable, NULL); + + if (editable) + g_signal_connect (obj->cell, "edited", + G_CALLBACK (vn_column_entry_cb_edited), obj); + else + g_signal_handlers_disconnect_by_func (obj->cell, + vn_column_entry_cb_edited, obj); +} + +static void vn_column_entry_set_value (VnColumnEntry * obj, GtkTreeModel * model, + GtkTreeIter * iter, GObject * cell, const GValue * value) +{ + GValue new_value = {0}; + gvn_value_to_format_string (value, obj->digits, &new_value); + g_object_set_property (cell, "text", &new_value); + g_value_unset (&new_value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_DIGITS = 1 +}; + +static void vn_column_entry_set_property (VnColumnEntry * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + obj->digits = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_column_entry_get_property (VnColumnEntry * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + g_value_set_uint (value, obj->digits); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_entry_init (VnColumnEntry * obj) +{ + GtkCellRenderer * cell = gtk_cell_renderer_text_new (); + VN_COLUMN_GET_CLASS (obj)->set_renderer (VN_COLUMN (obj), cell); +} + +static void vn_column_entry_finalize (VnColumnEntry * obj) +{ + G_OBJECT_CLASS (vn_column_entry_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_entry_class_init (VnColumnEntryClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_entry_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_column_entry_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_column_entry_get_property; + VN_COLUMN_CLASS (klass)->set_value = (VnColumnSetValueFunc) vn_column_entry_set_value; + VN_COLUMN_CLASS (klass)->set_editable = (VnColumnSetEditableFunc) vn_column_entry_set_editable; + + g_object_class_install_property (k, PROP_DIGITS, + g_param_spec_uint ("digits" + ,_("Digits") + ,_("The number of decimal places to display.") + ,0 ,20 ,0 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} diff --git a/vn/column/vn-column-entry.h b/vn/column/vn-column-entry.h new file mode 100644 index 0000000..d80b80a --- /dev/null +++ b/vn/column/vn-column-entry.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_COLUMN_ENTRY_H +#define VN_COLUMN_ENTRY_H + +#include + +#define VN_TYPE_COLUMN_ENTRY (vn_column_entry_get_type ()) +#define VN_COLUMN_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN_ENTRY, VnColumnEntry)) +#define VN_IS_COLUMN_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN_ENTRY)) +#define VN_COLUMN_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN_ENTRY, VnColumnEntryClass)) +#define VN_IS_COLUMN_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN_ENTRY)) +#define VN_COLUMN_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN_ENTRY, VnColumnEntryClass)) + +typedef struct _VnColumnEntry VnColumnEntry; +typedef struct _VnColumnEntryClass VnColumnEntryClass; + +struct _VnColumnEntry +{ + VnColumn parent; + guint digits; +}; + +struct _VnColumnEntryClass +{ + /* */ + VnColumnClass parent; +}; + +GType vn_column_entry_get_type (); + +#endif \ No newline at end of file diff --git a/vn/column/vn-column-image.c b/vn/column/vn-column-image.c new file mode 100644 index 0000000..5177f6c --- /dev/null +++ b/vn/column/vn-column-image.c @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-column-image.h" +#include "../vn-model.h" + +#define LOAD_IMAGE _IMAGE_DIR"/load.gif" + +G_DEFINE_TYPE (VnColumnImage, vn_column_image, VN_TYPE_COLUMN); + +typedef struct +{ + VnColumnImage * obj; + GtkTreeModel * model; + GtkTreeIter * iter; + GtkCellRenderer * cell; + gchar * name; +} +DownloadData; + +typedef struct +{ + gchar * path; + gboolean error; + gint tooltip_size; + GtkWidget * image; +} +TooltipData; + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +static void vn_column_image_download_error (VnColumnImage * obj, const GError * error) +{ + g_object_set (VN_COLUMN (obj)->cell, "icon-name", "image-missing", NULL); +} + +static void free_object (gpointer object) +{ + if (object != NULL) + g_object_unref (object); +} + +static void free_tooltip_data (TooltipData * data) +{ + if (data) + { + g_free (data->path); + + if (data->image) + g_object_unref (data->image); + } + + g_free (data); +} + +static void vn_column_image_on_tooltip_size (GdkPixbufLoader * loader, gint width, + gint height, gpointer tsize) +{ + gint h, w, size = GPOINTER_TO_INT (tsize); + + if (width > size || height > size) + { + if (width >= height) + { + w = size; + h = height * w / width; + } + else + { + h = size; + w = width * h / height; + } + } + else + { + w = width; + h = height; + } + + gdk_pixbuf_loader_set_size (loader, w, h); +} + +static void vn_column_image_on_download_tooltip (DbFileLoader * fl, + GBytes * bytes, const GError * error, TooltipData * data) +{ + if (error) + data->image = g_object_ref_sink (gtk_image_new_from_icon_name + ("image-missing", GTK_ICON_SIZE_MENU)); + else if (bytes && data) + { + gsize size; + GError * err = NULL; + const guchar * raw_data = g_bytes_get_data (bytes, &size); + GdkPixbufLoader * loader = gdk_pixbuf_loader_new (); + + if (data->tooltip_size > 0) + g_signal_connect (loader, "size-prepared", + G_CALLBACK (vn_column_image_on_tooltip_size), + GINT_TO_POINTER (data->tooltip_size)); + + if (gdk_pixbuf_loader_write (loader, raw_data, size, &err) + && gdk_pixbuf_loader_close (loader, &err)) + { + GdkPixbuf * pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + + if (data->image) + g_object_unref (data->image); + + data->image = g_object_ref_sink (gtk_image_new_from_pixbuf (pixbuf)); + } + else + { + gdk_pixbuf_loader_close (loader, NULL); + g_error_free (err); + } + + g_object_unref (loader); + } + + gtk_tooltip_trigger_tooltip_query (gdk_display_get_default()); +} + +static gboolean vn_column_image_on_query_tooltip (GtkTreeView * view, + gint x, gint y, gboolean k, GtkTooltip * tip, VnColumnImage * obj) +{ + gboolean ret = FALSE; + TooltipData * data; + GtkTreeIter iter; + GtkTreePath * path = NULL; + + if (obj && VN_IS_COLUMN_IMAGE (obj) && !k + && gtk_tree_view_get_tooltip_context (view, &x, &y, k, NULL, &path, &iter)) + { + gint wx, wy; + GdkRectangle rect; + + gtk_tree_view_convert_bin_window_to_widget_coords (view, x, y, &wx, &wy); + gtk_tree_view_get_background_area + (view, path, GTK_TREE_VIEW_COLUMN (obj), &rect); + + if (!(rect.x < wx && wx < rect.x + rect.width)) + { + gtk_tree_path_free (path); + return FALSE; + } + } + else + return FALSE; + + if (obj->tooltips + && (data = g_hash_table_lookup (obj->tooltips, iter.user_data)) + && !data->error && data->path) + { + gtk_tree_view_set_tooltip_cell (view, tip, path, + GTK_TREE_VIEW_COLUMN (obj), VN_COLUMN (obj)->cell); + + if (!data->image) + { + data->image = g_object_ref_sink (gtk_image_new_from_file (LOAD_IMAGE)); + gtk_tooltip_set_custom (tip, data->image); + + db_file_loader_download (obj->loader, data->path, + (DbFileLoaderCallbackFunc) vn_column_image_on_download_tooltip, data); + } + else + gtk_tooltip_set_custom (tip, data->image); + + ret = TRUE; + } + + gtk_tree_path_free (path); + return ret; +} + +static GdkPixbuf * vn_column_image_set_image (VnColumnImage * obj, + GtkCellRenderer * cell, GBytes * bytes, gboolean pix) +{ + gsize size; + GdkPixbuf * pixbuf = NULL; + GError * error = NULL; + const guchar * raw_data = g_bytes_get_data (bytes, &size); + GdkPixbufLoader * loader = gdk_pixbuf_loader_new (); + + if (raw_data) + { + if (gdk_pixbuf_loader_write (loader, raw_data, size, &error) + && gdk_pixbuf_loader_close (loader, &error)) + { + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + g_object_set (cell, "pixbuf", pixbuf, NULL); + } + else + { + gdk_pixbuf_loader_close (loader, NULL); + vn_column_image_download_error (obj, error); + g_error_free (error); + } + } + else + gdk_pixbuf_loader_close (loader, NULL); + + g_object_unref (loader); + + return pix && pixbuf ? g_object_ref (pixbuf) : NULL; +} + +static void vn_column_image_on_download (DbFileLoader * obj, GBytes * bytes, + const GError * error, DownloadData * data) +{ + GtkTreePath * path; + + if (error) + vn_column_image_download_error (data->obj, error); + else if (bytes && vn_model_iter_is_valid (data->iter, data->model)) + { + if (data->obj->loaded) + g_hash_table_replace (data->obj->loaded, g_strdup (data->name), + vn_column_image_set_image (data->obj, data->cell, bytes, TRUE)); + + path = gtk_tree_model_get_path (data->model, data->iter); + gtk_tree_model_row_changed (data->model, path, data->iter); + gtk_tree_path_free (path); + } + + g_object_unref (data->obj); + g_object_unref (data->model); + gtk_tree_iter_free (data->iter); + g_free (data->name); + g_free (data); +} + +static void vn_column_image_set_value (VnColumnImage * obj, GtkTreeModel * model, + GtkTreeIter * iter, GtkCellRenderer * cell, const GValue * value) +{ + GType type = G_VALUE_TYPE (value); + + if (type == GVN_TYPE_NULL) + g_object_set (cell, "pixbuf", NULL, "icon-name", NULL, NULL); + else if (type == G_TYPE_BYTES) + { + GBytes * bytes = g_value_get_boxed (value); + + if (bytes) + vn_column_image_set_image (obj, cell, bytes, FALSE); + else + vn_column_image_download_error (obj, NULL); + } + else if (type == G_TYPE_STRING) + { + DownloadData * data; + gchar * name = g_value_dup_string (value); + GtkTreeView * view = GTK_TREE_VIEW + (gtk_tree_view_column_get_tree_view (GTK_TREE_VIEW_COLUMN (obj))); + + if (obj->loaded) + { + if (g_hash_table_contains (obj->loaded, name)) + { + GdkPixbuf * pixbuf = g_hash_table_lookup (obj->loaded, name); + + if (pixbuf) + g_object_set (cell, "pixbuf", pixbuf, NULL); + else + vn_column_image_download_error (obj, NULL); + + if (obj->tooltips + && !g_hash_table_contains (obj->tooltips, iter->user_data)) + { + TooltipData * data = g_new (TooltipData, 1); + data->path = g_strconcat ("/", obj->tooltip_path, "/", name, NULL); + data->error = FALSE; + data->tooltip_size = obj->tooltip_size; + data->image = NULL; + + g_hash_table_insert (obj->tooltips, iter->user_data, data); + } + + g_free (name); + + return; + } + else + { + gint view_x, view_y; + GdkRectangle view_rect, cell_rect; + GtkTreePath * path = gtk_tree_model_get_path (model, iter); + + gtk_tree_view_get_cell_area + (view, path, GTK_TREE_VIEW_COLUMN (obj), &cell_rect); + gtk_tree_path_free (path); + gtk_tree_view_get_visible_rect (view, &view_rect); + gtk_tree_view_convert_tree_to_bin_window_coords + (view, view_rect.x, view_rect.y, &view_x, &view_y); + + if (!(view_x <= cell_rect.x && cell_rect.x <= view_x + view_rect.width + && view_y <= cell_rect.y && cell_rect.y <= view_y + view_rect.height)) + { + g_free (name); + return; + } + } + } + else + obj->loaded = g_hash_table_new_full + ((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, (GDestroyNotify) free_object); + + g_hash_table_insert (obj->loaded, g_strdup (name), NULL); + + if (!obj->loader) + obj->loader = db_file_loader_new (obj->host, obj->path); + + if (obj->tooltip_path && !obj->tooltips) + obj->tooltips = g_hash_table_new_full + ((GHashFunc) g_direct_hash, (GEqualFunc) g_direct_equal, + (GDestroyNotify) NULL, (GDestroyNotify) free_tooltip_data); + g_object_set (view, "has-tooltip", TRUE, NULL); + g_signal_connect (view, "query-tooltip", + G_CALLBACK (vn_column_image_on_query_tooltip), obj); + + data = g_new (DownloadData, 1); + data->obj = g_object_ref (obj); + data->model = g_object_ref (model); + data->iter = gtk_tree_iter_copy (iter); + data->cell = cell; + data->name = name; + + db_file_loader_download (obj->loader, name, + (DbFileLoaderCallbackFunc) vn_column_image_on_download, data); + } +} + +static void vn_column_image_set_editable (VnColumn * obj, gboolean editable){} + +static void vn_column_image_on_model_changed (VnColumnImage * obj) +{ + if (obj->loader) + db_file_loader_cancel_all (obj->loader); + + if (obj->loaded) + g_hash_table_destroy (obj->loaded); + + if (obj->tooltips) + { + g_hash_table_destroy (obj->tooltips); + g_signal_handlers_disconnect_by_func (GTK_TREE_VIEW + (gtk_tree_view_column_get_tree_view (GTK_TREE_VIEW_COLUMN (obj))), + vn_column_image_on_query_tooltip, obj); + } + + obj->loaded = NULL; + obj->tooltips = NULL; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Property + +enum +{ + PROP_HOST = 1 + ,PROP_PATH + ,PROP_TOOLTIP_PATH + ,PROP_TOOLTIP_SIZE +}; + +static void vn_column_image_set_property (VnColumnImage * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_HOST: + g_free (obj->host); + obj->host = g_value_dup_string (value); + break; + case PROP_PATH: + g_free (obj->path); + obj->path = g_value_dup_string (value); + break; + case PROP_TOOLTIP_PATH: + g_free (obj->tooltip_path); + obj->tooltip_path = g_value_dup_string (value); + break; + case PROP_TOOLTIP_SIZE: + obj->tooltip_size = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_column_image_get_property (VnColumnImage * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_HOST: + g_value_set_string (value, obj->host); + break; + case PROP_PATH: + g_value_set_string (value, obj->path); + break; + case PROP_TOOLTIP_PATH: + g_value_set_string (value, obj->tooltip_path); + break; + case PROP_TOOLTIP_SIZE: + g_value_set_int (value, obj->tooltip_size); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_image_init (VnColumnImage * obj) +{ + GtkCellRenderer * cell = gtk_cell_renderer_pixbuf_new (); + VN_COLUMN_GET_CLASS (obj)->set_renderer (VN_COLUMN (obj), cell); + + obj->host = NULL; + obj->path = NULL; + obj->tooltip_path = NULL; + obj->loader = NULL; + obj->loaded = NULL; + obj->tooltips = NULL; +} + +static void vn_column_image_finalize (VnColumnImage * obj) +{ + g_free (obj->host); + g_free (obj->path); + g_free (obj->tooltip_path); + + if (obj->loader) + { + db_file_loader_cancel_all (obj->loader); + g_object_unref (obj->loader); + } + + if (obj->loaded) + g_hash_table_destroy (obj->loaded); + + if (obj->tooltips) + g_hash_table_destroy (obj->tooltips); + + G_OBJECT_CLASS (vn_column_image_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_image_class_init (VnColumnImageClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + VnColumnClass * col_k = VN_COLUMN_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_image_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_column_image_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_column_image_get_property; + col_k->set_value = (VnColumnSetValueFunc) vn_column_image_set_value; + col_k->set_editable = (VnColumnSetEditableFunc) vn_column_image_set_editable; + col_k->model_changed = (VnColumnModelChangedFunc) vn_column_image_on_model_changed; + + g_object_class_install_property (k, PROP_HOST, + g_param_spec_string ("host" + ,_("Host") + ,_("The host web server name to get the images") + ,"www.verdnatura.es"//XXX NULL + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_PATH, + g_param_spec_string ("path" + ,_("Path") + ,_("Base path from the host where the images will be downloaded") + ,"image/cache/catalog/icon"//XXX NULL + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_TOOLTIP_PATH, + g_param_spec_string ("tooltip-path" + ,_("Tooltip path") + ,_("Prefix for the path of the images to be shown in the tooltip. " + "Starting after the path of the column and appending the name on " + "each cell") + ,"../full"//XXX NULL + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_TOOLTIP_SIZE, + g_param_spec_int ("tooltip-size" + ,_("Tooltip size") + ,_("Size of the bigger side of the tooltip images, the another side " + "will be scaled accordingly and smaller images won't be scaled") + ,-1 + ,G_MAXINT + ,300 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} diff --git a/vn/column/vn-column-image.h b/vn/column/vn-column-image.h new file mode 100644 index 0000000..6c12ac3 --- /dev/null +++ b/vn/column/vn-column-image.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_COLUMN_IMAGE_H +#define VN_COLUMN_IMAGE_H + +#include + +#define VN_TYPE_COLUMN_IMAGE (vn_column_image_get_type ()) +#define VN_COLUMN_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN_IMAGE, VnColumnImage)) +#define VN_IS_COLUMN_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN_IMAGE)) +#define VN_COLUMN_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN_IMAGE, VnColumnImageClass)) +#define VN_IS_COLUMN_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN_IMAGE)) +#define VN_COLUMN_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN_IMAGE, VnColumnImageClass)) + +typedef struct _VnColumnImage VnColumnImage; +typedef struct _VnColumnImageClass VnColumnImageClass; + +/** + * VnColumnImage: + **/ +struct _VnColumnImage +{ + /* */ + VnColumn parent; + gchar * host; + gchar * path; + gchar * tooltip_path; + gint tooltip_size; + DbFileLoader * loader; + GHashTable * loaded; + GHashTable * tooltips; +}; + +struct _VnColumnImageClass +{ + /* */ + VnColumnClass parent; +}; + +GType vn_column_image_get_type (); + +#endif diff --git a/vn/column/vn-column-spin.c b/vn/column/vn-column-spin.c new file mode 100644 index 0000000..9145a4c --- /dev/null +++ b/vn/column/vn-column-spin.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-column-spin.h" + +G_DEFINE_TYPE (VnColumnSpin, vn_column_spin, VN_TYPE_COLUMN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_spin_init (VnColumnSpin * obj) +{ + GtkCellRenderer * cell = gtk_cell_renderer_spin_new (); + VN_COLUMN_GET_CLASS (obj)->set_renderer (VN_COLUMN (obj), cell); +} + +static void vn_column_spin_finalize (VnColumnSpin * obj) +{ + G_OBJECT_CLASS (vn_column_spin_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_spin_class_init (VnColumnSpinClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_spin_finalize; +} diff --git a/vn/column/vn-column-spin.h b/vn/column/vn-column-spin.h new file mode 100644 index 0000000..fab80ef --- /dev/null +++ b/vn/column/vn-column-spin.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_COLUMN_SPIN_H +#define VN_COLUMN_SPIN_H + +#include + +#define VN_TYPE_COLUMN_SPIN (vn_column_spin_get_type ()) +#define VN_COLUMN_SPIN(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN_SPIN, VnColumnSpin)) +#define VN_IS_COLUMN_SPIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN_SPIN)) +#define VN_COLUMN_SPIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN_SPIN, VnColumnSpinClass)) +#define VN_IS_COLUMN_SPIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN_SPIN)) +#define VN_COLUMN_SPIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN_SPIN, VnColumnSpinClass)) + +typedef struct _VnColumnSpin VnColumnSpin; +typedef struct _VnColumnSpinClass VnColumnSpinClass; + +struct _VnColumnSpin +{ + VnColumn parent; +}; + +struct _VnColumnSpinClass +{ + /* */ + VnColumnClass parent; +}; + +GType vn_column_spin_get_type (); + +#endif \ No newline at end of file diff --git a/vn/field/Makefile.am b/vn/field/Makefile.am new file mode 100644 index 0000000..10f75cd --- /dev/null +++ b/vn/field/Makefile.am @@ -0,0 +1,30 @@ +include $(top_srcdir)/Makefile.decl + +field_lib_LTLIBRARIES = libvnfield.la +field_include_HEADERS = \ + field.h \ + vn-calendar.h \ + vn-check.h \ + vn-combo.h \ + vn-spin.h \ + vn-entry.h \ + vn-image.h \ + vn-completion.h \ + vn-date-chooser.h \ + vn-http-image.h + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(gtk_CFLAGS) +libvnfield_la_LIBADD = $(gtk_LIBS) +libvnfield_la_SOURCES = \ + $(field_include_HEADERS) \ + vn-calendar.c \ + vn-check.c \ + vn-combo.c \ + vn-spin.c \ + vn-entry.c \ + vn-image.c \ + vn-completion.c \ + vn-date-chooser.c \ + vn-http-image.c diff --git a/vn/field/field.h b/vn/field/field.h new file mode 100644 index 0000000..a66e177 --- /dev/null +++ b/vn/field/field.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef FIELD_H +#define FIELD_H + +#include "vn-entry.h" +#include "vn-spin.h" +#include "vn-calendar.h" +#include "vn-combo.h" +#include "vn-check.h" +#include "vn-image.h" +#include "vn-completion.h" +#include "vn-date-chooser.h" +#include "vn-http-image.h" + +#endif \ No newline at end of file diff --git a/vn/field/vn-calendar.c b/vn/field/vn-calendar.c new file mode 100644 index 0000000..986748c --- /dev/null +++ b/vn/field/vn-calendar.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-calendar.h" +#include + +/** + * SECTION:vn-calendar + * @Short_description: a calendar widget + * @Title: VnCalendar + * @See_also: #VnField + * @Image: vn-calendar.png + * + * A calendar widget to see or set the date. + */ +G_DEFINE_TYPE (VnCalendar, vn_calendar, VN_TYPE_FIELD); + +/** + * vn_calendar_new: + * + * Creates a new #VnCalendar + * + * Return value: a #VnCalendar + **/ +VnField * vn_calendar_new () +{ + return g_object_new (VN_TYPE_CALENDAR, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_calendar_cb_day_selected (GtkCalendar * calendar, VnCalendar * obj) +{ + guint year; + guint month; + guint day; + GDate new_date; + GValue value = {0}; + + gtk_calendar_clear_marks (calendar); + gtk_calendar_get_date (calendar, &year, &month, &day); + month++; + + g_date_clear (&new_date, 1); + g_date_set_dmy (&new_date, day, month, year); + + if (!g_date_valid (&obj->date) || g_date_compare (&new_date, &obj->date)) + { + g_date_set_julian (&obj->date, g_date_get_julian (&new_date)); + gtk_calendar_mark_day (calendar, day); + g_value_init (&value, G_TYPE_DATE); + g_value_set_boxed (&value, &obj->date); + } + else + { + g_date_clear (&obj->date, 1); + g_value_init (&value, GVN_TYPE_NULL); + } + + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + g_value_unset (&value); +} + +void vn_calendar_cb_month_changed (GtkCalendar * calendar, VnCalendar * obj) +{ + guint year; + guint month; + GDate * date = &obj->date; + + gtk_calendar_clear_marks (calendar); + gtk_calendar_get_date (calendar, &year, &month, NULL); + + if (g_date_valid (date) + && g_date_get_month (date) == month + 1 + && g_date_get_year (date) == year) + { + guint day = g_date_get_day (date); + gtk_calendar_mark_day (calendar, day); + gtk_calendar_select_day (obj->calendar, day); + } + else + gtk_calendar_select_day (obj->calendar, 0); +} + +static void vn_calendar_set_value (VnCalendar * obj, const GValue * value) +{ + GDate * date = NULL; + + if (G_VALUE_TYPE (value) != G_TYPE_DATE) + { + GValue new_value = {0}; + g_value_init (&new_value, G_TYPE_DATE); + g_value_transform (value, &new_value); + date = g_value_get_boxed (&new_value); + } + else if (!gvn_value_is_null (value)) + date = g_value_get_boxed (value); + + if (date) + { + obj->date = *date; + gtk_calendar_select_month (obj->calendar + ,g_date_get_month (date) - 1 + ,g_date_get_year (date) + ); + } + else + { + g_date_clear (&obj->date, 1); + gtk_calendar_select_day (obj->calendar, 0); + gtk_calendar_clear_marks (obj->calendar); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_calendar_init (VnCalendar * obj) +{ + g_date_clear (&obj->date, 1); + + obj->calendar = GTK_CALENDAR (gtk_calendar_new ()); + gtk_calendar_set_display_options (obj->calendar, + GTK_CALENDAR_SHOW_HEADING | GTK_CALENDAR_SHOW_DAY_NAMES); + g_object_connect (obj->calendar + ,"signal::day-selected-double-click", vn_calendar_cb_day_selected, obj + ,"signal::month-changed", vn_calendar_cb_month_changed, obj + ,NULL + ); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->calendar)); + VN_FIELD (obj)->field = GTK_WIDGET (obj->calendar); +} + +static void vn_calendar_finalize (VnCalendar * obj) +{ + G_OBJECT_CLASS (vn_calendar_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_calendar_class_init (VnCalendarClass * klass) +{ + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) vn_calendar_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_calendar_set_value; +} diff --git a/vn/field/vn-calendar.h b/vn/field/vn-calendar.h new file mode 100644 index 0000000..037b4a5 --- /dev/null +++ b/vn/field/vn-calendar.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_CALENDAR_H +#define VN_CALENDAR_H + +#include + +#define VN_TYPE_CALENDAR (vn_calendar_get_type ()) +#define VN_CALENDAR(object) (G_TYPE_CHECK_INSTANCE_CAST (object, VN_TYPE_CALENDAR , VnCalendar)) +#define VN_IS_CALENDAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_CALENDAR)) +#define VN_CALENDAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_CALENDAR, VnCalendarClass)) +#define VN_IS_CALENDAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_CALENDAR)) +#define VN_CALENDAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_CALENDAR, VnCalendarClass)) + +typedef struct _VnCalendar VnCalendar; +typedef struct _VnCalendarClass VnCalendarClass; + +struct _VnCalendar +{ + VnField parent; + GtkCalendar * calendar; + GDate date; +}; + +struct _VnCalendarClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_calendar_get_type (); +VnField * vn_calendar_new (); + +#endif \ No newline at end of file diff --git a/vn/field/vn-check.c b/vn/field/vn-check.c new file mode 100644 index 0000000..61d3aec --- /dev/null +++ b/vn/field/vn-check.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-check.h" +#include + +/** + * SECTION:vn-check + * @Short_description: a checkbox widget + * @Title: VnCheck + * @See_also: #VnField + * @Image: check.png + * + * A checkbox widget to use in boolean fields. + */ +G_DEFINE_TYPE (VnCheck, vn_check, VN_TYPE_FIELD); + +/** + * vn_check_new: + * + * Creates a new #VnCheck + * + * Return value: a #VnCheck + **/ +VnField * vn_check_new () +{ + return VN_FIELD (g_object_new (VN_TYPE_CHECK, NULL)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_check_changed (VnCheck * obj) +{ + GValue value = {0}; + + if (!gtk_toggle_button_get_inconsistent (obj->button)) + { + g_value_init (&value, G_TYPE_BOOLEAN); + g_value_set_boolean (&value, gtk_toggle_button_get_active (obj->button)); + } + else + g_value_init (&value, GVN_TYPE_NULL); + + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + g_value_unset (&value); +} + +static void vn_check_on_toggled (GtkToggleButton * button, VnCheck * obj) +{ + gtk_toggle_button_set_inconsistent (button, FALSE); + vn_check_changed (obj); +} + +static gboolean vn_check_on_secondary_button (GtkToggleButton * button, GdkEventButton * event, VnField * obj) +{ + if (event->type != GDK_BUTTON_RELEASE || event->button != 3 || !vn_field_get_null (obj)) + return FALSE; + + if (gtk_toggle_button_get_inconsistent (button)) + gtk_toggle_button_set_inconsistent (button, FALSE); + else + gtk_toggle_button_set_inconsistent (button, TRUE); + + g_signal_handlers_block_by_func (button, vn_check_on_toggled, obj); + gtk_toggle_button_set_active (button, FALSE); + g_signal_handlers_unblock_by_func (button, vn_check_on_toggled, obj); + + vn_check_changed (VN_CHECK (obj)); + return FALSE; +} + +static void vn_check_set_value (VnCheck * obj, const GValue * value) +{ + GValue new_value = {0}; + + if (!gvn_value_is_null (value)) + { + g_value_init (&new_value, G_TYPE_BOOLEAN); + g_value_transform (value, &new_value); + } + else + g_value_init (&new_value, GVN_TYPE_NULL); + + g_signal_handlers_block_by_func (obj->button, vn_check_on_toggled, obj); + + if (!gvn_value_is_null (&new_value)) + { + gtk_toggle_button_set_inconsistent (obj->button, FALSE); + gtk_toggle_button_set_active (obj->button, + g_value_get_boolean (&new_value)); + } + else + { + gtk_toggle_button_set_inconsistent (obj->button, TRUE); + gtk_toggle_button_set_active (obj->button, FALSE); + } + + g_signal_handlers_unblock_by_func (obj->button, vn_check_on_toggled, obj); + + g_value_unset (&new_value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_check_init (VnCheck * obj) +{ + obj->button = GTK_TOGGLE_BUTTON (gtk_check_button_new ()); + gtk_button_set_use_underline (GTK_BUTTON (obj->button), TRUE); + g_object_connect (obj->button + ,"signal::toggled", vn_check_on_toggled, obj + ,"signal::button-release-event", vn_check_on_secondary_button, obj + ,NULL + ); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->button)); + + VN_FIELD (obj)->field = GTK_WIDGET (obj->button); +} + +static void vn_check_finalize (VnCheck * obj) +{ + G_OBJECT_CLASS (vn_check_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_check_class_init (VnCheckClass * klass) +{ + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) vn_check_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_check_set_value; +} diff --git a/vn/field/vn-check.h b/vn/field/vn-check.h new file mode 100644 index 0000000..a02e1c2 --- /dev/null +++ b/vn/field/vn-check.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_CHECK_H +#define VN_CHECK_H + +#include + +#define VN_TYPE_CHECK (vn_check_get_type ()) +#define VN_CHECK(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_CHECK , VnCheck)) +#define VN_IS_CHECK(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_CHECK)) +#define VN_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_CHECK, VnCheckClass)) +#define VN_IS_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_CHECK)) +#define VN_CHECK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_CHECK, VnCheckClass)) + +typedef struct _VnCheck VnCheck; +typedef struct _VnCheckClass VnCheckClass; + +struct _VnCheck +{ + VnField parent; + GtkToggleButton * button; +}; + +struct _VnCheckClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_check_get_type (); +VnField * vn_check_new (); + +#endif \ No newline at end of file diff --git a/vn/field/vn-combo.c b/vn/field/vn-combo.c new file mode 100644 index 0000000..8d4d5a4 --- /dev/null +++ b/vn/field/vn-combo.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-combo.h" +#include "../vn-model.h" +#include + +/** + * SECTION:vn-combo + * @Short_description: a combo box widget + * @Title: VnCombo + * @See_also: #VnField + * @Image: combo.png + * + * A combo box widget to select from a list of items, selected from a + * database. + */ +G_DEFINE_TYPE (VnCombo, vn_combo, VN_TYPE_FIELD); + +/** + * vn_combo_new: + * @model: the model used by combo box + * + * Creates a new #VnCombo + * + * Return value: a #VnCombo + **/ +VnField * vn_combo_new (DbModel * model) +{ + return g_object_new (VN_TYPE_COMBO, "model", model, NULL); +} + +/** + * vn_combo_new_with_sql: + * @conn: the connection used to create the combo model + * @sql: the SQL query used to create the combo model + * + * Creates a new #VnCombo + * + * Return value: a #VnCombo + **/ +VnField * vn_combo_new_with_sql (DbConn * conn, const gchar * sql) +{ + return g_object_new (VN_TYPE_COMBO, "sql", sql, "conn", conn, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_combo_cb_changed (GtkComboBox * combo, VnCombo * obj) +{ + GtkTreeIter iter; + GValue value = {0}; + + if (gtk_combo_box_get_active_iter (combo, &iter)) + gtk_tree_model_get_value (obj->tree, &iter, obj->index_column, &value); + else + g_value_init (&value, GVN_TYPE_NULL); + + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + g_value_unset (&value); +} + +static void vn_combo_on_model_ready (VnCombo * obj, const GValue * value) +{ + DbIter iter; + + g_signal_handlers_block_by_func (obj->combo, + vn_combo_cb_changed, obj); + + if (!gvn_value_is_null (value) + && db_model_search_value (obj->model, obj->index_column, &iter, value)) + { + GtkTreeIter tree_iter; + + vn_gtk_tree_iter_from_db_iter (&tree_iter, &iter); + gtk_combo_box_set_active_iter (obj->combo, &tree_iter); + } + else + gtk_combo_box_set_active_iter (obj->combo, NULL); + + g_signal_handlers_unblock_by_func (obj->combo, + vn_combo_cb_changed, obj); +} + +static void vn_combo_set_value (VnCombo * obj, const GValue * value) +{ + if (obj->model && db_model_get_status (obj->model) == DB_MODEL_STATUS_READY) + vn_combo_on_model_ready (obj, value); +} + +static void vn_combo_cb_status_changed (DbModel * model, DbModelStatus status, VnCombo * obj) +{ + if (status == DB_MODEL_STATUS_READY) + { + gtk_combo_box_set_model (obj->combo, obj->tree); + vn_combo_on_model_ready (obj, vn_field_get_value (VN_FIELD (obj))); + } + else + gtk_combo_box_set_model (obj->combo, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_combo_set_conn: + * @obj: a #VnCombo + * @conn: the #DbConn + * + * Sets the connection used to create the combo model. + **/ +void vn_combo_set_conn (VnCombo * obj, DbConn * conn) +{ + g_return_if_fail (VN_IS_COMBO (obj)); + g_return_if_fail (DB_IS_CONN (conn)); + g_return_if_fail (obj->model); + + db_model_set_conn (obj->model, conn); +} + +/** + * vn_combo_set_model: + * @obj: a #VnCombo + * @model: the #DbModel + * + * Sets the SQL query used to create the combo model. + **/ +void vn_combo_set_model (VnCombo * obj, DbModel * model) +{ + g_return_if_fail (VN_IS_COMBO (obj)); + g_return_if_fail (DB_IS_MODEL (model) || !model); + + if (obj->model) + { + g_signal_handlers_disconnect_by_func (obj->model, + vn_combo_cb_status_changed, obj); + g_clear_object (&obj->model); + g_clear_object (&obj->tree); + } + if (model) + { + obj->tree = GTK_TREE_MODEL (vn_model_new (model)); + obj->model = g_object_ref (model); + g_signal_connect (model, "status-changed", + G_CALLBACK (vn_combo_cb_status_changed), obj); + vn_combo_cb_status_changed (model, + db_model_get_status (model), obj); + } +} + +/** + * vn_combo_get_index_column: + * @obj: a #VnCombo + * + * Gets the combo column index. + * + * Return value: the column + **/ +guint vn_combo_get_index_column (VnCombo * obj) +{ + g_return_val_if_fail (VN_IS_COMBO (obj), 0); + + return obj->index_column; +} + +/** + * vn_combo_set_index_column: + * @obj: a #VnCombo + * @column: the column + * + * Sets the column index used by combo. + **/ +void vn_combo_set_index_column (VnCombo * obj, guint column) +{ + g_return_if_fail (VN_IS_COMBO (obj)); + + obj->index_column = column; + vn_combo_set_value (obj, vn_field_get_value (VN_FIELD (obj))); +} + +/** + * vn_combo_get_show_column: + * @obj: a #VnCombo + * + * Gets the column shown by combo. + * + * Return value: the column + **/ +guint vn_combo_get_show_column (VnCombo * obj) +{ + g_return_val_if_fail (VN_IS_COMBO (obj), 0); + + return obj->show_column; +} + +/** + * vn_combo_set_show_column: + * @obj: a #VnCombo + * @column: the column + * + * Sets the column shown by combo. + **/ +void vn_combo_set_show_column (VnCombo * obj, guint column) +{ + g_return_if_fail (VN_IS_COMBO (obj)); + + obj->show_column = column; + gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (obj->combo), obj->cell); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (obj->combo), + obj->cell, "text", (gint) obj->show_column); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_INDEX_COLUMN = 1 + ,PROP_SHOW_COLUMN + ,PROP_MODEL + ,PROP_CONN + ,PROP_SQL +}; + +static void vn_combo_set_property (VnCombo * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_INDEX_COLUMN: + vn_combo_set_index_column (obj, g_value_get_uint (value)); + break; + case PROP_SHOW_COLUMN: + vn_combo_set_show_column (obj, g_value_get_uint (value)); + break; + case PROP_MODEL: + vn_combo_set_model (obj, g_value_get_object (value)); + break; + case PROP_CONN: + vn_combo_set_conn (obj, g_value_get_object (value)); + break; + case PROP_SQL: + { + const gchar * sql = g_value_get_string (value); + + if (sql) + { + DbModel * model = db_model_new_with_sql (NULL, g_value_get_string (value)); + vn_combo_set_model (obj, model); + g_object_unref (model); + } + + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void vn_combo_get_property (VnCombo * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_INDEX_COLUMN: + g_value_set_uint (value, obj->index_column); + break; + case PROP_SHOW_COLUMN: + g_value_set_uint (value, obj->show_column); + break; + case PROP_MODEL: + g_value_set_object (value, obj->model); + break; + case PROP_CONN: + g_value_set_object (value, db_model_get_conn (obj->model)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_combo_init (VnCombo * obj) +{ + obj->tree = NULL; + obj->model = NULL; + + obj->combo = GTK_COMBO_BOX (gtk_combo_box_new ()); + g_signal_connect (obj->combo, "changed", + G_CALLBACK (vn_combo_cb_changed), obj); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->combo)); + + obj->cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (obj->combo), obj->cell, TRUE); + + VN_FIELD (obj)->field = GTK_WIDGET (obj->combo); +} + +static void vn_combo_finalize (VnCombo * obj) +{ + vn_combo_set_model (obj, NULL); + G_OBJECT_CLASS (vn_combo_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_combo_class_init (VnComboClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_combo_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_combo_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_combo_get_property; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_combo_set_value; + + g_object_class_install_property (k, PROP_INDEX_COLUMN, + g_param_spec_uint ("index-column" + ,_("Index column") + ,_("The column index of the model") + ,0, 255, 0 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SHOW_COLUMN, + g_param_spec_uint ("show-column" + ,_("Show column") + ,_("The column of the model shown by combo") + ,0, 255, 1 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_MODEL, + g_param_spec_object ("model" + ,_("Model") + ,_("The model from which the combo takes the values shown in the list") + ,DB_TYPE_MODEL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by the model") + ,DB_TYPE_CONN + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("The SQL query used to create the model") + , NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE + )); +} diff --git a/vn/field/vn-combo.h b/vn/field/vn-combo.h new file mode 100644 index 0000000..34436a7 --- /dev/null +++ b/vn/field/vn-combo.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_COMBO_H +#define VN_COMBO_H + +#include + +#define VN_TYPE_COMBO (vn_combo_get_type ()) +#define VN_COMBO(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COMBO , VnCombo)) +#define VN_IS_COMBO(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COMBO)) +#define VN_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COMBO, VnComboClass)) +#define VN_IS_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COMBO)) +#define VN_COMBO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COMBO, VnComboClass)) + +typedef struct _VnCombo VnCombo; +typedef struct _VnComboClass VnComboClass; + +struct _VnCombo +{ + VnField parent; + DbModel * model; + GtkComboBox * combo; + GtkCellRenderer * cell; + GtkTreeModel * tree; + guint show_column; + guint index_column; +}; + +struct _VnComboClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_combo_get_type (); +VnField * vn_combo_new (DbModel * model); +VnField * vn_combo_new_with_sql (DbConn * conn, const gchar * sql); +void vn_combo_set_conn (VnCombo * obj, DbConn * conn); +void vn_combo_set_model (VnCombo * obj, DbModel * model); +guint vn_combo_get_index_column (VnCombo * obj); +void vn_combo_set_index_column (VnCombo * obj, guint column); +guint vn_combo_get_show_column (VnCombo * obj); +void vn_combo_set_show_column (VnCombo * obj, guint column); + +#endif \ No newline at end of file diff --git a/vn/field/vn-completion.c b/vn/field/vn-completion.c new file mode 100644 index 0000000..5e00843 --- /dev/null +++ b/vn/field/vn-completion.c @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-completion.h" +#include +#include +#include + +#define set_icon(obj,icon_name) (gtk_entry_set_icon_from_icon_name (obj->entry, GTK_ENTRY_ICON_SECONDARY, icon_name)) + +/** + * SECTION:vn-completion + * @Short_description: an auto-completable text box + * @Title: VnCompletion + * @See_also: #VnField + * @Image: completion.png + * + * A text box widget that auto-completes as the user writes using the data + * retrieved from a database. + */ +G_DEFINE_TYPE (VnCompletion, vn_completion, VN_TYPE_FIELD); + +/** + * vn_completion_new: + * @sql: the SQL string used by completion. + * @field: the field where to search the values. + * + * Creates a new #VnCompletion. + * + * Return value: a #VnCompletion. + **/ +VnField * vn_completion_new (const gchar * sql, const gchar * field) +{ + return g_object_new (VN_TYPE_COMPLETION, "sql", sql, "field", field, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_completion_cb_status_changed (DbModel * model, DbModelStatus status, VnCompletion * obj) +{ + const gchar * icon_name; + + if (status == DB_MODEL_STATUS_READY) + { + GtkTreeModel * tree = GTK_TREE_MODEL (vn_model_new (model)); + gtk_entry_completion_set_model (obj->completion, tree); + g_signal_emit_by_name (obj->entry, "changed"); + g_object_unref (tree); + icon_name = NULL; + } + else + { + switch (status) + { + case DB_MODEL_STATUS_LOADING: + icon_name = "edit-find"; + break; + case DB_MODEL_STATUS_ERROR: + icon_name = "edit-delete"; + break; + default: + icon_name = NULL; + } + + gtk_entry_completion_set_model (obj->completion, NULL); + } + + set_icon (obj, icon_name); +} + +static void vn_completion_cb_changed (GtkEditable * entry, VnCompletion * obj) +{ + const gchar * text = gtk_entry_get_text (GTK_ENTRY (entry)); + + if (obj->invalid) + { + obj->invalid = FALSE; + set_icon (obj, NULL); + } + + if (obj->sql && strlen (text) == 1 + && (!obj->last_match || g_strcmp0 (text, obj->last_match))) + { + gchar * pattern; + GValue value = {0}; + + set_icon (obj, "edit-find"); + + g_free (obj->last_match); + obj->last_match = g_strdup (text); + pattern = g_strconcat (text, "%", NULL); + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, pattern); + sql_value_set_value (SQL_VALUE (obj->value), &value); + g_value_unset (&value); + + g_free (pattern); + } +} + +static void vn_completion_iter_changed (VnCompletion * obj, DbIter * iter) +{ + const GValue * value = db_model_get_value (obj->model, iter, 0, NULL); + + if (value) + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), value); + + set_icon (obj, "gtk-apply"); +} + +static void vn_completion_cb_activate (GtkEntry * entry, VnCompletion * obj) +{ + gboolean ok = FALSE; + const gchar * text = gtk_entry_get_text (entry); + + if (text && g_strcmp0 (text, "")) + { + DbModel * model = obj->model; + + if (db_model_get_column_type (model, obj->column) == G_TYPE_STRING) + { + DbIter iter; + const GValue * v; + + if (db_model_get_iter_first (model, &iter)) + { + do { + v = db_model_get_value (model, &iter, obj->column, NULL); + + if (!gvn_value_is_null (v) + && !g_ascii_strcasecmp (g_value_get_string (v), text)) + { + vn_completion_iter_changed (obj, &iter); + ok = TRUE; + } + } + while (!ok && db_model_iter_next (model, &iter)); + } + } + } + + if (!ok) + { + GValue value = {0}; + + g_value_init (&value, GVN_TYPE_NULL); + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + g_value_unset (&value); + + if (g_strcmp0 (text, "")) + { + obj->invalid = TRUE; + set_icon (obj, "edit-delete"); + } + else + set_icon (obj, NULL); + } +} + +static gboolean vn_completion_match_selected (GtkEntryCompletion * completion, + GtkTreeModel * model, GtkTreeIter * tree_iter, VnCompletion * obj) +{ + DbIter iter; + + vn_gtk_tree_iter_to_db_iter (tree_iter, &iter); + vn_completion_iter_changed (obj, &iter); + + return FALSE; +} + +static void vn_completion_create_model (VnCompletion * obj) +{ + SqlExpr * field; + SqlString * stmt; + SqlOperation * op; + + if (!(obj->sql && obj->field)) + return; + + op = sql_operation_new (SQL_OPERATION_TYPE_LIKE); + + field = sql_field_new (obj->field, NULL, NULL); + sql_operation_add_expr (op, field); + + obj->value = sql_value_new (); + sql_operation_add_expr (op, obj->value); + + stmt = sql_string_new (obj->sql); + sql_string_add_expr (stmt, field); + sql_string_add_expr (stmt, SQL_EXPR (op)); + db_model_set_stmt (obj->model, SQL_STMT (stmt)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_CONN = 1 + ,PROP_SQL + ,PROP_FIELD +}; + +static void vn_completion_set_property (VnCompletion * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_CONN: + db_model_set_conn (obj->model, g_value_get_object (value)); + break; + case PROP_SQL: + obj->sql = g_value_dup_string (value); + vn_completion_create_model (obj); + break; + case PROP_FIELD: + obj->field = g_value_dup_string (value); + vn_completion_create_model (obj); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void vn_completion_get_property (VnCompletion * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_CONN: + g_value_set_object (value, db_model_get_conn (obj->model)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_completion_init (VnCompletion * obj) +{ + GtkEntryCompletion * completion; + + obj->sql = NULL; + obj->field = NULL; + obj->last_match = NULL; + obj->invalid = FALSE; + obj->column = 1; + + obj->entry = GTK_ENTRY (gtk_entry_new ()); + gtk_entry_set_icon_sensitive (obj->entry, GTK_ENTRY_ICON_SECONDARY, FALSE); + g_object_connect (obj->entry, + "signal::changed", vn_completion_cb_changed, obj + ,"signal::activate", vn_completion_cb_activate, obj + ,NULL + ); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->entry)); + VN_FIELD (obj)->field = GTK_WIDGET (obj->entry); + + completion = gtk_entry_completion_new (); + gtk_entry_completion_set_minimum_key_length (completion, 1); + gtk_entry_completion_set_text_column (completion, obj->column); + gtk_entry_completion_set_inline_selection (completion, FALSE); +// gtk_entry_completion_set_inline_completion (completion, TRUE); + g_signal_connect (completion, "match-selected", + G_CALLBACK (vn_completion_match_selected), obj); + gtk_entry_set_completion (obj->entry, completion); + obj->completion = completion; + + obj->model = db_model_new (NULL, NULL); + g_signal_connect (obj->model, "status-changed", + G_CALLBACK (vn_completion_cb_status_changed), obj); +} + +static void vn_completion_finalize (VnCompletion * obj) +{ + g_free (obj->sql); + g_free (obj->field); + g_free (obj->last_match); + g_object_unref (obj->completion); + g_object_unref (obj->model); + G_OBJECT_CLASS (vn_completion_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_completion_class_init (VnCompletionClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_completion_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_completion_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_completion_get_property; + + g_object_class_install_property (k, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by the model") + ,DB_TYPE_CONN + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("The SQL query used to create the model") + ,NULL + ,G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + )); + g_object_class_install_property (k, PROP_FIELD, + g_param_spec_string ("field" + ,_("Field") + ,_("The name of the field used for the search") + ,NULL + ,G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/vn/field/vn-completion.h b/vn/field/vn-completion.h new file mode 100644 index 0000000..7bbac31 --- /dev/null +++ b/vn/field/vn-completion.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_COMPLETION_H +#define VN_COMPLETION_H + +#include + +#define VN_TYPE_COMPLETION (vn_completion_get_type ()) +#define VN_COMPLETION(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COMPLETION, VnCompletion)) +#define VN_IS_COMPLETION(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COMPLETION)) +#define VN_COMPLETION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COMPLETION, VnCompletionClass)) +#define VN_IS_COMPLETION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COMPLETION)) +#define VN_COMPLETION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COMPLETION, VnCompletionClass)) + +typedef struct _VnCompletion VnCompletion; +typedef struct _VnCompletionClass VnCompletionClass; + +struct _VnCompletion +{ + VnField parent; + GtkEntry * entry; + GtkEntryCompletion * completion; + gboolean invalid; + guint column; + gchar * last_match; + DbModel * model; + SqlExpr * value; + gchar * field; + gchar * sql; +}; + +struct _VnCompletionClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_completion_get_type (); +VnField * vn_completion_new (const gchar * sql, const gchar * field); + +#endif \ No newline at end of file diff --git a/vn/field/vn-date-chooser.c b/vn/field/vn-date-chooser.c new file mode 100644 index 0000000..c356f95 --- /dev/null +++ b/vn/field/vn-date-chooser.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-date-chooser.h" +#include + +/** + * SECTION:vn-date-chooser + * @Short_description: an embedded popup date selector + * @Title: VnDateChooser + * @See_also: #VnField + * @Image: date-chooser.png + * + * An embedded button and text field that pops-up a #GtkCalendar. + */ +G_DEFINE_TYPE (VnDateChooser, vn_date_chooser, VN_TYPE_FIELD); + +/** + * vn_date_chooser_new: + * + * Creates a new #VnDateChooser + * + * Return value: a #VnDateChooser + **/ +VnField * vn_date_chooser_new () +{ + return VN_FIELD (g_object_new (VN_TYPE_DATE_CHOOSER, NULL)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_date_chooser_hide_popup (VnDateChooser * obj) +{ + if (obj->device) + { + gdk_device_ungrab (obj->device, GDK_CURRENT_TIME); + gtk_device_grab_remove (obj->popup, obj->device); + } + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (obj->button), FALSE); + gtk_widget_hide (obj->popup); +} + +static void vn_date_chooser_changed (VnDateChooser * obj) +{ + GDate * date = &obj->date; + + if (g_date_valid (date)) + { + gchar * str; + GDateWeekday wday; + GDateMonth month; + + month = g_date_get_month (date); + wday = g_date_get_weekday (date); + + str = g_strdup_printf (_("%s, %u %s %u") + ,GVN_ABR_WDAY[wday] + ,g_date_get_day (date) + ,GVN_ABR_MONTH[month] + ,g_date_get_year (date) + ); + gtk_label_set_text (obj->label, str); + g_free (str); + } + else + gtk_label_set_text (obj->label, ""); +} + +static void vn_date_chooser_set_value (VnDateChooser * obj, const GValue * value) +{ + GDate * date = NULL; + + if (G_VALUE_TYPE (value) != G_TYPE_DATE) + { + GValue new_value = {0}; + g_value_init (&new_value, G_TYPE_DATE); + g_value_transform (value, &new_value); + date = g_value_get_boxed (&new_value); + } + else if (!gvn_value_is_null (value)) + date = g_value_get_boxed (value); + + if (date) + obj->date = *date; + else + g_date_clear (&obj->date, 1); + + vn_date_chooser_changed (obj); +} + +static void vn_date_chooser_on_day_selected (GtkCalendar * calendar, VnDateChooser * obj) +{ + guint year; + guint month; + guint day; + GDate new_date; + GValue value = {0}; + + gtk_calendar_get_date (calendar, &year, &month, &day); + month++; + + g_date_clear (&new_date, 1); + g_date_set_dmy (&new_date, day, month, year); + + if (!g_date_valid (&obj->date) || g_date_compare (&new_date, &obj->date)) + { + g_date_set_julian (&obj->date, g_date_get_julian (&new_date)); + g_value_init (&value, G_TYPE_DATE); + g_value_set_boxed (&value, &obj->date); + } + else + { + g_value_init (&value, GVN_TYPE_NULL); + g_date_clear (&obj->date, 1); + } + + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + g_value_unset (&value); + + vn_date_chooser_hide_popup (obj); + vn_date_chooser_changed (obj); +} + +static gboolean vn_date_chooser_on_buton_press (GtkWidget * widget, + GdkEventButton * event, VnDateChooser * obj) +{ + gint x, y; + GtkAllocation allocation; + + gdk_window_get_origin ( + gtk_widget_get_window (obj->popup), &x, &y); + gtk_widget_get_allocation (obj->popup, &allocation); + + if (!( event->x_root >= x && event->x_root <= x + allocation.width + && event->y_root >= y && event->y_root <= y + allocation.height)) + { + vn_date_chooser_hide_popup (obj); + return TRUE; + } + + return FALSE; +} + +static gboolean vn_date_chooser_on_key_press (GtkWidget * widget, + GdkEventKey * event, VnDateChooser * obj) +{ + if (event->keyval == GDK_KEY_Escape) + { + vn_date_chooser_hide_popup (obj); + return TRUE; + } + + return FALSE; +} + +static void vn_date_chooser_on_toggled (GtkToggleButton * button, VnDateChooser * obj) +{ + if (gtk_toggle_button_get_active (button)) + { + gint x, y; + GdkWindow * window; + GdkScreen * screen; + GtkRequisition req; + GdkRectangle monitor; + GtkAllocation allocation; + GtkWidget * widget = GTK_WIDGET (button); + + // Set the date on the calendar + + if (g_date_valid (&obj->date)) + { + gtk_calendar_select_month (obj->calendar + ,g_date_get_month (&obj->date) - 1 + ,g_date_get_year (&obj->date) + ); + gtk_calendar_select_day (obj->calendar, + g_date_get_day (&obj->date)); + } + else + gtk_calendar_select_day (obj->calendar, 0); + + // Setting the position of the popup + + window = gtk_widget_get_window (widget); + + gtk_widget_get_allocation (widget, &allocation); + + gdk_window_get_origin (window, &x, &y); + x += allocation.x; + y += allocation.y; + + screen = gtk_widget_get_screen (widget); + gdk_screen_get_monitor_geometry (screen, + gdk_screen_get_monitor_at_point (screen, x, y), &monitor); + + gtk_widget_show (GTK_WIDGET (obj->calendar)); + gtk_widget_get_preferred_size (obj->popup, &req, NULL); + + if (y - monitor.y > monitor.height) + y = monitor.y + monitor.height - req.height; + else if ((y - monitor.y) + req.height + allocation.height > monitor.height) + y -= req.height; + else + y += allocation.height; + + if ((x + allocation.width) - monitor.x > monitor.width) + x = monitor.x + monitor.width - req.width; + else if ((x - monitor.x) + req.width > monitor.width) + x -= req.width - allocation.width; + + gtk_window_set_screen (GTK_WINDOW (obj->popup), screen); + gtk_window_move (GTK_WINDOW (obj->popup), x, y); + gtk_widget_show_all (obj->popup); + + // Graving the focus on the popup window + + obj->device = gtk_get_current_event_device (); + + if (obj->device && gdk_device_get_source (obj->device) == GDK_SOURCE_KEYBOARD) + obj->device = gdk_device_get_associated_device (obj->device); + + if (!obj->device) + { + GList * devices; + GdkDisplay * display; + GdkDeviceManager * device_manager; + + display = gtk_widget_get_display (GTK_WIDGET (obj->popup)); + device_manager = gdk_display_get_device_manager (display); + + devices = gdk_device_manager_list_devices (device_manager, + GDK_DEVICE_TYPE_MASTER); + obj->device = devices->data; + g_list_free (devices); + } + +// gtk_widget_grab_focus (obj->popup); + gtk_device_grab_add (obj->popup, obj->device, TRUE); + gdk_device_grab (obj->device + ,gtk_widget_get_window (obj->popup) + ,GDK_OWNERSHIP_WINDOW, TRUE + ,GDK_BUTTON_PRESS_MASK, NULL + ,GDK_CURRENT_TIME + ); + } + else + vn_date_chooser_hide_popup (obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_date_chooser_init (VnDateChooser * obj) +{ + GtkWidget * image; + + obj->popup = NULL; + obj->device = NULL; + g_date_clear (&obj->date, 1); + + obj->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->box)); + VN_FIELD (obj)->field = obj->box; + + obj->button = gtk_toggle_button_new (); + gtk_widget_set_tooltip_text (GTK_WIDGET (obj->button), _("Change date")); + g_signal_connect (obj->button, "toggled", + G_CALLBACK (vn_date_chooser_on_toggled), obj); + gtk_box_pack_start (GTK_BOX (obj->box), + GTK_WIDGET (obj->button), FALSE, FALSE, 0); + + image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON); + gtk_button_set_image (GTK_BUTTON (obj->button), image); + + obj->label = GTK_LABEL (gtk_label_new (NULL)); + gtk_misc_set_alignment (GTK_MISC (obj->label), 0, 0.5); + gtk_box_pack_end (GTK_BOX (obj->box), + GTK_WIDGET (obj->label), TRUE, TRUE, 0); + + obj->popup = gtk_window_new (GTK_WINDOW_POPUP); + g_object_connect (obj->popup + ,"signal::button-press-event", vn_date_chooser_on_buton_press, obj + ,"signal::key-press-event", vn_date_chooser_on_key_press, obj + ,NULL + ); + + obj->calendar = GTK_CALENDAR (gtk_calendar_new ()); + g_signal_connect (obj->calendar, "day-selected-double-click", + G_CALLBACK (vn_date_chooser_on_day_selected), obj); + gtk_container_add (GTK_CONTAINER (obj->popup), GTK_WIDGET (obj->calendar)); +} + +static void vn_date_chooser_finalize (VnDateChooser * obj) +{ + gtk_widget_destroy (obj->popup); + G_OBJECT_CLASS (vn_date_chooser_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_date_chooser_class_init (VnDateChooserClass * klass) +{ + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) vn_date_chooser_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_date_chooser_set_value; +} diff --git a/vn/field/vn-date-chooser.h b/vn/field/vn-date-chooser.h new file mode 100644 index 0000000..2d7135f --- /dev/null +++ b/vn/field/vn-date-chooser.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_DATE_CHOOSER_H +#define VN_DATE_CHOOSER_H + +#include + +#define VN_TYPE_DATE_CHOOSER (vn_date_chooser_get_type ()) +#define VN_DATE_CHOOSER(object) (G_TYPE_CHECK_INSTANCE_CAST (object, VN_TYPE_DATE_CHOOSER , VnDateChooser)) +#define VN_IS_DATE_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_DATE_CHOOSER)) +#define VN_DATE_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_DATE_CHOOSER, VnDateChooserClass)) +#define VN_IS_DATE_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_DATE_CHOOSER)) +#define VN_DATE_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_DATE_CHOOSER, VnDateChooserClass)) + +typedef struct _VnDateChooser VnDateChooser; +typedef struct _VnDateChooserClass VnDateChooserClass; + +struct _VnDateChooser +{ + VnField parent; + GtkWidget * box; + GtkLabel * label; + GtkWidget * popup; + GtkWidget * button; + GdkDevice * device; + GtkCalendar * calendar; + GDate date; +}; + +struct _VnDateChooserClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_date_chooser_get_type (); +VnField * vn_date_chooser_new (); + +#endif \ No newline at end of file diff --git a/vn/field/vn-entry.c b/vn/field/vn-entry.c new file mode 100644 index 0000000..18d7ab7 --- /dev/null +++ b/vn/field/vn-entry.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-entry.h" +#include + +/** + * SECTION:vn-entry + * @Short_description: a text box widget + * @Title: VnEntry + * @See_also: #VnField + * @Image: entry.png + * + * A text box representing a generic value field. + */ +G_DEFINE_TYPE (VnEntry, vn_entry, VN_TYPE_FIELD); + +/** + * vn_entry_new: + * + * Creates a new #VnEntry + * + * Return value: a #VnEntry + **/ +VnField * vn_entry_new (VnEntry * obj) +{ + return g_object_new (VN_TYPE_ENTRY, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_entry_cb_editing_done (GtkEntry * entry, VnField * obj) +{ + GValue value = {0}; + const gchar * text = gtk_entry_get_text (entry); + + if (g_strcmp0 (text, "") || !vn_field_get_null (obj)) + { + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, text); + } + else + g_value_init (&value, GVN_TYPE_NULL); + + VN_FIELD_GET_CLASS (obj)->value_changed (obj, &value); + g_value_unset (&value); +} + +static gboolean vn_entry_cb_focus_out (GtkEntry * entry, GdkEvent * event, VnField * obj) +{ + vn_entry_cb_editing_done (entry, obj); + return FALSE; +} + +static void vn_entry_set_value (VnEntry * obj, const GValue * value) +{ + GValue new_value = {0}; + gvn_value_to_format_string (value, obj->digits, &new_value); + + g_signal_handlers_block_by_func (obj->entry, + vn_entry_cb_editing_done, obj); + g_signal_handlers_block_by_func (obj->entry, + vn_entry_cb_focus_out, obj); + + gtk_entry_set_text (obj->entry, + g_value_get_string (&new_value)); + + g_signal_handlers_unblock_by_func (obj->entry, + vn_entry_cb_editing_done, obj); + g_signal_handlers_unblock_by_func (obj->entry, + vn_entry_cb_focus_out, obj); + + g_value_unset (&new_value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_DIGITS = 1 +}; + +static void vn_entry_set_property (VnEntry * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + obj->digits = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_entry_get_property (VnEntry * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + g_value_set_uint (value, obj->digits); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_entry_init (VnEntry * obj) +{ + obj->entry = GTK_ENTRY (gtk_entry_new ()); + g_object_connect (obj->entry + ,"signal::activate", vn_entry_cb_editing_done, obj + ,"signal::focus-out-event", vn_entry_cb_focus_out, obj + ,NULL + ); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->entry)); + + VN_FIELD (obj)->field = GTK_WIDGET (obj->entry); +} + +static void vn_entry_finalize (VnEntry * obj) +{ + G_OBJECT_CLASS (vn_entry_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_entry_class_init (VnEntryClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->set_property = (GObjectSetPropertyFunc) vn_entry_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_entry_get_property; + k->finalize = (GObjectFinalizeFunc) vn_entry_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_entry_set_value; + + g_object_class_install_property (k, PROP_DIGITS, + g_param_spec_uint ("digits" + ,_("Digits") + ,_("The number of decimal places to display.") + ,0 ,20 ,0 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} diff --git a/vn/field/vn-entry.h b/vn/field/vn-entry.h new file mode 100644 index 0000000..24cbe96 --- /dev/null +++ b/vn/field/vn-entry.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_ENTRY_H +#define VN_ENTRY_H + +#include + +#define VN_TYPE_ENTRY (vn_entry_get_type ()) +#define VN_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_ENTRY, VnEntry)) +#define VN_IS_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_ENTRY)) +#define VN_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_ENTRY, VnEntryClass)) +#define VN_IS_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_ENTRY)) +#define VN_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_ENTRY, VnEntryClass)) + +typedef struct _VnEntry VnEntry; +typedef struct _VnEntryClass VnEntryClass; + +struct _VnEntry +{ + VnField parent; + GtkEntry * entry; + guint digits; + +}; + +struct _VnEntryClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_entry_get_type (); +VnField * vn_entry_new (); + +#endif \ No newline at end of file diff --git a/vn/field/vn-http-image.c b/vn/field/vn-http-image.c new file mode 100644 index 0000000..ec5c6c0 --- /dev/null +++ b/vn/field/vn-http-image.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-http-image.h" +#include + +/* + * img: an icon name + * msg: text message or NULL + */ +#define set_icon(img, msg) \ + g_object_set \ + (obj->priv->image, "icon-name", img, \ + "icon-size", GTK_ICON_SIZE_MENU, NULL); \ + gtk_widget_set_tooltip_text (GTK_WIDGET (obj->priv->image), msg) + +/** + * SECTION: vn-http-image + * @Short_description: an image from an http server + * @Title: VnHttpImage + * + * The image is loaded using a, previously created, #DbFileLoader. + */ + +struct _VnHttpImagePrivate +{ + DbFileLoader * loader; + gchar * path; + GtkImage * image; + GBytes * bytes; +}; + +G_DEFINE_TYPE (VnHttpImage, vn_http_image, VN_TYPE_FIELD); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +/** + * vn_http_image_new: + * @loader: a #DbFileLoader + * @name: (allow-none): the name of the file, with or without type extension + * + * Create an image named after @name located in an HTTP server. + * + * Return value: a #VnHttpImage + **/ +VnField * vn_http_image_new (DbFileLoader * loader, const gchar * name) +{ + g_return_val_if_fail (DB_IS_FILE_LOADER (loader), NULL); + + return g_object_new (VN_TYPE_HTTP_IMAGE, "name", name, "loader", loader, NULL); +} + +static void vn_http_image_cb_error (VnHttpImage * obj, const GError * error) +{ + gchar * e = error ? error->message : _("Undefined error"); + const gchar * name = g_value_get_string (vn_field_get_value (VN_FIELD (obj))); + g_warning ("VnHttpImage (%s%s): %s", obj->priv->path, name, e); + set_icon ("image-missing", e); +} + +static void vn_http_image_cb_download + (DbFileLoader * fl, GBytes * bytes, const GError * error, VnHttpImage * obj) +{ + gtk_widget_set_tooltip_text (GTK_WIDGET (obj->priv->image), NULL); + + if (error) + { + vn_http_image_cb_error (obj, error); + return; + } + + if (bytes) + { + gsize len; + const gchar * data = g_bytes_get_data (bytes, &len); + GError * err = NULL; + GdkPixbufLoader * loader = gdk_pixbuf_loader_new (); + + if (gdk_pixbuf_loader_write (loader, (guchar *) data, len, &err) + && gdk_pixbuf_loader_close (loader, &err)) + { + GdkPixbuf * pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + gtk_image_set_from_pixbuf (obj->priv->image, pixbuf); + obj->priv->bytes = g_bytes_ref (bytes); + } + else + { + vn_http_image_cb_error (obj, err); + g_error_free (err); + } + + g_object_unref (loader); + } +} +/* +static void vn_http_image_cb_upload + (DbFileLoader * loader, GBytes * data, GError * error, VnHttpImage * obj) +{} +*/ +static void vn_http_image_set_value (VnHttpImage * obj, const GValue * value) +{ + gchar * full_path = g_strconcat (obj->priv->path, "/", + g_value_get_string (value), NULL); + set_icon ("view-refresh", _("Loading")); + + db_file_loader_download (obj->priv->loader, full_path, + (DbFileLoaderCallbackFunc) vn_http_image_cb_download, obj); + g_free (full_path); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_PATH = 1 + ,PROP_LOADER + ,PROP_BYTES +}; + +static void vn_http_image_set_property (VnHttpImage * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PATH: + g_free (obj->priv->path); + obj->priv->path = g_value_dup_string (value); + break; + case PROP_LOADER: + if (obj->priv->loader) + g_warning (_("File loader already set")); + else + obj->priv->loader = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_http_image_get_property (VnHttpImage * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PATH: + g_value_set_string (value, obj->priv->path); + break; + case PROP_LOADER: + g_value_set_object (value, obj->priv->loader); + break; + case PROP_BYTES: + g_value_set_boxed (value, obj->priv->bytes); + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_http_image_init (VnHttpImage * obj) +{ + obj->priv = G_TYPE_INSTANCE_GET_PRIVATE + (obj, VN_TYPE_HTTP_IMAGE, VnHttpImagePrivate); + + obj->priv->path = g_strdup (""); + obj->priv->loader = NULL; + obj->priv->image = GTK_IMAGE (gtk_image_new ()); +//Se usará para subir imagenes y cambiar nombres, o abrir un menu contextual +// g_signal_connect (obj, "event", G_CALLBACK (vn_http_image_cb_event), obj); + set_icon ("image-missing", _("No image set")); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->priv->image)); + + VN_FIELD (obj)->field = GTK_WIDGET (obj->priv->image); +} + +static void vn_http_image_finalize (VnHttpImage * obj) +{ + g_clear_object (&obj->priv->loader); + g_free (obj->priv->path); + g_bytes_unref (obj->priv->bytes); + G_OBJECT_CLASS (vn_http_image_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_http_image_class_init (VnHttpImageClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->set_property = (GObjectSetPropertyFunc) vn_http_image_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_http_image_get_property; + k->finalize = (GObjectFinalizeFunc) vn_http_image_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_http_image_set_value; + + g_type_class_add_private (klass, sizeof (VnHttpImagePrivate)); + + g_object_class_install_property (k, PROP_LOADER, + g_param_spec_object ("loader" + ,_("File loader") + ,_("A DbFileLoader, used to download the files") + ,DB_TYPE_FILE_LOADER + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_PATH, + g_param_spec_string ("path" + ,_("File path") + ,_("The relative path to the image from file loader path") + ,NULL + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_BYTES, + g_param_spec_boxed ("bytes" + ,_("Image bytes") + ,_("A GBytes structure with the image data") + ,G_TYPE_BYTES + ,G_PARAM_READABLE + )); +} diff --git a/vn/field/vn-http-image.h b/vn/field/vn-http-image.h new file mode 100644 index 0000000..50e85db --- /dev/null +++ b/vn/field/vn-http-image.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_HTTP_IMAGE_H +#define VN_HTTP_IMAGE_H + +#include + +#define VN_TYPE_HTTP_IMAGE (vn_http_image_get_type ()) +#define VN_HTTP_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_HTTP_IMAGE, VnHttpImage)) +#define VN_IS_HTTP_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_HTTP_IMAGE)) +#define VN_HTTP_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_HTTP_IMAGE, VnHttpImageClass)) +#define VN_IS_HTTP_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_HTTP_IMAGE)) +#define VN_HTTP_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_HTTP_IMAGE, VnHttpImageClass)) + +typedef struct _VnHttpImage VnHttpImage; +typedef struct _VnHttpImageClass VnHttpImageClass; +typedef struct _VnHttpImagePrivate VnHttpImagePrivate; + +struct _VnHttpImage +{ + VnField parent; + VnHttpImagePrivate * priv; +}; + +struct _VnHttpImageClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_http_image_get_type (); +VnField * vn_http_image_new (DbFileLoader * loader + ,const gchar * name); + +#endif diff --git a/vn/field/vn-image.c b/vn/field/vn-image.c new file mode 100644 index 0000000..075232d --- /dev/null +++ b/vn/field/vn-image.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-image.h" +#include + +#define set_default_image(obj) g_object_set (obj->image, \ + "icon-name", "dialog-question", "icon-size", GTK_ICON_SIZE_DIALOG, NULL) + +/** + * SECTION:vn-image + * @Short_description: an image widget + * @Title: VnImage + * @See_also: #VnField + * @Image: image.png + * + * An image widget that allows to set a new image with a double click. + */ +G_DEFINE_TYPE (VnImage, vn_image, VN_TYPE_FIELD); + +/** + * vn_image_new: + * + * Creates a new #VnImage + * + * Return value: a #VnImage + **/ +VnField * vn_image_new () +{ + return g_object_new (VN_TYPE_IMAGE, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_image_set_value (VnImage * obj, const GValue * value) +{ + GBytes * bytes = NULL; + + if (!gvn_value_is_null (value) + && G_VALUE_TYPE (value) == G_TYPE_BYTES) + bytes = g_value_get_boxed (value); + + if (bytes) + { + gsize size; + const guchar * data; + GError * err = NULL; + GdkPixbufLoader * loader; + + loader = gdk_pixbuf_loader_new (); + data = g_bytes_get_data (bytes, &size); + + if (gdk_pixbuf_loader_write (loader, data, size, &err) + && gdk_pixbuf_loader_close (loader, &err)) + { + GdkPixbuf * pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + gtk_image_set_from_pixbuf (obj->image, pixbuf); + } + else + { + g_warning ("%s",err->message); + g_error_free (err); + set_default_image (obj); + } + + g_object_unref (loader); + } + else + set_default_image (obj); +} + +static void vn_image_cb_update_preview (GtkFileChooser * chooser, GtkImage * image) +{ + char *filename; + GdkPixbuf *pixbuf; + + filename = gtk_file_chooser_get_preview_filename (chooser); + + if (filename) + { + pixbuf = gdk_pixbuf_new_from_file_at_size (filename, 128, 128, NULL); + g_free (filename); + + if (pixbuf) + { + gtk_image_set_from_pixbuf (image, pixbuf); + g_object_unref (pixbuf); + } + } + else + pixbuf = NULL; + + gtk_file_chooser_set_preview_widget_active (chooser, pixbuf != NULL); +} + +static gboolean vn_image_cb_event (VnField * field, GdkEvent * event, VnImage * obj) +{ + if (event->type != GDK_2BUTTON_PRESS) + return FALSE; + + GtkWidget * image; + GtkWidget * toplevel; + GtkFileChooser * chooser; + GtkFileFilter * filter; + + filter = gtk_file_filter_new (); + gtk_file_filter_add_mime_type (filter, "image/png"); + gtk_file_filter_add_mime_type (filter, "image/jpeg"); + gtk_file_filter_add_mime_type (filter, "image/gif"); + gtk_file_filter_add_mime_type (filter, "image/bmp"); + gtk_file_filter_add_mime_type (filter, "image/svg+xml"); + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (obj)); + + if (!gtk_widget_is_toplevel (toplevel)) + toplevel = NULL; + + chooser = GTK_FILE_CHOOSER (gtk_file_chooser_dialog_new ( + _("Select the image") + ,GTK_WINDOW (toplevel) + ,GTK_FILE_CHOOSER_ACTION_OPEN + ,"Cancel", GTK_RESPONSE_CANCEL + ,"Open", GTK_RESPONSE_ACCEPT + ,NULL + )); + gtk_file_chooser_set_filter (chooser, filter); + + image = gtk_image_new (); + gtk_file_chooser_set_preview_widget (chooser, image); + g_signal_connect (chooser, "update-preview", + G_CALLBACK (vn_image_cb_update_preview), image); + + if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT) + { + gsize len; + gchar * data; + gchar * filename; + GError * err = NULL; + + filename = gtk_file_chooser_get_filename (chooser); + + if (g_file_get_contents (filename, &data, &len, &err)) + { + GValue value = {0}; + g_value_init (&value, G_TYPE_BYTES); + g_value_take_boxed (&value, g_bytes_new_take (data, len)); + + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + vn_image_set_value (obj, &value); + g_value_unset (&value); + } + else + { + g_warning ("%s", err->message); + g_error_free (err); + } + + g_free (filename); + } + + gtk_widget_destroy (GTK_WIDGET (chooser)); + return FALSE; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_image_init (VnImage * obj) +{ + obj->image = GTK_IMAGE (gtk_image_new ()); + g_signal_connect (obj, "event", + G_CALLBACK (vn_image_cb_event), obj); + set_default_image (obj); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->image)); + VN_FIELD (obj)->field = GTK_WIDGET (obj->image); +} + +static void vn_image_finalize (VnImage * obj) +{ + G_OBJECT_CLASS (vn_image_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_image_class_init (VnImageClass * klass) +{ + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) vn_image_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_image_set_value; +} diff --git a/vn/field/vn-image.h b/vn/field/vn-image.h new file mode 100644 index 0000000..ba15377 --- /dev/null +++ b/vn/field/vn-image.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_IMAGE_H +#define VN_IMAGE_H + +#include + +#define VN_TYPE_IMAGE (vn_image_get_type ()) +#define VN_IMAGE(object) (G_TYPE_CHECK_INSTANCE_CAST (object, VN_TYPE_IMAGE , VnImage)) +#define VN_IS_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_IMAGE)) +#define VN_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_IMAGE, VnImageClass)) +#define VN_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_IMAGE)) +#define VN_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_IMAGE, VnImageClass)) + +typedef struct _VnImage VnImage; +typedef struct _VnImageClass VnImageClass; + +struct _VnImage +{ + VnField parent; + GtkImage * image; +}; + +struct _VnImageClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_image_get_type (); +VnField * vn_image_new (); + +#endif \ No newline at end of file diff --git a/vn/field/vn-spin.c b/vn/field/vn-spin.c new file mode 100644 index 0000000..d0c5a67 --- /dev/null +++ b/vn/field/vn-spin.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-spin.h" +#include + +/** + * SECTION:vn-spin + * @Short_description: an incrementable numeric field + * @Title: VnSpin + * @See_also: #VnField + * @Image: spin.png + * + * An incrementable numeric field + */ +G_DEFINE_TYPE (VnSpin, vn_spin, VN_TYPE_FIELD); + +/** + * vn_spin_new: + * + * Creates a new #VnSpin + * + * Return value: a #VnSpin + **/ +VnField * vn_spin_new () +{ + return g_object_new (VN_TYPE_SPIN, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_spin_on_value_changed (GtkSpinButton * spin, VnField * obj) +{ + GValue value = {0}; + + if (g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (spin)), "")) + { + g_value_init (&value, G_TYPE_DOUBLE); + g_value_set_double (&value, gtk_spin_button_get_value (spin)); + } + else + g_value_init (&value, GVN_TYPE_NULL); + + VN_FIELD_GET_CLASS (obj)->value_changed (obj, &value); + g_value_unset (&value); +} + +static gboolean vn_spin_cb_focus_out (GtkSpinButton * spin, GdkEvent * event, VnField * obj) +{ + vn_spin_on_value_changed (spin, obj); + return FALSE; +} + +static gboolean vn_spin_on_output (GtkSpinButton * spin, VnField * obj) +{ + const gchar * text = gtk_entry_get_text (GTK_ENTRY (spin)); + + return vn_field_get_null (obj) + && (!text || !g_strcmp0 (text, "")) + && gtk_spin_button_get_value (spin) == 0.0; +} + +static void vn_spin_set_value (VnSpin * obj, const GValue * value) +{ + g_signal_handlers_block_by_func (obj->spin, + vn_spin_on_value_changed, obj); + + if (!gvn_value_is_null (value)) + { + GValue new_value = {0}; + g_value_init (&new_value, G_TYPE_DOUBLE); + g_value_transform (value, &new_value); + gtk_spin_button_set_value (obj->spin, + g_value_get_double (&new_value)); + g_value_unset (&new_value); + } + else + gtk_entry_set_text (GTK_ENTRY (obj->spin), ""); + + g_signal_handlers_unblock_by_func (obj->spin, + vn_spin_on_value_changed, obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_DIGITS = 1 +}; + +static void vn_spin_set_property (VnSpin * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + gtk_spin_button_set_digits (obj->spin, g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_spin_get_property (VnSpin * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + g_value_set_uint (value, gtk_spin_button_get_digits (obj->spin)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_spin_init (VnSpin * obj) +{ + obj->spin = GTK_SPIN_BUTTON (gtk_spin_button_new (NULL, 1, 0)); + gtk_spin_button_set_range (obj->spin, G_MININT, G_MAXINT); + gtk_spin_button_set_update_policy (obj->spin, GTK_UPDATE_ALWAYS); + gtk_spin_button_set_numeric (obj->spin, TRUE); + gtk_spin_button_set_increments (obj->spin, 1, 1); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->spin)); + + g_object_connect (obj->spin +// ,"signal::activate", vn_spin_on_value_changed, obj + ,"signal::value-changed", vn_spin_on_value_changed, obj + ,"signal::focus-out-event", vn_spin_cb_focus_out, obj + ,"signal::output", vn_spin_on_output, obj + ,NULL + ); + g_signal_connect (obj->spin, "value-changed", + G_CALLBACK (vn_spin_on_value_changed), obj); + g_signal_connect (obj->spin, "output", + G_CALLBACK (vn_spin_on_output), obj); + + VN_FIELD (obj)->field = GTK_WIDGET (obj->spin); +} + +static void vn_spin_finalize (VnSpin * obj) +{ + G_OBJECT_CLASS (vn_spin_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_spin_class_init (VnSpinClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->set_property = (GObjectSetPropertyFunc) vn_spin_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_spin_get_property; + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) vn_spin_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_spin_set_value; + + g_object_class_install_property (k, PROP_DIGITS, + g_param_spec_uint ("digits" + ,_("Digits") + ,_("The number of decimal places to display.") + ,0 ,20 ,0 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} diff --git a/vn/field/vn-spin.h b/vn/field/vn-spin.h new file mode 100644 index 0000000..5f26a22 --- /dev/null +++ b/vn/field/vn-spin.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_SPIN_H +#define VN_SPIN_H + +#include + +#define VN_TYPE_SPIN (vn_spin_get_type ()) +#define VN_SPIN(object) (G_TYPE_CHECK_INSTANCE_CAST (object, VN_TYPE_SPIN , VnSpin)) +#define VN_IS_SPIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_SPIN)) +#define VN_SPIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_SPIN, VnSpinClass)) +#define VN_IS_SPIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_SPIN)) +#define VN_SPIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_SPIN, VnSpinClass)) + +typedef struct _VnSpin VnSpin; +typedef struct _VnSpinClass VnSpinClass; + +struct _VnSpin +{ + VnField parent; + GtkSpinButton * spin; +}; + +struct _VnSpinClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_spin_get_type (); +VnField * vn_spin_new (); + +#endif \ No newline at end of file diff --git a/vn/gui/actions.glade b/vn/gui/actions.glade new file mode 100644 index 0000000..8178cc0 --- /dev/null +++ b/vn/gui/actions.glade @@ -0,0 +1,82 @@ + + + + + + + _Logout + Logout + gtk-quit + contact-new + + + + + + + _Quit + application-exit + + + + + + + _About + help-about + + + + + + _File + system-file-manager + + + + + _Help + help-contents + + + + + _Reconnect + Reconnect + gtk-connect + + + + + + + _Close + window-close + + + + + + + _View + + + + + Dynamic _Tabs + Don't show tabs if there is only one tab open + True + + + + + + + Tool_bar + True + + + + + + diff --git a/vn/gui/child-window.glade b/vn/gui/child-window.glade new file mode 100644 index 0000000..067fc10 --- /dev/null +++ b/vn/gui/child-window.glade @@ -0,0 +1,47 @@ + + + + + + 400 + 300 + False + Hedera + True + ../image/icon.svg + + + + True + False + vertical + + + True + False + 4 + + + True + True + False + False + True + + + + + + + + + True + True + end + 1 + + + + + + diff --git a/vn/gui/login.glade b/vn/gui/login.glade new file mode 100644 index 0000000..017ff1d --- /dev/null +++ b/vn/gui/login.glade @@ -0,0 +1,403 @@ + + + + + + False + 5 + Configuration + False + True + ../image/icon.svg + dialog + window + + + + False + vertical + 2 + + + False + spread + + + gtk-cancel + False + True + True + True + False + True + + + + False + True + 0 + + + + + gtk-apply + False + True + True + True + False + True + + + + + + False + True + 1 + + + + + False + True + end + 0 + + + + + True + False + 6 + 6 + + + True + True + + True + + + 1 + 0 + 1 + 1 + + + + + True + True + + True + + + 1 + 1 + 1 + 1 + + + + + True + False + 1 + Plugin: + + + 0 + 0 + 1 + 1 + + + + + True + False + 1 + Host: + + + 0 + 1 + 1 + 1 + + + + + True + False + 1 + Schema: + + + 0 + 2 + 1 + 1 + + + + + True + True + + True + + + 1 + 2 + 1 + 1 + + + + + True + False + SSL CA: + + + 0 + 3 + 1 + 1 + + + + + True + True + + gtk-info + False + False + Path to the file containing the CA certificate, if this is empty SSL won't be used + + + 1 + 3 + 1 + 1 + + + + + False + True + 1 + + + + + + cancel + apply + + + + False + 15 + Access + False + center + ../image/icon.svg + + + + True + False + vertical + 10 + + + 40 + True + False + 15 + + + True + False + ../image/logo.svg + + + False + True + 0 + + + + + 25 + False + True + + + False + True + 1 + + + + + True + True + 0 + + + + + True + False + 6 + 6 + True + + + True + True + + True + + + 1 + 0 + 1 + 1 + + + + + True + False + 1 + User: + + + + + + 0 + 0 + 1 + 1 + + + + + True + False + 1 + Password: + + + + + + 0 + 1 + 1 + 1 + + + + + True + True + False + + True + + + 1 + 1 + 1 + 1 + + + + + Remember + False + True + True + False + False + 0 + True + + + 1 + 2 + 1 + 1 + + + + + + + + False + False + 1 + + + + + True + False + 8 + spread + + + gtk-edit + False + True + False + True + False + True + + + + False + True + 0 + + + + + gtk-connect + False + True + True + True + False + True + + + + + + False + True + 1 + + + + + False + True + 3 + + + + + + diff --git a/vn/gui/main.glade b/vn/gui/main.glade new file mode 100644 index 0000000..ffa3370 --- /dev/null +++ b/vn/gui/main.glade @@ -0,0 +1,181 @@ + + + + + + False + 5 + True + ../image/icon.svg + dialog + window + Hedera + Versión 1.0 + Copyright - Verdnatura Levante S. L. + Management and administration of companies + http://www.verdnatura.es + www.verdnatura.es + This program is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + Juan Ferrer Toribio +Alejandro Colombini Gómez +Javier Gallego Ferris + Juan Ferrer Toribio +Alejandro Colombini Gómez +Javier Gallego Ferris + Jordi Cuquerella +Juan Ferrer Toribio + ../image/icon.svg + gpl-3-0 + + + + False + vertical + 2 + + + False + end + + + False + True + end + 0 + + + + + + + + + + False + Hedera + center + ../image/icon.svg + + + + True + False + vertical + + + True + False + 4 + vertical + 4 + + + True + True + False + False + True + vn-notebook + + + + + + + True + True + 0 + + + + + True + False + 6 + 6 + 6 + + + 18 + True + False + + + False + True + 0 + + + + + True + False + 0 + Ready + + + True + True + 1 + + + + + True + False + 1 + User: + + + + + + False + True + 3 + + + + + True + False + [user-name] + + + False + True + 4 + + + + + False + True + 1 + + + + + True + True + end + 1 + + + + + + diff --git a/vn/gui/menubar.ui b/vn/gui/menubar.ui new file mode 100644 index 0000000..755cc18 --- /dev/null +++ b/vn/gui/menubar.ui @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vn/image/hedera16x16.xpm b/vn/image/hedera16x16.xpm new file mode 100644 index 0000000..000647d --- /dev/null +++ b/vn/image/hedera16x16.xpm @@ -0,0 +1,48 @@ +/* XPM */ +static char * icon16x16_xpm[] = { +"16 16 29 1", +" c None", +". c #536E00", +"+ c #638300", +"@ c #678700", +"# c #587400", +"$ c #AABA09", +"% c #C9DD0A", +"& c #A6B608", +"* c #5D7A00", +"= c #719500", +"- c #607F00", +"; c #ADBF09", +"> c #C8DC0A", +", c #709300", +"' c #5B7800", +") c #AABB08", +"! c #597500", +"~ c #A3B507", +"{ c #618000", +"] c #C4D70A", +"^ c #648300", +"/ c #516B00", +"( c #A7B808", +"_ c #899706", +": c #5E7C00", +"< c #9AA907", +"[ c #567200", +"} c #668700", +"| c #648400", +" ", +" ", +" .+@@@# ", +"$%%%%& *=====- ", +";%%%%> ,=====' ", +")%%%%% !====== ", +"~%%%%% -====={ ", +" ]%%%% ^====+ ", +" ", +" !**/ ((_ ", +" +==={ %%> ", +" ====: ]%% ", +"*==== << ", +"[}}| ", +" ", +" "}; diff --git a/vn/image/hedera32x32.xpm b/vn/image/hedera32x32.xpm new file mode 100644 index 0000000..58d2401 --- /dev/null +++ b/vn/image/hedera32x32.xpm @@ -0,0 +1,107 @@ +/* XPM */ +static char * icon32x32_xpm[] = { +"32 32 72 1", +" c None", +". c #617E00", +"+ c #628100", +"@ c #546E00", +"# c #A3B507", +"$ c #9DAD07", +"% c #638100", +"& c #719500", +"* c #607F00", +"= c #96A608", +"- c #C9DD0A", +"; c #A3B409", +"> c #668600", +", c #5C7A00", +"' c #9FB108", +") c #9BAB08", +"! c #5E7B00", +"~ c #587400", +"{ c #A6B708", +"] c #C3D60A", +"^ c #719400", +"/ c #ABBB08", +"( c #A1B008", +"_ c #5F7C00", +": c #ACBD09", +"< c #A3B307", +"[ c #6D8F00", +"} c #688900", +"| c #AABC08", +"1 c #A4B509", +"2 c #5D7C00", +"3 c #A9BA09", +"4 c #587500", +"5 c #97A807", +"6 c #A2B208", +"7 c #5E7A00", +"8 c #C4D70A", +"9 c #9BAB07", +"0 c #5F7F00", +"a c #6B8D00", +"b c #95A407", +"c c #628000", +"d c #6A8A00", +"e c #94A507", +"f c #B6C809", +"g c #6A8C00", +"h c #5B7800", +"i c #5C7900", +"j c #516A00", +"k c #AABA09", +"l c #A6B808", +"m c #5D7A00", +"n c #9CAC07", +"o c #C7DA0A", +"p c #577400", +"q c #A4B408", +"r c #A7B909", +"s c #ADBE09", +"t c #5D7B00", +"u c #99AB07", +"v c #6C8E00", +"w c #648400", +"x c #C1D30A", +"y c #A9B909", +"z c #A6B608", +"A c #ACBD08", +"B c #8F9C06", +"C c #678800", +"D c #5D7900", +"E c #536E00", +"F c #608000", +"G c #587300", +" ", +" ", +" ", +" ", +" .+++++++++@", +" ########$ %&&&&&&&&&&&*", +"=----------; >&&&&&&&&&&&&,", +"'-----------) !&&&&&&&&&&&&&~", +"{-----------] ^&&&&&&&&&&&&& ", +"/------------( _&&&&&&&&&&&&&& ", +":------------< [&&&&&&&&&&&&&} ", +"|------------1 &&&&&&&&&&&&&&2 ", +"3------------{ 4&&&&&&&&&&&&&& ", +"5------------6 7&&&&&&&&&&&&&7 ", +" 8-----------9 0&&&&&&&&&&&&a ", +" b----------- c&&&&&&&&&&&d ", +" ef--------- a&&&&&&&&&gh ", +" ", +" ", +" i______j kkkkl ", +" _&&&&&&&&m n-----o ", +" m&&&&&&&&&p q------r ", +" &&&&&&&&&& #------s ", +" t&&&&&&&&&& u------s ", +" v&&&&&&&&&w x-----y ", +" &&&&&&&&&&h zAAAAB ", +" &&&&&&&&&C ", +"D&&&&&&&&} ", +"E++++++FG ", +" ", +" ", +" "}; diff --git a/vn/image/icon.svg b/vn/image/icon.svg new file mode 100644 index 0000000..010caec --- /dev/null +++ b/vn/image/icon.svg @@ -0,0 +1,558 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vn/image/load.gif b/vn/image/load.gif new file mode 100644 index 0000000..7947e82 Binary files /dev/null and b/vn/image/load.gif differ diff --git a/vn/image/logo.svg b/vn/image/logo.svg new file mode 100644 index 0000000..aeaea8e --- /dev/null +++ b/vn/image/logo.svg @@ -0,0 +1,172 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vn/schema/hedera.gschema.xml b/vn/schema/hedera.gschema.xml new file mode 100644 index 0000000..2f71881 --- /dev/null +++ b/vn/schema/hedera.gschema.xml @@ -0,0 +1,23 @@ + + + + + "" + + + "localhost" + + + "" + + + "" + + + "" + + + "" + + + diff --git a/vn/schema/module.dtd b/vn/schema/module.dtd new file mode 100644 index 0000000..bdef5bf --- /dev/null +++ b/vn/schema/module.dtd @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/vn/vn-batch.c b/vn/vn-batch.c new file mode 100644 index 0000000..1938ee6 --- /dev/null +++ b/vn/vn-batch.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-batch.h" + +/** + * SECTION: vn-batch + * @Short_description: a group of objects + * @Title: VnBatch + * + * A group of GObjects. + */ + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +/** + * vn_batch_add: + * @obj: a #VnBatch + * @child: a #GObject + * + * Adds a child identified whith @id to the group. + **/ +void vn_batch_add (VnBatch * obj, GObject * child) +{ + g_return_if_fail (VN_IS_BATCH (obj)); + g_return_if_fail (G_IS_OBJECT (child)); + + obj->objects = g_list_prepend (obj->objects, child); +} + +/** + * vn_batch_remove: + * @obj: a #VnBatch + * @child: a #GObject contained by @obj + * + * Removes a child from the group. + **/ +void vn_batch_remove (VnBatch * obj, GObject * child) +{ + g_return_if_fail (VN_IS_BATCH (obj)); + + obj->objects = g_list_remove (obj->objects, child); +} + +/** + * vn_batch_get_objects: + * @obj: a #VnBatch + * + * Returns all the #GObjects in @obj. + * + * Return value: (transfer container) (element-type GObject): a #GList with all + * the objects, that must be freed with #g_list_free + **/ +GList * vn_batch_get_objects (VnBatch * obj) +{ + g_return_val_if_fail (VN_IS_BATCH (obj), NULL); + + return g_list_copy (obj->objects); +} + +static void vn_batch_buildable_add_child (GtkBuildable * obj, + GtkBuilder * builder, GObject * child, const gchar * type) +{ + vn_batch_add (VN_BATCH (obj), child); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_batch_init (VnBatch * obj) +{ + obj->objects = NULL; +} + +static void vn_batch_finalize (VnBatch * obj) +{ + GObjectClass * parent; + parent = g_type_class_peek_parent (VN_BATCH_GET_CLASS (obj)); + + g_list_free (obj->objects); + + parent->finalize (G_OBJECT (obj)); +} + +static void vn_batch_class_init (VnBatchClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_batch_finalize; +} + +static void vn_batch_buildable_interface_init (GtkBuildableIface * iface) +{ + iface->add_child = vn_batch_buildable_add_child; +} + +G_DEFINE_TYPE_WITH_CODE (VnBatch, vn_batch, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + vn_batch_buildable_interface_init) +); diff --git a/vn/vn-batch.h b/vn/vn-batch.h new file mode 100644 index 0000000..dd38fb6 --- /dev/null +++ b/vn/vn-batch.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_BATCH_H +#define VN_BATCH_H + +#include +#include + +#define VN_TYPE_BATCH (vn_batch_get_type ()) +#define VN_BATCH(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_BATCH, VnBatch)) +#define VN_IS_BATCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_BATCH)) +#define VN_BATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_BATCH, VnBatchClass)) +#define VN_IS_BATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_BATCH)) +#define VN_BATCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_BATCH, VnBatchClass)) + +typedef struct _VnBatch VnBatch; +typedef struct _VnBatchClass VnBatchClass; + +struct _VnBatch +{ + GObject parent; + GList * objects; +}; + +struct _VnBatchClass +{ + GObjectClass parent; +}; + +GType vn_batch_get_type (); + +void vn_batch_add (VnBatch * obj + ,GObject * child); +void vn_batch_remove (VnBatch * obj + ,GObject * child); +GList * vn_batch_get_objects (VnBatch * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-builder.c b/vn/vn-builder.c new file mode 100644 index 0000000..931cb63 --- /dev/null +++ b/vn/vn-builder.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-builder.h" +#include "vn-field.h" +#include +#include + +/** + * SECTION: vn-builder + * @Short_description: functions to use with the GtkBuilder + * @Title: VnBuilder + * + * These are a set of functions that make it easy to create and bind interfaces + * to data using the #GtkBuilder and the #DbLib, part of the Hedera library. + **/ +G_DEFINE_TYPE (VnBuilder, vn_builder, GTK_TYPE_BUILDER); + +VnBuilder * vn_builder_new () +{ + return g_object_new (VN_TYPE_BUILDER, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_builder_load_file: + * @obj: a #VnBuilder + * @conn: a #DbConn + * @filename: the name of the file to parse + * @err: (out) (allow-none): return location for an error, or %NULL + * + * Parses a file containing a GtkBuilder UI definition and merges it with the + * current contents of builder. Also sets de conn property of objects that use + * the #DbConn + * + * Return value: A positive value on success, 0 if an error occurred + **/ +guint vn_builder_load_file (VnBuilder * obj, DbConn * conn, const gchar * filename, GError ** err) +{ + guint success; + GtkBuilder * builder; + + g_return_val_if_fail (VN_IS_BUILDER (obj), 0); + g_return_val_if_fail (DB_IS_CONN (conn), 0); + + builder = GTK_BUILDER (obj); + success = gtk_builder_add_from_file (builder, filename, err); + + if (success) + { + GSList * n; + GSList * objects = gtk_builder_get_objects (builder); + + for (n = objects; n; n = n->next) + if (VN_IS_COMBO (n->data) + || VN_IS_COMPLETION (n->data) + || VN_IS_COLUMN_COMBO (n->data) + || DB_IS_MODEL (n->data) + || DB_IS_ITERATOR (n->data)) + g_object_set (n->data, "conn", conn, NULL); + + g_slist_free (objects); + } + + return success; +} + +/** + * vn_builder_bind_fields: + * @obj: a #VnBuilder + * @iterator: where params are obtained. + * @...: pairs of column name and field id, terminated with -1 + * + * Binds several iterator parameters to #VnEntry entries. + * The variable list argument should contain a string with the column name and + * a string the #VnField id. + **/ +void vn_builder_bind_fields (VnBuilder * obj, DbIterator * iterator, ...) +{ + va_list vl; + gchar * column; + const gchar * id; + VnField * field; + + g_return_if_fail (VN_IS_BUILDER (obj)); + g_return_if_fail (DB_IS_ITERATOR (iterator)); + + va_start (vl, iterator); + + while ((column = va_arg (vl, gchar *)) != NULL) + { + id = va_arg (vl, gchar *); + field = vn_builder_get (obj, id); + + if (VN_IS_FIELD (field)) + vn_field_set_param (field, db_iterator_get_param (iterator, column)); + else + g_warning ("VnBuilder: Object '%s' isn't a VnField!", id); + } + + va_end (vl); +} + +/** + * vn_builder_bind_columns: + * @obj: a #VnBuilder + * @...: pairs of column name and column id, terminated with -1 + * + * Binds several model columns to #VnColumn columns. + * The variable list argument should contain a string with the column name and + * a string the #VnColumn id. + **/ +void vn_builder_bind_columns (VnBuilder * obj, ...) +{ + va_list vl; + gchar * column_name; + const gchar * id; + VnColumn * column; + + g_return_if_fail (VN_IS_BUILDER (obj)); + + va_start (vl, obj); + + while ((column_name = va_arg (vl, gchar *)) != NULL) + { + id = va_arg (vl, gchar *); + column = vn_builder_get (obj, id); + + if (VN_IS_COLUMN (column)) + vn_column_set_column_name (column, column_name); + else + g_warning ("VnBuilder: Object '%s' isn't a VnColumn!", id); + } + + va_end (vl); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_builder_init (VnBuilder * obj) {} + +static void vn_builder_class_init (VnBuilderClass * k) {} diff --git a/vn/vn-builder.h b/vn/vn-builder.h new file mode 100644 index 0000000..adf9ef9 --- /dev/null +++ b/vn/vn-builder.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_BUILDER_H +#define VN_BUILDER_H + +#include +#include +#include "vn-column.h" + +#define VN_TYPE_BUILDER (vn_builder_get_type ()) +#define VN_BUILDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_BUILDER, VnBuilder)) +#define VN_IS_BUILDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_BUILDER)) +#define VN_BUILDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_BUILDER, VnBuilderClass)) +#define VN_IS_BUILDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_BUILDER)) +#define VN_BUILDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_BUILDER, VnBuilderClass)) + +#define vn_builder_get(obj,name) ((gpointer) gtk_builder_get_object (GTK_BUILDER (obj), name)) + +typedef struct _VnBuilder VnBuilder; +typedef struct _VnBuilderClass VnBuilderClass; + +struct _VnBuilder +{ + GtkBuilder parent; +}; + +struct _VnBuilderClass +{ + /* */ + GtkBuilderClass parent; +}; + +GType vn_builder_get_type (); +VnBuilder * vn_builder_new (); +guint vn_builder_load_file (VnBuilder * obj, DbConn * conn, const gchar * filename, GError ** err); +void vn_builder_bind_fields (VnBuilder * obj, DbIterator * iterator, ...); +void vn_builder_bind_columns (VnBuilder * obj, ...); + +#endif \ No newline at end of file diff --git a/vn/vn-column.c b/vn/vn-column.c new file mode 100644 index 0000000..7a122e4 --- /dev/null +++ b/vn/vn-column.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-column.h" +#include "vn-grid.h" + +G_DEFINE_ABSTRACT_TYPE (VnColumn, vn_column, GTK_TYPE_TREE_VIEW_COLUMN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_column_value_changed (VnColumn * obj, const gchar * path, const GValue * value) +{ + DbIter iter; + + vn_column_get_iter (obj, path, &iter); + db_model_set_value (vn_column_get_model (obj), + &iter, obj->column_index, value, NULL); +} + +static void vn_column_data_func (GtkTreeViewColumn * col, GtkCellRenderer * cell, + GtkTreeModel * model, GtkTreeIter * iter, VnColumn * obj) +{ + gchar * background; + DbIter dbiter; + DbModel * dbmodel; + + GValue value = {0}; + gtk_tree_model_get_value (model, iter, obj->column_index, &value); + obj->set_value (obj, model, iter, cell, &value); + g_value_unset (&value); + + vn_gtk_tree_iter_to_db_iter (iter, &dbiter); + g_object_get (model, "model", &dbmodel, NULL); + DbModelRowOp ops = db_model_get_row_operations (dbmodel, &dbiter); + g_object_unref (dbmodel); + + if (ops & DB_MODEL_ROW_OP_DELETE) + background = "#fcc"; + else if (ops & DB_MODEL_ROW_OP_INSERT || ops & DB_MODEL_ROW_OP_UPDATE) + background = "#cfc"; + else + background = NULL; + + g_object_set (cell, "cell-background", background, NULL); +} + +static void vn_column_set_renderer (VnColumn * obj, GtkCellRenderer * cell) +{ + GtkTreeViewColumn * column; + + obj->cell = cell; + obj->set_value = VN_COLUMN_GET_CLASS (obj)->set_value; + VN_COLUMN_GET_CLASS (obj)->set_editable (obj, obj->editable); + + column = GTK_TREE_VIEW_COLUMN (obj); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_set_cell_data_func (column, cell, + (GtkTreeCellDataFunc) vn_column_data_func, obj, NULL); +} + +static void vn_column_update_column_index (VnColumn * obj) +{ + if (obj->column_name) + { + DbModel * model = vn_column_get_model (obj); + + if (model) + { + obj->column_index = db_model_get_column_index (model, obj->column_name); + + if (obj->column_index >= 0) + gtk_tree_view_column_set_sort_column_id ( + GTK_TREE_VIEW_COLUMN (obj), obj->column_index); + } + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_column_get_column_name: + * @obj: the #VnColumn + * + * Gets the column name. + * + * Return value: the column name + **/ +const gchar * vn_column_get_column_name (VnColumn * obj) +{ + g_return_val_if_fail (VN_IS_COLUMN (obj), NULL); + + return obj->column_name; +} + +/** + * vn_column_set_column_name: + * @obj: the #VnColumn + * @name: the column name + * + * Sets the column name. + **/ +void vn_column_set_column_name (VnColumn * obj, const gchar * name) +{ + g_return_if_fail (VN_IS_COLUMN (obj)); + + g_free (obj->column_name); + obj->column_name = g_strdup (name); + vn_column_update_column_index (obj); +} + +/** + * vn_column_get_column_index: + * @obj: the #VnColumn + * + * Gets the column index. + * + * Return value: the column index + **/ +gint vn_column_get_column_index (VnColumn * obj) +{ + g_return_val_if_fail (VN_IS_COLUMN (obj), -1); + + return obj->column_index; +} + +/** + * vn_column_set_column_index: + * @obj: the #VnColumn + * @index: the column index + * + * Sets the column index. + **/ +void vn_column_set_column_index (VnColumn * obj, gint index) +{ + g_return_if_fail (VN_IS_COLUMN (obj)); + g_return_if_fail (index >= -1); + + obj->column_index = index; + vn_column_set_column_name (obj, NULL); + + if (index >= 0) + gtk_tree_view_column_set_sort_column_id ( + GTK_TREE_VIEW_COLUMN (obj), index); +} + +/** + * vn_column_get_editable: + * @obj: the #VnColumn + * + * Gets if the column can be edited by the user. + * + * Return value: %TRUE if its editable, %FALSE otherwhise + **/ +gboolean vn_column_get_editable (VnColumn * obj) +{ + g_return_val_if_fail (VN_IS_COLUMN (obj), FALSE); + + return obj->editable; +} + +/** + * vn_column_set_editable: + * @obj: the #VnColumn + * @editable: a %gboolean indicating whether the column value is editable + * + * Sets if the column could be edited by the user. + **/ +void vn_column_set_editable (VnColumn * obj, gboolean editable) +{ + g_return_if_fail (VN_IS_COLUMN (obj)); + + obj->editable = editable; + VN_COLUMN_GET_CLASS (obj)->set_editable (obj, editable); +} + +/** + * vn_column_get_model: + * @obj: the #VnColumn + * + * Gets the model used by the main #VnGrid. + * + * Return value: (transfer none): the #DbModel if has it, %NULL otherwise. + **/ +DbModel * vn_column_get_model (VnColumn * obj) +{ + g_return_val_if_fail (VN_IS_COLUMN (obj), NULL); + + GtkWidget * grid = gtk_tree_view_column_get_tree_view ( + GTK_TREE_VIEW_COLUMN (obj)); + + if (grid) + return vn_grid_get_model (VN_GRID (grid)); + + return NULL; +} + +/** + * vn_column_get_iter: + * @obj: the #VnColumn + * @path: the path string + * @iter: (out): the #DbIter to be set + * + * Gets a #DbIter from a #GtkTreePath string. + * + * Return value: %TRUE if the iter could be obtained, %FALSE otherwise. + **/ +gboolean vn_column_get_iter (VnColumn * obj, const gchar * path, DbIter * iter) +{ + DbModel * model; + GtkTreePath * tree_path; + + g_return_val_if_fail (VN_IS_COLUMN (obj), FALSE); + g_return_val_if_fail (iter, FALSE); + + tree_path = gtk_tree_path_new_from_string (path); + model = vn_column_get_model (obj); + + if (!tree_path) + g_warning ("VnColumn: Invalid path."); + else if (gtk_tree_path_get_depth (tree_path) < 1) + g_warning ("VnColumn: Path has no indices."); + else if (!model) + g_warning ("VnColumn: Tree view has no model assigned."); + else + { + db_model_get_iter (model, iter, gtk_tree_path_get_indices (tree_path)[0]); + return TRUE; + } + + gtk_tree_path_free (tree_path); + return FALSE; +} + +/** + * vn_column_model_changed: + * @obj: the #VnColumn + * + * This function is used primarily by the #VnGrid. Warns the column that the + * model has changed. + **/ +void vn_column_model_changed (VnColumn * obj) +{ + VnColumnClass * klass; + + g_return_if_fail (VN_IS_COLUMN (obj)); + + klass = VN_COLUMN_GET_CLASS (obj); + vn_column_update_column_index (obj); + + if (klass->model_changed) + klass->model_changed (obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_COLUMN_INDEX = 1 + ,PROP_COLUMN_NAME + ,PROP_EDITABLE +}; + +static void vn_column_set_property (VnColumn * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_COLUMN_INDEX: + vn_column_set_column_index (obj, g_value_get_int (value)); + break; + case PROP_COLUMN_NAME: + vn_column_set_column_name (obj, g_value_get_string (value)); + break; + case PROP_EDITABLE: + vn_column_set_editable (obj, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_column_get_property (VnColumn * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_COLUMN_INDEX: + g_value_set_int (value, obj->column_index); + break; + case PROP_COLUMN_NAME: + g_value_set_string (value, obj->column_name); + break; + case PROP_EDITABLE: + g_value_set_boolean (value, obj->editable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_init (VnColumn * obj) +{ + obj->column_index = -1; + obj->column_name = NULL; +} + +static void vn_column_finalize (VnColumn * obj) +{ + G_OBJECT_CLASS (vn_column_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_class_init (VnColumnClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_column_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_column_get_property; + klass->value_changed = vn_column_value_changed; + klass->set_renderer = vn_column_set_renderer; + klass->model_changed = NULL; + + g_object_class_install_property (k, PROP_COLUMN_INDEX, + g_param_spec_int ("column-index" + ,_("Column index") + ,_("The column index in the model") + ,-1, 255, -1 + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_COLUMN_NAME, + g_param_spec_string ("column-name" + ,_("Column name") + ,_("The referenced column name") + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_EDITABLE, + g_param_spec_boolean ("editable" + ,_("Editable") + ,_("Whether the column values are editable") + ,FALSE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} diff --git a/vn/vn-column.h b/vn/vn-column.h new file mode 100644 index 0000000..09a3447 --- /dev/null +++ b/vn/vn-column.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_COLUMN_H +#define VN_COLUMN_H + +#include +#include + +#define VN_TYPE_COLUMN (vn_column_get_type ()) +#define VN_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN, VnColumn)) +#define VN_IS_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN)) +#define VN_COLUMN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN, VnColumnClass)) +#define VN_IS_COLUMN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN)) +#define VN_COLUMN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN, VnColumnClass)) + +typedef struct _VnColumn VnColumn; +typedef struct _VnColumnClass VnColumnClass; + +typedef void (*VnColumnSetValueFunc) (VnColumn * obj, GtkTreeModel * model, GtkTreeIter * iter, + GtkCellRenderer * cell, const GValue * value); +typedef void (*VnColumnValuechangedFunc) (VnColumn * obj, const gchar * path, const GValue * value); +typedef void (*VnColumnSetRendererFunc) (VnColumn * obj, GtkCellRenderer * cell); +typedef void (*VnColumnSetEditableFunc) (VnColumn * obj, gboolean editable); +typedef void (*VnColumnModelChangedFunc) (VnColumn * obj); + +struct _VnColumn +{ + GtkTreeViewColumn parent; + GtkCellRenderer * cell; + gint column_index; + gchar * column_name; + gboolean editable; + VnColumnSetValueFunc set_value; +}; + +struct _VnColumnClass +{ + GtkTreeViewColumnClass parent; + VnColumnSetValueFunc set_value; + VnColumnValuechangedFunc value_changed; + VnColumnSetRendererFunc set_renderer; + VnColumnSetEditableFunc set_editable; + VnColumnModelChangedFunc model_changed; +}; + +GType vn_column_get_type (); +const gchar * vn_column_get_column_name (VnColumn * obj); +void vn_column_set_column_name (VnColumn * obj, const gchar * name); +gint vn_column_get_column_index (VnColumn * obj); +void vn_column_set_column_index (VnColumn * obj, gint index); +gboolean vn_column_get_editable (VnColumn * obj); +void vn_column_set_editable (VnColumn * obj, gboolean editable); +DbModel * vn_column_get_model (VnColumn * obj); +gboolean vn_column_get_iter (VnColumn * obj, const gchar * path, DbIter * iter); +void vn_column_model_changed (VnColumn * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-field.c b/vn/vn-field.c new file mode 100644 index 0000000..f71decf --- /dev/null +++ b/vn/vn-field.c @@ -0,0 +1,572 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-field.h" + +G_DEFINE_ABSTRACT_TYPE (VnField, vn_field, GTK_TYPE_EVENT_BOX); + +enum { + VALUE_CHANGED + ,LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_field_spec_changed (VnField * obj) +{ + gvn_param_spec_unset (obj->spec); + gvn_param_spec_merge (obj->spec, obj->user_spec); + + if (obj->param) + gvn_param_spec_merge (obj->spec, + gvn_param_get_spec (obj->param)); +} + +static void vn_field_on_param_spec_changed (GvnParam * param, VnField * obj) +{ + vn_field_spec_changed (obj); +} + +static void vn_field_on_param_value_changed (GvnParam * param, const GValue * value, VnField * obj) +{ + obj->update_param = FALSE; + vn_field_set_value (obj, value); + obj->update_param = TRUE; +} + +static void vn_field_set_invalid (VnField * obj, const GError * err) +{ +/* GdkRGBA color = {0.95, 0.8, 0.8, 1}; + gtk_widget_override_background_color (GTK_WIDGET (obj->field), + GTK_STATE_FLAG_NORMAL, &color); +*/} + +static void vn_field_on_param_status_changed (GvnParam * param, VnField * obj) +{ + switch (gvn_param_get_status (param)) + { + case GVN_PARAM_STATUS_OK: + gtk_widget_set_sensitive (obj->field, TRUE); + break; + case GVN_PARAM_STATUS_BUSY: + gtk_widget_set_sensitive (obj->field, FALSE); + break; + case GVN_PARAM_STATUS_ERROR: + vn_field_set_invalid (obj, gvn_param_get_error (param)); + break; + } +} + +static void vn_field_changed (VnField * obj) +{ + if (obj->param && obj->update_param) + { + GError * err = NULL; + + g_signal_handlers_block_by_func (obj->param, + vn_field_on_param_value_changed, obj); + + if (!gvn_param_set_value (obj->param, obj->value, &err)) + { + vn_field_set_invalid (obj, err); + g_error_free (err); + } + + g_signal_handlers_unblock_by_func (obj->param, + vn_field_on_param_value_changed, obj); + } + + g_signal_emit (obj, signals[VALUE_CHANGED], 0, obj->value); +} + +static void vn_field_value_changed (VnField * obj, const GValue * value) +{ + GType type; + gboolean changed; + + if (obj->lock_changed_done) + return; + + type = gvn_param_spec_get_gtype (obj->spec); + + if (type != G_TYPE_NONE && !gvn_value_is_null (value)) + { + if (G_VALUE_TYPE (obj->value) != type) + { + g_value_unset (obj->value); + g_value_init (obj->value, type); + } + + g_value_transform (value, obj->value); + changed = TRUE; + } + else + changed = gvn_value_ccopy (value, obj->value); + + if (changed) + vn_field_changed (obj); +} + +static void vn_field_make_param (VnField * obj) +{ + GvnParam * param; + + if (!obj->iterator || !obj->column_name) + return; + + param = db_iterator_get_param (obj->iterator, obj->column_name); + vn_field_set_param (obj, param); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_field_get_value: + * @obj: the #VnField + * + * Gets the current field value. + * + * Return value: (transfer none): the #GValue + **/ +const GValue * vn_field_get_value (VnField * obj) +{ + return obj->value; +} + +/** + * vn_field_set_value: + * @obj: the #VnField + * @value: a #GValue + * + * Sets the field value. + **/ +void vn_field_set_value (VnField * obj, const GValue * value) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + g_return_if_fail (G_IS_VALUE (value)); + + if (gvn_value_ccopy (value, obj->value)) + { + obj->lock_changed_done = TRUE; + VN_FIELD_GET_CLASS (obj)->set_value (obj, obj->value); + obj->lock_changed_done = FALSE; + vn_field_changed (obj); + } +} + +/** + * vn_field_get_param: + * @obj: the #VnField + * + * Return value: (transfer none): +**/ +GvnParam * vn_field_get_param (VnField * obj) +{ + g_return_val_if_fail (VN_IS_FIELD (obj), NULL); + + return obj->param; +} + +/** + * vn_field_set_param: + * @obj: the #VnField + * @param: a #GvnParam + * + * Binds the field with a #GvnParam. + **/ +void vn_field_set_param (VnField * obj, GvnParam * param) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + g_return_if_fail (GVN_IS_PARAM (param) || !param); + + if (obj->param) + { + g_object_disconnect (obj->param + ,"any_signal", vn_field_on_param_value_changed, obj + ,"any_signal", vn_field_on_param_spec_changed, obj + ,"any_signal", vn_field_on_param_status_changed, obj + ,NULL + ); + g_clear_object (&obj->param); + } + if (param) + { + obj->param = g_object_ref_sink (param); + g_object_connect (param + ,"signal::value-changed", vn_field_on_param_value_changed, obj + ,"signal::spec-changed", vn_field_on_param_spec_changed, obj + ,"signal::status-changed", vn_field_on_param_status_changed, obj + ,NULL + ); + vn_field_on_param_spec_changed (param, obj); + vn_field_on_param_value_changed (param, + gvn_param_get_value (param), obj); + vn_field_on_param_status_changed (param, obj); + } + +/* GFile * css = g_file_new_for_path ("vn/gui/style.css"); + + if (g_file_query_exists (css, NULL)) + { + GError * err = NULL; + GtkCssProvider * provider = gtk_css_provider_get_default (); + + if (gtk_css_provider_load_from_file (provider, css, &err)) + { + GtkStyleContext * style; + style = gtk_widget_get_style_context (GTK_WIDGET (obj->field)); + gtk_style_context_add_provider (style, GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + gtk_style_context_add_class (style, "invalid"); + } + else + { + g_warning ("Vn: %s", err->message); + g_error_free (err); + } + } + else + g_warning ("Vn: Can't load CSS styles"); + + g_object_unref (css); +*/ +} + +/** + * vn_field_get_gtype: + * @obj: the #VnField + * + * Gets the field type. + * + * Return value: the field type or %G_TYPE_NONE if it havent. + **/ +GType vn_field_get_gtype (VnField * obj) +{ + g_return_val_if_fail (VN_IS_FIELD (obj), G_TYPE_INVALID); + + return gvn_param_spec_get_gtype (obj->spec); +} + +/** + * vn_field_set_gtype: + * @obj: the #VnField + * @type: a valid #GType + * + * Sets the type of the field. When you set a value, it will be converted to + * this type. + **/ +void vn_field_set_gtype (VnField * obj, GType type) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + + gvn_param_spec_set_gtype (obj->user_spec, type); + vn_field_spec_changed (obj); +} + +/** + * vn_field_get_null: + * @obj: the #VnField + * + * If field allows values with %GVN_TYPE_NULL type. + * + * Return value: %TRUE if it can, %FALSE otherwise. + **/ +gboolean vn_field_get_null (VnField * obj) +{ + g_return_val_if_fail (VN_IS_FIELD (obj), FALSE); + + return gvn_param_spec_get_null (obj->spec); +} + +/** + * vn_field_set_null: + * @obj: the #VnField + * @null: %TRUE if field should accept NULL values + * + * Sets if field allows values with %GVN_TYPE_NULL type. If param property of + * the field is set, this attribute will be merged with the same #GvnParam + * attribute, remaining the most restrictive. + **/ +void vn_field_set_null (VnField * obj, gboolean null) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + + gvn_param_spec_set_null (obj->user_spec, null); + vn_field_spec_changed (obj); +} + +/** + * vn_field_get_editable: + * @obj: the #VnField + * + * Gets whether the field value can be modified. + * + * Return value: %TRUE if field value can be edited, %FALSE otherwise. + **/ +gboolean vn_field_get_editable (VnField * obj) +{ + g_return_val_if_fail (VN_IS_FIELD (obj), FALSE); + + return gvn_param_spec_get_editable (obj->spec); +} + +/** + * vn_field_set_editable: + * @obj: the #VnField + * @editable: %TRUE if field can be editable + * + * Sets if field value can be modified. If param property of + * the field is set, this attribute will be merged with the same #GvnParam + * attribute, remaining the most restrictive. + **/ +void vn_field_set_editable (VnField * obj, gboolean editable) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + + gvn_param_spec_set_editable (obj->user_spec, editable); + vn_field_spec_changed (obj); +} + +/** + * vn_field_get_default: + * @obj: the #VnField + **/ +const GValue * vn_field_get_default (VnField * obj) +{ + g_return_val_if_fail (VN_IS_FIELD (obj), NULL); + + return gvn_param_spec_get_default (obj->spec); +} + +/** + * vn_field_set_default: + * @obj: the #VnField + * @def: the default #GValue + * + * Sets the default value of the field. + **/ +void vn_field_set_default (VnField * obj, const GValue * def) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + + gvn_param_spec_set_default (obj->user_spec, def); + vn_field_spec_changed (obj); +} + +/** + * vn_field_set_to_default: + * @obj: the #VnField + **/ +void vn_field_set_to_default (VnField * obj) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + + vn_field_set_value (obj, + gvn_param_spec_get_default (obj->spec)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_VALUE = 1 + ,PROP_PARAM + ,PROP_ITERATOR + ,PROP_COLUMN_NAME + ,PROP_GTYPE + ,PROP_EDITABLE + ,PROP_NULL + ,PROP_DEFAULT +}; + +static void vn_field_set_property (VnField * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_VALUE: + vn_field_set_value (obj, g_value_get_boxed (value)); + break; + case PROP_PARAM: + vn_field_set_param (obj, g_value_get_object (value)); + break; + case PROP_ITERATOR: + g_clear_object (&obj->iterator); + obj->iterator = g_value_dup_object (value); + vn_field_make_param (obj); + break; + case PROP_COLUMN_NAME: + g_free (obj->column_name); + obj->column_name = g_value_dup_string (value); + vn_field_make_param (obj); + break; + case PROP_GTYPE: + vn_field_set_gtype (obj, g_value_get_gtype (value)); + break; + case PROP_EDITABLE: + vn_field_set_editable (obj, g_value_get_boolean (value)); + break; + case PROP_NULL: + vn_field_set_null (obj, g_value_get_boolean (value)); + break; + case PROP_DEFAULT: + vn_field_set_default (obj, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_field_get_property (VnField * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_VALUE: + g_value_set_boxed (value, obj->value); + break; + case PROP_PARAM: + g_value_set_object (value, obj->param); + break; + case PROP_ITERATOR: + g_value_set_object (value, obj->iterator); + break; + case PROP_COLUMN_NAME: + g_value_set_string (value, obj->column_name); + break; + case PROP_GTYPE: + g_value_set_gtype (value, vn_field_get_gtype (obj)); + break; + case PROP_EDITABLE: + g_value_set_boolean (value, vn_field_get_editable (obj)); + break; + case PROP_NULL: + g_value_set_boolean (value, vn_field_get_null (obj)); + break; + case PROP_DEFAULT: + g_value_set_boxed (value, vn_field_get_default (obj)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_field_init (VnField * obj) +{ + GdkRGBA color = {0.0, 0.0, 0.0, 0.0}; + + obj->param = NULL; + obj->update_param = TRUE; + obj->lock_changed_done = FALSE; + obj->user_spec = gvn_param_spec_new (); + obj->spec = gvn_param_spec_new (); + obj->iterator = NULL; + obj->column_name = NULL; + + obj->value = g_new0 (GValue, 1); + g_value_init (obj->value, GVN_TYPE_NULL); + + gtk_widget_override_background_color (GTK_WIDGET (obj), + GTK_STATE_FLAG_NORMAL, &color); +} + +static void vn_field_finalize (VnField * obj) +{ + gvn_param_spec_free (obj->spec); + gvn_param_spec_free (obj->user_spec); + g_clear_object (&obj->iterator); + g_clear_object (&obj->param); + g_value_unset (obj->value); + g_free (obj->value); + g_free (obj->column_name); + G_OBJECT_CLASS (vn_field_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_field_class_init (VnFieldClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_field_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_field_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_field_get_property; + klass->value_changed = vn_field_value_changed; + + signals[VALUE_CHANGED] = g_signal_new ("value-changed", + VN_TYPE_FIELD, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, G_TYPE_VALUE + ); + + g_object_class_install_property (k, PROP_VALUE, + g_param_spec_boxed ("value" + ,_("Value") + ,_("The current value of the field") + ,G_TYPE_VALUE + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_PARAM, + g_param_spec_object ("param" + ,_("Parameter") + ,_("The param where the field can read/write its value") + ,GVN_TYPE_PARAM + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_ITERATOR, + g_param_spec_object ("iterator" + ,_("Iterator") + ,_("The iterator used to get the field param") + ,DB_TYPE_ITERATOR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_COLUMN_NAME, + g_param_spec_string ("column-name" + ,_("Column name") + ,_("The column name on the iterator") + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_GTYPE, + g_param_spec_gtype ("gtype" + ,_("Glib Type") + ,_("The type of the value") + ,G_TYPE_NONE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_EDITABLE, + g_param_spec_boolean ("editable" + ,_("Editable") + ,_("Whether the field value is user editable") + ,TRUE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_NULL, + g_param_spec_boolean ("null" + ,_("Null") + ,_("Whether the field value can be of type GVN_TYPE_NULL") + ,TRUE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_DEFAULT, + g_param_spec_boxed ("default" + ,_("Default Value") + ,_("The default value") + ,G_TYPE_VALUE + ,G_PARAM_READWRITE + )); +} diff --git a/vn/vn-field.h b/vn/vn-field.h new file mode 100644 index 0000000..a24672a --- /dev/null +++ b/vn/vn-field.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_FIELD_H +#define VN_FIELD_H + +#include +#include + +#define VN_TYPE_FIELD (vn_field_get_type ()) +#define VN_FIELD(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_FIELD, VnField)) +#define VN_IS_FIELD(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_FIELD)) +#define VN_FIELD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_FIELD, VnFieldClass)) +#define VN_FIELD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_FIELD, VnFieldClass)) + +typedef struct _VnField VnField; +typedef struct _VnFieldClass VnFieldClass; + +typedef void (*VnFieldSetValueFunc) (VnField * obj, const GValue * value); +typedef void (*VnFieldValueChangedFunc) (VnField * obj, const GValue * value); + +struct _VnField +{ + GtkEventBox parent; + GtkWidget * field; + GValue * value; + GvnParam * param; + GvnParamSpec * spec; + GvnParamSpec * user_spec; + gboolean lock_changed_done; + gboolean update_param; + DbIterator * iterator; + gchar * column_name; +}; + +struct _VnFieldClass +{ + GtkEventBoxClass parent; + GCallback changed; + VnFieldSetValueFunc set_value; + VnFieldValueChangedFunc value_changed; +}; + +GType vn_field_get_type (); +const GValue * vn_field_get_value (VnField * obj); +void vn_field_set_value (VnField * obj, const GValue * value); +GvnParam * vn_field_get_param (VnField * obj); +void vn_field_set_param (VnField * obj, GvnParam * param); +GType vn_field_get_gtype (VnField * obj); +void vn_field_set_gtype (VnField * obj, GType type); +gboolean vn_field_get_null (VnField * obj); +void vn_field_set_null (VnField * obj, gboolean null); +gboolean vn_field_get_editable (VnField * obj); +void vn_field_set_editable (VnField * obj, gboolean editable); +const GValue * vn_field_get_default (VnField * obj); +void vn_field_set_default (VnField * obj, const GValue * def); +void vn_field_set_to_default (VnField * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-form.c b/vn/vn-form.c new file mode 100644 index 0000000..f83435e --- /dev/null +++ b/vn/vn-form.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-form.h" + +G_DEFINE_ABSTRACT_TYPE (VnForm, vn_form, GTK_TYPE_ALIGNMENT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_form_open: + * @obj: the #VnForm + * + * Activates the form. + * Virtual: open + **/ +void vn_form_open (VnForm * obj) +{ + const gchar * dir; + gchar * file; + GError * err = NULL; + + obj->builder = GTK_BUILDER (vn_builder_new ()); + gtk_builder_set_translation_domain (obj->builder, vn_mod_get_text_domain (obj->mod)); + + dir = vn_mod_get_data_dir (obj->mod); + file = g_strdup_printf ("%s/%s.glade", dir, obj->name); + + if (vn_builder_load_file (VN_BUILDER (obj->builder), obj->conn, file, &err)) + { + gtk_builder_connect_signals (obj->builder, obj); + VN_FORM_GET_CLASS (obj)->open (obj, obj->builder, NULL); + gtk_container_add (GTK_CONTAINER (obj), vn_form_get (obj, "main")); + gtk_widget_show_all (GTK_WIDGET (obj)); + + // Loading actions from the .glade and the GtkUIManager definition + + if ((obj->actions = vn_form_get (obj, "actions"))) + { + gchar * buffer; + gchar * ui_file = g_strdup_printf ("%s/%s.ui", dir, obj->name); + GError * err = NULL; + + if (g_file_get_contents (ui_file, &buffer, NULL, &err)) + { + obj->ui = buffer; + g_object_ref (obj->actions); + } + else + { + g_warning ("VnForm: Actions were defined for %s but no UI " + "definition was found.", obj->name); + g_error_free (err); + obj->actions = NULL; + } + + g_free (ui_file); + } + } + else + { + g_warning ("VnForm: %s", err->message); + g_error_free (err); + } + + g_free (file); +} + +/** + * vn_form_get: + * @obj: the #VnForm + * @name: the object name + * + * Gets an object from the form builder using its name. + * + * Return value: (transfer none): the object, or %NULL if there is no object + * with that name + **/ +gpointer vn_form_get (VnForm * obj, const gchar * name) +{ + return (gpointer) gtk_builder_get_object (obj->builder, name); +} + +/** + * vn_form_get_name: + * @obj: the #VnForm + * + * Gets the name of the form. + **/ +const gchar * vn_form_get_name (VnForm * obj) +{ + g_return_val_if_fail (VN_IS_FORM (obj), NULL); + + return obj->name; +} + +/** + * vn_form_get_ui_manager: + * @obj: the #VnForm + * + * Returns the string containing the path of the #GtkUIManager UI definition + * file for the form @obj. + * + * Return value: a string + **/ +const gchar * vn_form_get_ui_manager (VnForm * obj) +{ + g_return_val_if_fail (VN_IS_FORM (obj), NULL); + + return obj->ui; +} + +/** + * vn_form_get_action_group: + * @obj: the #VnForm + * + * Returns the group actions implemented by @obj. + * + * Return value: (transfer none): a #GtkActionGroup + **/ +GtkActionGroup * vn_form_get_action_group (VnForm * obj) +{ + g_return_val_if_fail (VN_IS_FORM (obj), NULL); + + return obj->actions; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_NAME = 1 + ,PROP_GUI + ,PROP_CONN + ,PROP_MODULE +}; + +static void vn_form_set_property (VnForm * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + obj->name = g_value_dup_string (value); + break; + case PROP_GUI: + obj->gui = g_value_dup_object (value); + obj->conn = vn_gui_get_conn (obj->gui); + break; + case PROP_MODULE: + obj->mod = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_form_get_property (VnForm * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_value_set_string (value, obj->name); + break; + case PROP_GUI: + g_value_set_object (value, obj->gui); + break; + case PROP_CONN: + g_value_set_object (value, obj->conn); + break; + case PROP_MODULE: + g_value_set_object (value, obj->mod); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_form_init (VnForm * obj) +{ + obj->name = NULL; + obj->gui = NULL; + obj->conn = NULL; + obj->builder = NULL; + obj->mod = NULL; + obj->actions = NULL; + obj->ui = NULL; +} + +static void vn_form_finalize (VnForm * obj) +{ + g_free (obj->name); + g_free (obj->ui); + g_clear_object (&obj->gui); + g_clear_object (&obj->builder); + g_clear_object (&obj->mod); + g_clear_object (&obj->actions); + G_OBJECT_CLASS (vn_form_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_form_class_init (VnFormClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) vn_form_finalize; + klass->set_property = (GObjectSetPropertyFunc) vn_form_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_form_get_property; + + g_object_class_install_property (klass, PROP_NAME, + g_param_spec_string ("name" + ,_("Name") + ,_("The form name") + ,NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_GUI, + g_param_spec_object ("gui" + ,_("Gui") + ,_("The Gui object") + ,VN_TYPE_GUI + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by the module") + ,DB_TYPE_CONN + ,G_PARAM_READABLE + )); + g_object_class_install_property (klass, PROP_MODULE, + g_param_spec_object ("module" + ,_("Module") + ,_("The module") + ,VN_TYPE_MOD + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); +} diff --git a/vn/vn-form.h b/vn/vn-form.h new file mode 100644 index 0000000..4787a51 --- /dev/null +++ b/vn/vn-form.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_FORM_H +#define VN_FORM_H + +#include "vn-builder.h" + +#define VN_TYPE_FORM (vn_form_get_type ()) +#define VN_FORM(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_FORM, VnForm)) +#define VN_IS_FORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_FORM)) +#define VN_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_FORM, VnFormClass)) +#define VN_IS_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_FORM)) +#define VN_FORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_FORM, VnFormClass)) + +typedef struct _VnForm VnForm; +typedef struct _VnFormClass VnFormClass; + +#include "vn-gui.h" + +typedef GType (* VnFormGetTypeFunc) (); +typedef void (* VnFormOpenFunc) (VnForm * obj, GtkBuilder * builder, gpointer user_data); +typedef void (* VnFormActivateFunc) (VnForm * obj); +typedef void (* VnFormDeactivateFunc) (VnForm * obj); + +struct _VnForm +{ + GtkAlignment parent; + gchar * name; + VnGui * gui; + DbConn * conn; + GtkBuilder * builder; + VnMod * mod; + GtkActionGroup * actions; + gchar * ui; +}; + +struct _VnFormClass +{ + GtkAlignmentClass parent; + void (* open) (VnForm * obj, GtkBuilder * builder, gpointer user_data); + void (* activate) (VnForm * obj); + void (* deactivate) (VnForm * obj); +}; + +GType vn_form_get_type (); +void vn_form_open (VnForm * obj); +gpointer vn_form_get (VnForm * obj, const gchar * name); +const gchar * vn_form_get_name (VnForm * obj); +const gchar * vn_form_get_ui_manager (VnForm * obj); +GtkActionGroup * vn_form_get_action_group (VnForm * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-grid.c b/vn/vn-grid.c new file mode 100644 index 0000000..4e24e07 --- /dev/null +++ b/vn/vn-grid.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-grid.h" + +G_DEFINE_TYPE (VnGrid, vn_grid, GTK_TYPE_TREE_VIEW); + +/** + * vn_grid_new: + * + * Creates a new grid + * + * Return value: #VnGrid created. + **/ +VnGrid * vn_grid_new () +{ + return g_object_new (VN_TYPE_GRID, "rules-hint", TRUE, NULL); +} + +/** + * vn_grid_new_with_iterator: + * @iterator: #DbIterator where the grid will be created + * + * Creates a new grid into a iterator + * + * Return value: #VnGrid created. + **/ +VnGrid * vn_grid_new_with_iterator (DbIterator * iterator) +{ + VnGrid * obj = vn_grid_new (); + g_object_set (obj, "iterator", iterator, NULL); + return obj; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_grid_on_iter_changed (DbIterator * iterator, GtkTreeView * obj); + +static void vn_grid_on_cursor_changed (GtkTreeView * obj, DbIterator * iterator) +{ + GtkTreeIter iter; + GtkTreeSelection * selection = gtk_tree_view_get_selection (obj); + + if (selection && gtk_tree_selection_get_selected (selection, NULL, &iter)) + { + DbIter dbiter; + vn_gtk_tree_iter_to_db_iter (&iter, &dbiter); + + g_signal_handlers_block_by_func (iterator, vn_grid_on_iter_changed, obj); + db_iterator_move_iter (iterator, &dbiter); + g_signal_handlers_unblock_by_func (iterator, vn_grid_on_iter_changed, obj); + } +} + +static void vn_grid_on_iter_changed (DbIterator * iterator, GtkTreeView * obj) +{ + DbIter dbiter; + GtkTreeSelection * selection = gtk_tree_view_get_selection (obj); + + if (db_iterator_get_iter (iterator, &dbiter) + && gtk_tree_view_get_model (obj)) + { + GtkTreeIter iter; + GtkTreePath * path; + + vn_gtk_tree_iter_from_db_iter (&iter, &dbiter); + g_signal_handlers_block_by_func (obj, vn_grid_on_cursor_changed, iterator); + gtk_tree_selection_select_iter (selection, &iter); + g_signal_handlers_unblock_by_func (obj, vn_grid_on_cursor_changed, iterator); + + path = gtk_tree_model_get_path ( + gtk_tree_view_get_model (obj), &iter); + gtk_tree_view_scroll_to_cell (obj, path, NULL, FALSE, 0, 0); + gtk_tree_path_free (path); + } + else if (selection) + gtk_tree_selection_unselect_all (selection); +} + +static void vn_grid_on_status_changed (DbIterator * iterator, gboolean ready, VnGrid * obj) +{ + GList * n; + GList * columns; + + obj->model = db_iterator_get_model (iterator); + columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (obj)); + + for (n = columns; n; n = n->next) + if (VN_IS_COLUMN (n->data)) + vn_column_model_changed (n->data); + + g_list_free (columns); + + if (ready) + { + GtkTreeModel * tree_model = GTK_TREE_MODEL (vn_model_new (obj->model)); + gtk_tree_view_set_model (GTK_TREE_VIEW (obj), tree_model); + vn_grid_on_iter_changed (obj->iterator, GTK_TREE_VIEW (obj)); + g_object_unref (tree_model); + } + else + gtk_tree_view_set_model (GTK_TREE_VIEW (obj), NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_grid_get_iterator: + * @obj: a #VnGrid + * + * Gets the iterator used by @obj. + * + * Return value: (transfer none): the #DbIterator + **/ +DbIterator * vn_grid_get_iterator (VnGrid * obj) +{ + g_return_val_if_fail (VN_IS_GRID (obj), NULL); + + return obj->iterator; +} + +/** + * vn_grid_set_iterator: + * @obj: a #VnGrid + * @iterator: the #DbIterator + * + * Sets the iterator handled by tree view. + **/ +void vn_grid_set_iterator (VnGrid * obj, DbIterator * iterator) +{ + g_return_if_fail (VN_IS_GRID (obj)); + g_return_if_fail (DB_IS_ITERATOR (iterator) || !iterator); + + if (obj->iterator) + { + g_signal_handlers_disconnect_by_func (obj, + vn_grid_on_cursor_changed, obj->iterator); + + g_object_disconnect (obj->iterator + ,"any-signal", vn_grid_on_iter_changed, obj + ,"any-signal", vn_grid_on_status_changed, obj + ,NULL + ); + g_clear_object (&obj->iterator); + obj->model = NULL; + } + if (iterator) + { + obj->iterator = g_object_ref (iterator); + g_object_connect (iterator + ,"signal::iter-changed", vn_grid_on_iter_changed, obj + ,"signal::status-changed", vn_grid_on_status_changed, obj + ,NULL + ); + vn_grid_on_status_changed (iterator, + db_iterator_is_ready (iterator), obj); + + g_signal_connect (obj, "cursor-changed", + G_CALLBACK (vn_grid_on_cursor_changed), iterator); + } +} + +/** + * vn_grid_get_model: + * @obj: a #VnGrid + * + * Gets the model used by @obj. + * + * Return value: (transfer none): the #DbModel + **/ +DbModel * vn_grid_get_model (VnGrid * obj) +{ + g_return_val_if_fail (VN_IS_GRID (obj), NULL); + + return obj->model; +} + +/** + * vn_grid_insert_column: + * @obj: #VnGrid where the new column will be appended + * @position: the position where the column will be inserted + * @column_index: the index of the source column in the model + * @title: the title + * @column_type: the #GType of the assigned #VnColumn + * @editable: a %gboolean indicating whether the column is editable + * @expand: a %gboolean indicating whether the column is expandable + * + * Creates and inserts a new column in the grid with the given column and + * position, if @position is -1 the column will be appended to the end. + * + * Return value: (transfer none): the #VnColumn assigned to the new column. + **/ +VnColumn * vn_grid_insert_column (VnGrid * obj, gint position, gint column_index, + const gchar * title, GType column_type, gboolean editable, gboolean expand) +{ + GtkTreeViewColumn * column; + + g_return_val_if_fail (VN_IS_GRID (obj), NULL); + g_return_val_if_fail (index >= 0, NULL); + g_return_val_if_fail (g_type_is_a (column_type, VN_TYPE_COLUMN), NULL); + + column = g_object_new (column_type + ,"title" ,title + ,"expand" ,expand + ,"resizable" ,TRUE + ,"sort-indicator" ,TRUE + ,"sizing" ,GTK_TREE_VIEW_COLUMN_GROW_ONLY + ,"column-index" ,column_index + ,"editable" ,editable + ,NULL + ); + gtk_tree_view_append_column (GTK_TREE_VIEW (obj), column); + return VN_COLUMN (column); +} + +/** + * vn_grid_append_column: + * @obj: #VnGrid where the new column will be appended + * @column_index: the index of the source column in the model + * @title: the title + * @column_type: the #GType of the assigned #VnCell + * @editable: a %gboolean indicating whether the column is editable + * @expand: a %gboolean indicating whether the column is expandable + * + * Creates and appends a new column at the end of the grid with the given column. + * + * Return value: (transfer none): the #VnCell assigned to the new column. + **/ +VnColumn * vn_grid_append_column (VnGrid * obj, gint column_index, const gchar * title, + GType column_type, gboolean editable, gboolean expand) +{ + g_return_val_if_fail (VN_IS_GRID (obj), NULL); + g_return_val_if_fail (index >= 0, NULL); + g_return_val_if_fail (g_type_is_a (column_type, VN_TYPE_COLUMN), NULL); + + return vn_grid_insert_column (obj, -1, column_index, title, column_type, editable, expand); +} + +/** + * vn_grid_append_columns: + * @obj: the #VnGrid + * @...: columns information, see vn_grid_append_column(), terminated with -1 + * + * Appends columns into a #VnGrid. + **/ +void vn_grid_append_columns (VnGrid * obj, ...) +{ + va_list vl; + gint index; + gchar * title; + GType column_type; + gboolean editable; + gboolean expand; + + g_return_if_fail (VN_IS_GRID (obj)); + + va_start (vl, obj); + + while ((index = va_arg (vl, gint)) != -1) + { + title = va_arg (vl, gchar *); + column_type = va_arg (vl, GType); + editable = va_arg (vl, gboolean); + expand = va_arg (vl, gboolean); + + vn_grid_append_column (obj, + index, title, column_type, editable, expand); + } + + va_end (vl); + gtk_tree_view_columns_autosize (GTK_TREE_VIEW (obj)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +typedef enum +{ + PROP_ITERATOR = 1 +} +VnGridProp; + +static void vn_grid_set_property (VnGrid * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ITERATOR: + vn_grid_set_iterator (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_grid_get_property (VnGrid * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ITERATOR: + g_value_set_object (value, obj->iterator); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_grid_init (VnGrid * obj) +{ + obj->iterator = NULL; + obj->model = NULL; + gtk_tree_selection_set_mode ( + gtk_tree_view_get_selection (GTK_TREE_VIEW (obj)), + GTK_SELECTION_SINGLE + ); +} + +static void vn_grid_finalize (VnGrid * obj) +{ + vn_grid_set_iterator (obj, NULL); + G_OBJECT_CLASS (vn_grid_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_grid_class_init (VnGridClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->set_property = (GObjectSetPropertyFunc) vn_grid_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_grid_get_property; + klass->finalize = (GObjectFinalizeFunc) vn_grid_finalize; + + g_object_class_install_property (klass, PROP_ITERATOR, + g_param_spec_object ("iterator" + ,_("Iterator") + ,_("The iterator used by VnGrid") + ,DB_TYPE_ITERATOR + ,G_PARAM_READWRITE + )); +} diff --git a/vn/vn-grid.h b/vn/vn-grid.h new file mode 100644 index 0000000..cf8418a --- /dev/null +++ b/vn/vn-grid.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_GRID_H +#define VN_GRID_H + +#include +#include +#include "vn-model.h" +#include "vn-column.h" + +#define VN_TYPE_GRID (vn_grid_get_type ()) +#define VN_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_GRID, VnGrid)) +#define VN_IS_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_GRID)) +#define VN_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_GRID, VnGridClass)) +#define VN_IS_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_GRID)) +#define VN_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_GRID, VnGridClass)) + +typedef struct _VnGrid VnGrid; +typedef struct _VnGridClass VnGridClass; + +struct _VnGrid +{ + GtkTreeView parent; + DbIterator * iterator; + DbModel * model; + +}; + +struct _VnGridClass +{ + /* */ + GtkTreeViewClass parent; +}; + +GType vn_grid_get_type (); +VnGrid * vn_grid_new (); +VnGrid * vn_grid_new_with_iterator (DbIterator * iterator); +DbIterator * vn_grid_get_iterator (VnGrid * obj); +void vn_grid_set_iterator (VnGrid * obj, DbIterator * iterator); +DbModel * vn_grid_get_model (VnGrid * obj); +VnColumn * vn_grid_insert_column (VnGrid * obj, gint position, gint column_index, const gchar * title, GType column_type, gboolean editable, gboolean expand); +VnColumn * vn_grid_append_column (VnGrid * obj, gint column_index, const gchar * title, GType column_type, gboolean editable, gboolean expand); +void vn_grid_append_columns (VnGrid * obj, ...); + +#endif \ No newline at end of file diff --git a/vn/vn-gui.c b/vn/vn-gui.c new file mode 100644 index 0000000..f7cd705 --- /dev/null +++ b/vn/vn-gui.c @@ -0,0 +1,1543 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-gui.h" +#include +#include +#include + +#include +#include +#include + +#define MAIN_UI _GUI_DIR"/main.glade" +#define CHILD_WINDOW_UI _GUI_DIR"/child-window.glade" +#define ACTIONS_UI _GUI_DIR"/actions.glade" +#define MENUBAR_XML _GUI_DIR"/menubar.ui" +#define MODULE_DTD _DTD_DIR"/module.dtd" + +#define S(string) (gdome_str_mkref (string)) +#define gtk_builder_get(builder, id) ((gpointer) gtk_builder_get_object (builder, id)) + +/** + * SECTION: vn-gui + * @Short_description: GUI manager + * @Title: VnGui + * + * Manages most of the GUI operations. + **/ +G_DEFINE_TYPE (VnGui, vn_gui, G_TYPE_OBJECT); + +struct _VnWindow +{ + VnGui * obj; + GtkWindow * widget; + GtkNotebook * notebook; + GtkUIManager * manager; + GtkWidget * toolbar; + VnForm * active_form; + GtkToggleAction * dynamic_tabs; + GtkToggleAction * view_toolbar; + guint merge_id; +}; + +typedef struct +{ + VnGui * gui; + gchar * name; +} +FormData; + +typedef struct +{ + GtkActionEntry * entry; + FormData * form; +} +ActionData; + +typedef struct +{ + GSList * action_data; + VnMod * mod; +} +ModData; + +enum { + COL_ICON + ,COL_NAME + ,COL_TITLE + ,COL_TYPE + ,COL_MODULE + ,COL_COUNT +}; + +enum { + LOGOUT + ,EXIT + ,LAST_SIGNAL +}; + +typedef struct +{ + VnGui * obj; + gboolean aux; + GError * error; + GThread * thread; +} +GuiData; + +static void vn_gui_reconnect (VnGui * obj); +static void vn_gui_on_conn_error (DbConn * conn, const GError * error, VnGui * obj); +static void vn_gui_on_conn_status_changed (DbConn * conn, DbConnStatus status, VnGui * obj); +void vn_gui_on_open_form_activated (GtkAction * action, FormData * form_data); +void vn_gui_on_child_destroyed (GtkWindow * widget, VnWindow * window); +void vn_gui_on_page_removed (GtkNotebook * notebook, GtkWidget * page, guint num, VnWindow * window); +void vn_gui_on_main_page_removed (GtkNotebook * notebook, GtkWidget * page, guint num, VnWindow * window); + +static guint signals[LAST_SIGNAL] = {0}; + +/** + * vn_gui_new: + * @app: a #GtkApplication + * @conn: the #VnLogin + * + * Creates a new Gui object. + * + * Return value: the created #VnGui + **/ +VnGui * vn_gui_new (GtkApplication * app, DbConn * conn) +{ + return g_object_new (VN_TYPE_GUI, "app", app, "conn", conn, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_gui_free_action_data (ActionData * data) +{ + if (data) + { + if (data->form) + g_free (data->form->name); + + g_free (data->form); + + if (data->entry) + { + g_free ((gchar *) data->entry->name); + g_free ((gchar *) data->entry->stock_id); + g_free ((gchar *) data->entry->label); + g_free ((gchar *) data->entry->accelerator); + g_free ((gchar *) data->entry->tooltip); + } + + g_free (data->entry); + } + + g_free (data); +} + +static void vn_gui_free_mod_data (ModData * data) +{ + if (data->action_data) + g_slist_free_full (data->action_data, + (GDestroyNotify) vn_gui_free_action_data); +} + +/* + * Frees the #GuiData struct. + */ +static void gui_data_free (GuiData * gui_data) +{ + if (gui_data->error) + g_error_free (gui_data->error); + + g_object_unref (gui_data->obj); + g_thread_unref (gui_data->thread); + g_free (gui_data); +} + +static void vn_gui_free_window (VnGui * obj, VnWindow * window) +{ + g_signal_handlers_disconnect_by_func (window->widget, + vn_gui_on_child_destroyed, window); + g_signal_handlers_disconnect_by_func (window->notebook, + vn_gui_on_page_removed, window); + g_signal_handlers_disconnect_by_func (window->notebook, + vn_gui_on_main_page_removed, window); + + gtk_widget_destroy (GTK_WIDGET (window->widget)); + g_object_unref (window->manager); + g_free (window); +} + +static VnWindow * vn_gui_get_active_window (VnGui * obj) +{ + GSList * n; + + for (n = obj->windows; n; n = n->next) + if (gtk_window_is_active (((VnWindow *) n->data)->widget)) + return n->data; + + return obj->main_window; +} + +/* + * Loads a module and all of its forms. + */ +static void vn_gui_load_module (VnGui * obj, const gchar * dir, const gchar * file) +{ + gint n; + gchar * path; + gchar * mod_title_strip, * mod_name; + GModule * module = NULL; + VnMod * mod = NULL; + GdomeException e; + GdomeDocument * doc; + GdomeDOMImplementation * di; + GdomeNodeList * nl, * action_nl; + GdomeElement * el; + GdomeNode * node; + GdomeDOMString * mod_title; + GdomeDOMString * library; + + // Validating the module definition against the DTD (using libxml2 directly) + + xmlValidCtxtPtr ctxt = xmlNewValidCtxt (); + + path = g_strdup_printf ("%s/%s", dir, file); + + if (ctxt) + { + gboolean valid = FALSE; + xmlDocPtr doc = xmlParseFile (path); + + if (doc) + { + xmlDtdPtr dtd = xmlParseDTD (NULL, (const xmlChar *) MODULE_DTD); + + if (dtd) + valid = xmlValidateDtd (ctxt, doc, dtd); + else + g_warning ("The DTD is not well formed"); + + xmlFreeDtd (dtd); + } + + xmlFreeValidCtxt (ctxt); + xmlFreeDoc (doc); + + if (!valid) + { + g_free (path); + g_warning ("The module definition at \"%s\" is not valid", file); + return; + } + } + + // Getting the module info from XML file + + di = gdome_di_mkref (); + doc = gdome_di_createDocFromURI (di, path, 0, &e); + + g_free (path); + + gdome_di_unref (di, &e); + + if (doc) + { + nl = gdome_doc_getElementsByTagName (doc, S("library"), &e); + el = (GdomeElement *) gdome_nl_item (nl, 0, &e); + gdome_nl_unref (nl, &e); + + library = gdome_el_getAttribute (el, S("name"), &e); + mod_name = g_strdup (library->str); + + node = gdome_el_firstChild (el, &e); + mod_title = gdome_n_nodeValue (node, &e); + mod_title_strip = g_strstrip (g_strdup (mod_title->str)); + + nl = gdome_doc_getElementsByTagName (doc, S("form"), &e); + action_nl = gdome_doc_getElementsByTagName (doc, S("action"), &e); + + gdome_str_unref (library); + gdome_doc_unref (doc, &e); + gdome_n_unref (node, &e); + gdome_el_unref (el, &e); + } + else + { + g_warning ("VnGui: Error loading module info file: %s", (gchar *) file); + return; + } + + // Loading the module dynamically + + for (n = 0; obj->lib_dirs[n] && !module; n++) + { + path = g_module_build_path (obj->lib_dirs[n], mod_name); + module = g_module_open (path, 0); + g_free (path); + } + + if (module) + { + g_module_make_resident (module); + mod = g_object_new (VN_TYPE_MOD + ,"name" ,mod_name + ,"data-dir" ,dir + ,"module" ,module + ,NULL + ); + } + else + g_warning ("VnGui: Can't load module %s: %s" + ,mod_name + ,g_module_error () + ); + + // If successful, load forms, actions and UI + + if (mod) + { + GType type; + gulong len, n; + gchar * c_name; + gchar * title_strip; + gchar * ui_file; + gchar * buffer; + GdomeDOMString * name; + GdomeDOMString * icon; + GdomeDOMString * action_name; + GdomeDOMString * accel; + GdomeDOMString * title; + gchar * symbol_name; + const gchar * text_dom; + GtkTreeIter parent_iter; + GtkTreeIter * iter; + VnFormGetTypeFunc mod_get_type_func; + GError * err = NULL; + GSList * mod_actions = NULL; + ActionData * action_data; + GtkActionGroup * actions = gtk_action_group_new (mod_name); + + // Creating folder to put forms inside + + text_dom = vn_mod_get_text_domain (mod); + gtk_tree_store_append (obj->tree, &parent_iter, NULL); + gtk_tree_store_set (obj->tree, &parent_iter + ,COL_ICON ,"gtk-directory" + ,COL_TITLE ,g_dgettext (text_dom, mod_title_strip) + ,COL_TYPE ,G_TYPE_NONE + ,COL_NAME ,NULL + ,-1 + ); + + len = gdome_nl_length (nl, &e); + + for (n = 0; n < len; n++) + { + // Getting form info + + el = (GdomeElement *) gdome_nl_item (nl, n, &e); + icon = gdome_el_getAttribute (el, S("icon"), &e); + action_name = gdome_el_getAttribute (el, S("action-name"), &e); + accel = gdome_el_getAttribute (el, S("accel"), &e); + + name = gdome_el_getAttribute (el, S("name"), &e); + c_name = g_strdelimit (g_strdup (name->str), "-. ", '_'); + + node = gdome_el_firstChild (el, &e); + title = gdome_n_nodeValue (node, &e); + title_strip = g_strstrip (g_strdup (title->str)); + + gdome_n_unref (node, &e); + gdome_el_unref (el, &e); + + // Loading form + + symbol_name = g_strdup_printf ("vn_%s_get_type", c_name); + + if (g_module_symbol (module, symbol_name, (gpointer) &mod_get_type_func)) + { + type = mod_get_type_func (); + + if (g_type_is_a (type, VN_TYPE_FORM)) + { + iter = g_new (GtkTreeIter, 1); + gtk_tree_store_append (obj->tree, iter, &parent_iter); + gtk_tree_store_set (obj->tree, iter + ,COL_NAME ,name->str + ,COL_ICON ,icon->str + ,COL_TITLE ,g_dgettext (text_dom, title_strip) + ,COL_TYPE ,type + ,COL_MODULE ,mod + ,-1 + ); + g_hash_table_replace (obj->forms, g_strdup (name->str), iter); + + if (g_strcmp0 ("", action_name->str)) + { + action_data = g_new (ActionData, 1); + action_data->entry = g_new (GtkActionEntry, 1); + action_data->entry->name = g_strdup (action_name->str); + action_data->entry->stock_id = g_strdup (icon->str); + action_data->entry->label = g_strdup (title_strip); + action_data->entry->accelerator = g_strdup (accel->str); + action_data->entry->tooltip = NULL; + action_data->entry->callback = + (GCallback) vn_gui_on_open_form_activated; + + action_data->form = g_new (FormData, 1); + action_data->form->name = g_strdup (name->str); + action_data->form->gui = obj; + + mod_actions = g_slist_prepend (mod_actions, action_data); + + gtk_action_group_add_actions (actions + ,action_data->entry, 1 + ,action_data->form + ); + } + } + else + g_warning ("VnGui: %s isn't a VnForm", g_type_name (type)); + } + else + g_warning ("VnGui: Error loading form: %s", g_module_error ()); + + g_free (c_name); + gdome_str_unref (name); + gdome_str_unref (icon); + gdome_str_unref (action_name); + gdome_str_unref (accel); + gdome_str_unref (title); + g_free (title_strip); + g_free (symbol_name); + } + + len = gdome_nl_length (action_nl, &e); + + for (n = 0; n < len; n++) + { + el = (GdomeElement *) gdome_nl_item (action_nl, n, &e); + name = gdome_el_getAttribute (el, S("name"), &e); + + node = gdome_el_firstChild (el, &e); + title = gdome_n_nodeValue (node, &e); + title_strip = g_strstrip (g_strdup (title->str)); + + gdome_el_unref (el, &e); + gdome_n_unref (node, &e); + gdome_str_unref (title); + + action_data = g_new (ActionData, 1); + action_data->entry = g_new0 (GtkActionEntry, 1); + action_data->entry->name = g_strdup (name->str); + action_data->entry->label = g_strdup (title_strip); + action_data->form = NULL; + + mod_actions = g_slist_prepend (mod_actions, action_data); + + gtk_action_group_add_actions (actions, action_data->entry, 1, NULL); + + gdome_str_unref (name); + g_free (title_strip); + } + + ui_file = g_strdup_printf ("%s/%s.ui", dir, mod_name); + + if (g_file_get_contents (ui_file, &buffer, NULL, &err)) + { + ModData * mod_data = g_new (ModData, 1); + mod_data->action_data = mod_actions; + mod_data->mod = mod; + + vn_mod_set_ui (mod, buffer); + obj->modules = g_slist_prepend (obj->modules, mod_data); + } + else + { + g_warning ("VnGui: %s", err->message); + g_error_free (err); + g_slist_free_full (mod_actions, + (GDestroyNotify) vn_gui_free_action_data); + } + + g_free (ui_file); +// g_object_unref (mod); + g_object_unref (actions); + } + + g_free (mod_title_strip); + g_free (mod_name); + gdome_str_unref (mod_title); + gdome_nl_unref (nl, &e); + gdome_nl_unref (action_nl, &e); +} + +static VnWindow * vn_gui_add_window (VnGui * obj, GtkWindow * widget, GtkNotebook * notebook) +{ + GdkRGBA color; + GSList * n, * m; + GError * err = NULL; + GtkActionGroup * actions = NULL; + GtkStyleContext * style; + GtkBuilder * builder; + VnWindow * window; + + window = g_new (VnWindow, 1); + window->obj = obj; + window->widget = widget; + window->notebook = notebook; + window->manager = gtk_ui_manager_new (); + window->active_form = NULL; + window->merge_id = 0; + + obj->windows = g_slist_prepend (obj->windows, window); + gtk_application_add_window (obj->app, widget); + + gtk_notebook_set_group_name (notebook, + g_application_get_application_id (G_APPLICATION (obj->app))); + + style = gtk_widget_get_style_context (GTK_WIDGET (widget)); + gtk_style_context_get_background_color (style, + GTK_STATE_FLAG_NORMAL, &color); + gtk_widget_override_background_color (GTK_WIDGET (notebook), + GTK_STATE_FLAG_NORMAL, &color); + + gtk_widget_show_all (GTK_WIDGET (widget)); + + // Loading the bars and associated actions + + builder = gtk_builder_new (); + + if (gtk_builder_add_from_file (builder, ACTIONS_UI, &err)) + { + actions = gtk_builder_get (builder, "main-actions"); + gtk_builder_connect_signals (builder, window); + gtk_ui_manager_insert_action_group (window->manager, actions, -1); + } + else + { + g_warning ("VnGui: %s", err->message); + g_clear_error (&err); + } + + if (gtk_ui_manager_add_ui_from_file (window->manager, MENUBAR_XML, &err)) + { + GtkBox * box = GTK_BOX (gtk_bin_get_child (GTK_BIN (widget))); + GtkWidget * menubar = gtk_ui_manager_get_widget (window->manager, "/MenuBar"); + GtkWidget * toolbar = gtk_ui_manager_get_widget (window->manager, "/Toolbar"); + + gtk_box_pack_start (box, menubar, FALSE, FALSE, 0); + gtk_box_pack_start (box, toolbar, FALSE, FALSE, 0); + gtk_style_context_add_class (gtk_widget_get_style_context (toolbar), + GTK_STYLE_CLASS_PRIMARY_TOOLBAR); + gtk_window_add_accel_group (widget, + gtk_ui_manager_get_accel_group (window->manager)); + + window->toolbar = toolbar; + } + else + { + window->toolbar = NULL; + g_warning ("VnGui: %s", err->message); + g_error_free (err); + } + + if (actions) + { + // TODO: Load from config file the default value for toggle actions. + + window->dynamic_tabs = gtk_builder_get (builder, "menu-view-tabs"); + gtk_toggle_action_set_active (window->dynamic_tabs, TRUE); + + window->view_toolbar = gtk_builder_get (builder, "menu-view-toolbar"); + gtk_toggle_action_set_active (window->view_toolbar, TRUE); + } + + g_object_unref (builder); + + // Loading the modules actions + + for (n = obj->modules; n; n = n->next) + { + ModData * mod_data = n->data; + GtkActionGroup * mod_actions = gtk_action_group_new (vn_mod_get_name (mod_data->mod)); + + for (m = mod_data->action_data; m; m = m->next) + { + ActionData * action_data = m->data; + + gtk_action_group_add_actions (mod_actions + ,action_data->entry, 1 + ,action_data->form + ); + } + + gtk_ui_manager_insert_action_group (window->manager, mod_actions, -1); + g_object_unref (mod_actions); + + if (!gtk_ui_manager_add_ui_from_string (window->manager, + vn_mod_get_ui (mod_data->mod), -1, &err)) + { + g_warning ("VnGui: %s", err->message); + g_error_free (err); + } + } + + gtk_ui_manager_ensure_update (window->manager); + return window; +} + +/* + * Shows an error dialog. + */ +static void vn_gui_show_error (VnGui * obj, const GError * error) +{ + GtkWidget * dialog; + GtkWindow * window = vn_gui_get_active_window (obj)->widget; + + if (error && error->code == DB_CONN_ERROR_LOST) + dialog = gtk_message_dialog_new (window + ,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT + ,GTK_MESSAGE_QUESTION + ,GTK_BUTTONS_YES_NO + ,_("Connection has been lost. Do you want to reconnect?") + ); + else + dialog = gtk_message_dialog_new (window + ,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT + ,GTK_MESSAGE_WARNING + ,GTK_BUTTONS_CLOSE + ,_("An error occurred in the connection.") + ); + + gtk_window_set_title (GTK_WINDOW (dialog), _("Database error")); + + if (error) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", error->message); + else + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Unknown error")); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) + vn_gui_reconnect (obj); + + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +/* + * Closes and saves the GUI interface. + */ +static void vn_gui_close (VnGui * obj) +{ + GSList * n; + + g_return_if_fail (VN_IS_GUI (obj)); + + if (obj->main_window) + { + vn_gui_save (obj); + g_object_disconnect (obj->conn + ,"any_signal", vn_gui_on_conn_error, obj + ,"any_signal", vn_gui_on_conn_status_changed, obj + ,NULL + ); + + for (n = obj->windows; n; n = n->next) + vn_gui_free_window (obj, n->data); + + g_slist_free (obj->windows); + obj->windows = NULL; + obj->main_window = NULL; + } +} + +/* + * Idle function that completes the reopen thread. + */ +static gboolean vn_gui_reconnect_idle (GuiData * gui_data) +{ + if (!gui_data->aux) + vn_gui_show_error (gui_data->obj, gui_data->error); + + return FALSE; +} + +/* + * Thread function that tryes to reopen the connection asynchronously. + */ +static void vn_gui_reconnect_thread (GuiData * gui_data) +{ + gui_data->aux = db_conn_reconnect (gui_data->obj->conn, &gui_data->error); + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) vn_gui_reconnect_idle, gui_data, (GDestroyNotify) gui_data_free); +} + +/* + * Reconnects to database. + */ +void vn_gui_reconnect (VnGui * obj) +{ + GuiData * gui_data = g_new (GuiData, 1); + gui_data->obj = g_object_ref (obj); + gui_data->aux = FALSE; + gui_data->error = NULL; + gui_data->thread = g_thread_new ("vn-gui-reconnect", + (GThreadFunc) vn_gui_reconnect_thread, gui_data); +} + +/* + * Saves the login information and closes the main GUI. + */ +static gboolean vn_gui_logout_idle (GuiData * gui_data) +{ + vn_gui_close (gui_data->obj); + + if (gui_data->aux) + g_signal_emit (gui_data->obj, signals[EXIT], 0); + else + g_signal_emit (gui_data->obj, signals[LOGOUT], 0); + + return FALSE; +} + +/* + * Thread function that tryes to close the connection asynchronously. + */ +static void vn_gui_logout_thread (GuiData * gui_data) +{ + db_conn_close (gui_data->obj->conn, TRUE); + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) vn_gui_logout_idle, gui_data, (GDestroyNotify) gui_data_free); +} + +/* + * Closes the connection and the user interface. + */ +void vn_gui_logout (VnGui * obj, gboolean exit) +{ + GuiData * gui_data = g_new (GuiData, 1); + gui_data->obj = g_object_ref (obj); + gui_data->aux = exit; + gui_data->error = NULL; + gui_data->thread = g_thread_new ("vn-gui-close", + (GThreadFunc) vn_gui_logout_thread, gui_data); +} + +static void vn_gui_hide_form (VnWindow * window) +{ + if (window->active_form) + { + GtkActionGroup * actions = + vn_form_get_action_group (window->active_form); + + if (actions) + { + gtk_ui_manager_remove_ui (window->manager, window->merge_id); + gtk_ui_manager_remove_action_group (window->manager, actions); + } + + window->active_form = NULL; + } +} + +static void vn_gui_set_show_tabs (VnWindow * window) +{ + gboolean show_tabs = gtk_notebook_get_n_pages (window->notebook) > 1 + || !gtk_toggle_action_get_active (window->dynamic_tabs); + + gtk_notebook_set_show_tabs (window->notebook, show_tabs); +} + +//--------------------------------------------------- Window handlers + +/* + * Called when the main window is closed. + */ +gboolean vn_gui_on_main_deleted (GtkWidget * widget, GdkEvent * event, VnWindow * window) +{ + vn_gui_logout (window->obj, TRUE); + return TRUE; +} + +/* + * Called when a child window is closed. + */ +void vn_gui_on_child_destroyed (GtkWindow * widget, VnWindow * window) +{ + VnGui * obj = window->obj; + + vn_gui_free_window (obj, window); + obj->windows = g_slist_remove (obj->windows, window); +} + +//--------------------------------------------------- Notebook handlers + +/* + * Called when a page is detached from a notebook. This function creates a new + * window with a notebook and puts the page in. + * Connected to the "create-window" signal of GtkNotebook. + */ +GtkNotebook * vn_gui_on_page_detached (GtkNotebook * old_notebook, + GtkWidget * page, gint x, gint y, VnWindow * window) +{ + VnWindow * new_window = vn_gui_create_window (window->obj, x, y); + return (window) ? new_window->notebook : NULL; +} + +/* + * Called when the focus changes from a page to another. It is also used in + * newly opened pages. + */ +void vn_gui_on_switch_page (GtkNotebook * notebook, VnForm * form, guint num, VnWindow * window) +{ + GError * err = NULL; + GtkTreeIter * iter; + GtkActionGroup * actions; + VnGui * obj = window->obj; + + vn_gui_hide_form (window); + + // Merge form UI with the window UI + + window->active_form = form; + + if ((iter = g_hash_table_lookup (obj->forms, vn_form_get_name (form)))) + { + gchar * window_title, * form_title; + + gtk_tree_model_get (GTK_TREE_MODEL (obj->tree), + iter, COL_TITLE, &form_title, -1); + + window_title = g_strdup_printf ("%s - %s", form_title, obj->app_title); + gtk_window_set_title (window->widget, window_title); + + g_free (form_title); + g_free (window_title); + } + + if ((actions = vn_form_get_action_group (form))) + { + guint merge_id; + const gchar * form_ui = vn_form_get_ui_manager (form); + + gtk_ui_manager_insert_action_group (window->manager, actions, -1); + + if ((merge_id = gtk_ui_manager_add_ui_from_string (window->manager, + form_ui, -1, &err))) + { + window->merge_id = merge_id; + } + else + { + g_warning ("VnGui: %s", err->message); + g_error_free (err); + } + } + + gtk_ui_manager_ensure_update (window->manager); +} + +void vn_gui_on_page_added (GtkNotebook * notebook, + GtkWidget * page, guint num, VnWindow * window) +{ + vn_gui_set_show_tabs (window); +} + +/* + * Called when a page of the main window is removed from its notebook. + */ +void vn_gui_on_main_page_removed (GtkNotebook * notebook, + GtkWidget * page, guint num, VnWindow * window) +{ + vn_gui_set_show_tabs (window); + + if (gtk_notebook_get_n_pages (notebook) < 1) + { + vn_gui_hide_form (window); + gtk_window_set_title (window->widget, window->obj->app_title); + } +} + +/* + * Called when a page of a child window is removed from its notebook, if its + * the last page destroys the window too. + */ +void vn_gui_on_page_removed (GtkNotebook * notebook, + GtkWidget * page, guint num, VnWindow * window) +{ + vn_gui_set_show_tabs (window); + + if (gtk_notebook_get_n_pages (notebook) < 1) + gtk_widget_destroy (GTK_WIDGET (window->widget)); +} + +//--------------------------------------------------- Action handlers + +/* + * Opens a form when the action associated to it is activated. + */ +void vn_gui_on_open_form_activated (GtkAction * action, FormData * form_data) +{ + vn_gui_open_form (form_data->gui, form_data->name); +} + +/* + * Reconnects to database. + */ +void vn_gui_on_open_activated (GtkAction * action, VnWindow * window) +{ + vn_gui_reconnect (window->obj); +} + +/* + * Logout action handler. + */ +void vn_gui_on_logout_activated (GtkAction * action, VnWindow * window) +{ + vn_gui_logout (window->obj, FALSE); +} + +/* + * Exit action handler. + */ +void vn_gui_on_exit_activated (GtkAction * action, VnWindow * window) +{ + vn_gui_logout (window->obj, TRUE); +} + +/* + * Closes the current tab when the close-tab action is activated. + */ +void vn_gui_on_close_tab_activated (GtkAction * action, VnWindow * window) +{ + if (window->active_form) + vn_gui_close_form (window->obj, window->active_form); +} + +/* + * Shows/hides the tabs when the view-tabs action is activated + */ +void vn_gui_on_dynamic_tabs_activated (GtkToggleAction * action, VnWindow * window) +{ + vn_gui_set_show_tabs (window); +} + +/* + * Shows/hides the toolbar when the view-toolbar action is activated + */ +void vn_gui_on_view_toolbar_activated (GtkToggleAction * action, VnWindow * window) +{ + gtk_widget_set_visible (window->toolbar, gtk_toggle_action_get_active (action)); +} + +/* + * Shows a window with program information. + */ +void vn_gui_on_about_activated (GtkAction * action, VnWindow * window) +{ + gtk_dialog_run (window->obj->about); +} + +//--------------------------------------------------- Connection handlers + +/* + * Called when there is a query error in the connection. + */ +static void vn_gui_on_conn_error (DbConn * conn, const GError * error, VnGui * obj) +{ + vn_gui_show_error (obj, error); +} + +/* + * Enables/disables the #GtkSpinner when connection is loading. + */ +static void vn_gui_on_conn_status_changed (DbConn * conn, DbConnStatus status, VnGui * obj) +{ + gchar * status_text; + + if (status & DB_CONN_CLOSING) + status_text = _("Closing connection"); + else if (status & DB_CONN_TRANSACTION) + status_text = _("Transaction started"); + else if (status & DB_CONN_OPENING) + status_text = _("Connecting"); + else if (status & DB_CONN_LOST) + status_text = _("Connection lost"); + else if (status == DB_CONN_CLOSED) + status_text = _("Connection closed"); + else if (status & DB_CONN_LOADING) + status_text = _("Loading"); + else + status_text = _("Ready"); + + gtk_label_set_text (obj->status_label, status_text); + + if (status & DB_CONN_LOADING) + { + // XXX: Used to fix a bug that causes the GtkSpinner not spin. + gtk_widget_hide (GTK_WIDGET (obj->spinner)); + gtk_widget_show (GTK_WIDGET (obj->spinner)); + + gtk_spinner_start (obj->spinner); + } + else + gtk_spinner_stop (obj->spinner); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_gui_open: + * @obj: a #VnGui + * + * Shows the main GUI. + **/ +void vn_gui_open (VnGui * obj) +{ + GtkBuilder * builder; + GError * err = NULL; + + g_return_if_fail (VN_IS_GUI (obj)); + g_return_if_fail (!obj->main_window); + + builder = gtk_builder_new (); + + if (gtk_builder_add_from_file (builder, MAIN_UI, &err)) + { + gchar * user; + GtkLabel * label; + GKeyFile * config; + GtkWindow * widget; + GtkNotebook * notebook; + + obj->spinner = gtk_builder_get (builder, "spinner"); + obj->about = gtk_builder_get (builder, "about"); + obj->menu = gtk_builder_get (builder, "menu"); + obj->status_label = gtk_builder_get (builder, "status-label"); + + user = db_conn_get_user (obj->conn); + label = gtk_builder_get (builder, "user-name"); + gtk_label_set_text (label, user); + g_free (user); + + widget = gtk_builder_get (builder, "window"); + notebook = gtk_builder_get (builder, "notebook"); + obj->main_window = vn_gui_add_window (obj, widget, notebook); + gtk_builder_connect_signals (GTK_BUILDER (builder), obj->main_window); + + // Restoring interface + + config = g_key_file_new (); + + if (g_key_file_load_from_file (config, obj->config_file, 0, NULL)) + { + gsize m, n; + gsize len; + gint x, y; + gint width, height; + gchar ** windows; + gchar ** forms; + VnWindow * window; + + windows = g_key_file_get_groups (config, &len); + + for (m = 0; m < len; m++) + { + x = g_key_file_get_integer (config, windows[m], "x", NULL); + y = g_key_file_get_integer (config, windows[m], "y", NULL); + width = g_key_file_get_integer (config, windows[m], "width", NULL); + height = g_key_file_get_integer (config, windows[m], "height", NULL); + + if (g_key_file_get_boolean (config, windows[m], "main", NULL)) + { + window = obj->main_window; + gtk_window_move (window->widget, x, y); + } + else + window = vn_gui_create_window (obj, x, y); + + gtk_window_resize (window->widget, width, height); + + forms = g_key_file_get_string_list (config, + windows[m], "forms", NULL, NULL); + + for (n = 0; forms[n]; n++) + vn_gui_open_form_at_window (obj, forms[n], window); + + g_key_file_remove_group (config, windows[m], NULL); + g_strfreev (forms); + } + + g_strfreev (windows); + } + + g_key_file_free (config); + + g_object_connect (obj->conn + ,"signal::error", vn_gui_on_conn_error, obj + ,"signal::status-changed", vn_gui_on_conn_status_changed, obj + ,NULL + ); + } + else if (err) + { + g_warning ("VnGui: %s", err->message); + g_error_free (err); + } + + g_object_unref (builder); +} + +/** + * vn_gui_save: + * @obj: the #VnGui + * + * Saves the GUI configuration. + **/ +void vn_gui_save (VnGui * obj) +{ + gint len; + gint j, i = 0; + gchar * group; + const gchar ** forms; + GSList * m; + GList * nb_pages, * n; + VnWindow * window; + gint x, y, width, height; + GKeyFile * config; + + g_return_if_fail (VN_IS_GUI (obj)); + g_return_if_fail (obj->main_window); + + // Saving the interface configuration + + config = g_key_file_new (); + + for (m = obj->windows; m; m = m->next) + { + window = (VnWindow *) m->data; + group = g_strdup_printf ("window%d", i++); + + // Saving the window position and size + + gtk_window_get_position (window->widget, &x, &y); + gtk_window_get_size (window->widget, &width, &height); + + g_key_file_set_integer (config, group, "x", x); + g_key_file_set_integer (config, group, "y", y); + g_key_file_set_integer (config, group, "width", width); + g_key_file_set_integer (config, group, "height", height); + + // Saving the forms opened at window + + if (window == obj->main_window) + g_key_file_set_boolean (config, group, "main", TRUE); + + nb_pages = gtk_container_get_children (GTK_CONTAINER (window->notebook)); + len = gtk_notebook_get_n_pages (window->notebook); + forms = g_new (const gchar *, len); + + for (j = 0, n = nb_pages; n; n = n->next) + forms[j++] = vn_form_get_name (n->data); + + g_key_file_set_string_list (config, group, "forms", forms, len); + + g_list_free (nb_pages); + g_free (forms); + g_free (group); + } + + gvn_key_file_save (config, obj->config_file); + g_key_file_free (config); +} + +/** + * vn_gui_create_window: + * @obj: the #VnGui + * @x: the x coordinate + * @y: the y coordinate + * + * Creates a new window. + * + * Return value: VnWindow. + **/ +VnWindow * vn_gui_create_window (VnGui * obj, gint x, gint y) +{ + GtkBuilder * builder; + VnWindow * window = NULL; + GError * err = NULL; + + g_return_val_if_fail (VN_IS_GUI (obj), NULL); + g_return_val_if_fail (obj->main_window, NULL); + + builder = gtk_builder_new (); + + if (gtk_builder_add_from_file (builder, CHILD_WINDOW_UI, &err)) + { + GtkWindow * widget; + GtkNotebook * notebook; + + widget = gtk_builder_get (builder, "child"); + notebook = gtk_builder_get (builder, "notebook"); + gtk_window_move (widget, x, y); + + window = vn_gui_add_window (obj, widget, notebook); + gtk_builder_connect_signals (builder, window); + } + else + { + g_warning ("VnGui: %s", err->message); + g_error_free (err); + } + + g_object_unref (builder); + return window; +} + +/** + * vn_gui_open_form_at_window: + * @obj: the #VnGui + * @form_name: the name of the form that you want to open + * @window: the #VnWindow where the form will be opened + * + * Opens a new form at the specified window. + * + * Return value: (transfer none): the created #VnForm + **/ +VnForm * vn_gui_open_form_at_window (VnGui * obj, const gchar * form_name, VnWindow * window) +{ + gchar * icon; + gchar * title; + VnMod * module; + GType form_type; + GtkBox * hbox; + GtkWidget * form; + GtkWidget * widget; + GtkWidget * button; + GtkTreeIter * iter; + GtkNotebook * notebook = NULL; + + g_return_val_if_fail (VN_IS_GUI (obj), NULL); + g_return_val_if_fail (obj->main_window, NULL); + + iter = g_hash_table_lookup (obj->forms, form_name); + + if (!iter) + { + g_warning ("VnGui: Form %s doesn't exist", form_name); + return NULL; + } + + gtk_tree_model_get (GTK_TREE_MODEL (obj->tree), iter + ,COL_ICON ,&icon + ,COL_TITLE ,&title + ,COL_MODULE ,&module + ,COL_TYPE ,&form_type + ,-1 + ); + + form = g_object_new (form_type + ,"name" ,form_name + ,"gui" ,obj + ,"module" ,module + ,NULL + ); + vn_form_open (VN_FORM (form)); + + hbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5)); + + widget = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU); + gtk_box_pack_start (hbox, widget, FALSE, FALSE, 0); + + widget = gtk_label_new (title); + gtk_box_pack_start (hbox, widget, TRUE, TRUE, 0); + + button = gtk_button_new (); + g_signal_connect (button, "clicked", + G_CALLBACK (vn_gui_close_form), form); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_box_pack_start (hbox, button, FALSE, FALSE, 0); + + widget = gtk_image_new_from_icon_name ("window-close", GTK_ICON_SIZE_MENU); + gtk_button_set_image (GTK_BUTTON (button), widget); + + if (!window) + window = vn_gui_get_active_window (obj); + + notebook = window->notebook; + gtk_notebook_set_current_page (notebook, + gtk_notebook_append_page (notebook, form, GTK_WIDGET (hbox))); + gtk_notebook_set_tab_detachable (notebook, form, TRUE); + gtk_notebook_set_tab_reorderable (notebook, form, TRUE); + gtk_widget_show_all (GTK_WIDGET (hbox)); + gtk_widget_show (form); + + g_free (icon); + g_free (title); + + return VN_FORM (form); +} + +/** + * vn_gui_open_form: + * @obj: the #VnGui + * @form_name: the name of the form that you want to open + * + * Opens a new form, creating a new page for it at the main notebook. + * + * Return value: (transfer none): the created #VnForm + **/ +VnForm * vn_gui_open_form (VnGui * obj, const gchar * form_name) +{ + g_return_val_if_fail (VN_IS_GUI (obj), NULL); + g_return_val_if_fail (form_name, NULL); + + return vn_gui_open_form_at_window (obj, form_name, NULL); +} + +/** + * vn_gui_close_form: + * @obj: the #VnGui + * @form: the #VnForm that you want to close + * + * Closes a form. + **/ +void vn_gui_close_form (VnGui * obj, VnForm * form) +{ + gint num; + GtkNotebook * notebook; + + g_return_if_fail (VN_IS_FORM (form)); + + notebook = GTK_NOTEBOOK (gtk_widget_get_ancestor ( + GTK_WIDGET (form), GTK_TYPE_NOTEBOOK)); + num = gtk_notebook_page_num (notebook, GTK_WIDGET (form)); + gtk_notebook_remove_page (notebook, num); +} + +/** + * vn_gui_get_conn: + * @obj: a #VnGui + * + * Gets the Data Base connection used by @obj + * + * Return value: (transfer none): the #DbConn + **/ +DbConn * vn_gui_get_conn (VnGui * obj) +{ + g_return_val_if_fail (VN_IS_GUI (obj), NULL); + + return obj->conn; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_CONN = 1 + ,PROP_APP +}; + +static void vn_gui_set_property (VnGui * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_CONN: + { + gchar * query_path = g_build_path (G_SEARCHPATH_SEPARATOR_S + ,_VN_MODULE_QUERY_DIR + ,g_getenv ("VN_MODULE_QUERY_PATH") + ,NULL + ); + + obj->conn = g_value_dup_object (value); + db_conn_set_query_path (obj->conn, query_path); + + g_free (query_path); + break; + } + case PROP_APP: + { + gint n; + const gchar * app_id; + gchar * app_name; + gchar * config_dir; + gchar * lib_path, * data_path; + + obj->app = g_value_dup_object (value); + + app_id = g_application_get_application_id (G_APPLICATION (obj->app)); + app_name = g_strrstr (app_id, ".") + 1; + + obj->app_title = g_strdup (app_name); + obj->app_title[0] = g_ascii_toupper (obj->app_title[0]); + + config_dir = g_build_filename (g_get_user_config_dir (), app_name, NULL); + g_mkdir_with_parents (config_dir, 0600); + obj->config_file = g_build_filename (config_dir, "gui.ini", NULL); + g_free (config_dir); + + // Setting module search paths + + lib_path = g_build_path (G_SEARCHPATH_SEPARATOR_S + ,_VN_MODULE_LIB_DIR + ,g_getenv ("VN_MODULE_LIB_PATH") + ,NULL + ); + data_path = g_build_path (G_SEARCHPATH_SEPARATOR_S + ,_VN_MODULE_DATA_DIR + ,g_getenv ("VN_MODULE_DATA_PATH") + ,NULL + ); + + obj->lib_dirs = g_strsplit (lib_path, G_SEARCHPATH_SEPARATOR_S, 0); + obj->data_dirs = g_strsplit (data_path, G_SEARCHPATH_SEPARATOR_S, 0); + + g_free (data_path); + g_free (lib_path); + + // Initializing modules + + for (n = 0; obj->data_dirs[n]; n++) + { + const gchar * file; + GError * err = NULL; + GDir * dir = g_dir_open (obj->data_dirs[n], 0, &err); + + if (dir) + { + while ((file = g_dir_read_name (dir))) + if (!g_strcmp0 (".xml", g_strrstr (file, "."))) + vn_gui_load_module (obj, obj->data_dirs[n], file); + + g_dir_close (dir); + } + else + { + g_warning ("VnGui: Error opening directory at module data path: %s" + ,err->message + ); + g_error_free (err); + } + } + + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_gui_get_property (VnGui * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_CONN: + g_value_set_object (value, obj->conn); + break; + case PROP_APP: + g_value_set_object (value, obj->app); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_gui_init (VnGui * obj) +{ + obj->main_window = NULL; + obj->about = NULL; + obj->windows = NULL; + obj->conn = NULL; + obj->app = NULL; + obj->app_title = NULL; + obj->modules = NULL; + obj->lib_dirs = NULL; + obj->data_dirs = NULL; + obj->config_file = NULL; + + obj->forms = g_hash_table_new_full ( + (GHashFunc) g_str_hash + ,(GEqualFunc) g_str_equal + ,(GDestroyNotify) g_free + ,(GDestroyNotify) g_free + ); + obj->tree = gtk_tree_store_new (COL_COUNT + ,G_TYPE_STRING // COL_ICON + ,G_TYPE_STRING // COL_NAME + ,G_TYPE_STRING // COL_TITLE + ,G_TYPE_GTYPE // COL_TYPE + ,G_TYPE_OBJECT // COL_MODULE + ); +} + +static void vn_gui_finalize (VnGui * obj) +{ + vn_gui_close (obj); + db_conn_close (obj->conn, FALSE); + + g_hash_table_unref (obj->forms); + g_slist_free_full (obj->modules, (GDestroyNotify) vn_gui_free_mod_data); + g_clear_object (&obj->conn); + g_clear_object (&obj->tree); + g_clear_object (&obj->app); + g_strfreev (obj->lib_dirs); + g_strfreev (obj->data_dirs); + g_free (obj->config_file); + g_free (obj->app_title); + G_OBJECT_CLASS (vn_gui_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_gui_class_init (VnGuiClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) vn_gui_finalize; + klass->set_property = (GObjectSetPropertyFunc) vn_gui_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_gui_get_property; + + signals[LOGOUT] = g_signal_new ("logout", + VN_TYPE_GUI, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + signals[EXIT] = g_signal_new ("exit", + VN_TYPE_GUI, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + + g_object_class_install_property (klass, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by Gui") + ,DB_TYPE_CONN + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); + g_object_class_install_property (klass, PROP_APP, + g_param_spec_object ("app" + ,_("Application") + ,_("The application handler for the entire program") + ,GTK_TYPE_APPLICATION + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/vn/vn-gui.h b/vn/vn-gui.h new file mode 100644 index 0000000..8d3ffa7 --- /dev/null +++ b/vn/vn-gui.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_GUI_H +#define VN_GUI_H + +#include "vn-builder.h" + +#define VN_TYPE_GUI (vn_gui_get_type ()) +#define VN_GUI(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_GUI, VnGui)) +#define VN_IS_GUI(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_GUI)) +#define VN_GUI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_GUI, VnGuiClass)) +#define VN_IS_GUI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_GUI)) +#define VN_GUI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_GUI, VnGuiClass)) + +typedef struct _VnGui VnGui; +typedef struct _VnGuiClass VnGuiClass; + +typedef struct _VnWindow VnWindow; + +#include "vn-mod.h" +#include "vn-form.h" + +/* + * @windows: (element-type VnWindow): + * @modules: (element-type ModData): + */ +struct _VnGui +{ + GObject parent; + DbConn * conn; + GtkApplication * app; + gchar * app_title; + + VnWindow * main_window; + GtkTreeStore * tree; + GtkTreeView * menu; + GtkSpinner * spinner; + GtkDialog * about; + GtkLabel * status_label; + + GHashTable * forms; + GSList * windows; + GSList * modules; + + gchar * config_file; + gchar ** lib_dirs; + gchar ** data_dirs; +}; + +struct _VnGuiClass +{ + GObjectClass parent; +}; + +GType vn_gui_get_type (); +VnGui * vn_gui_new (GtkApplication * app, DbConn * conn); +void vn_gui_open (VnGui * obj); +void vn_gui_save (VnGui * obj); +VnWindow * vn_gui_create_window (VnGui * obj, gint x, gint y); +VnForm * vn_gui_open_form_at_window (VnGui * obj, const gchar * form_name, VnWindow * window); +VnForm * vn_gui_open_form (VnGui * obj, const gchar * form_name); +void vn_gui_close_form (VnGui * obj, VnForm * form); +DbConn * vn_gui_get_conn (VnGui * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-handler.c b/vn/vn-handler.c new file mode 100644 index 0000000..4ff1464 --- /dev/null +++ b/vn/vn-handler.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-handler.h" + +G_DEFINE_TYPE (VnHandler, vn_handler, GTK_TYPE_HBUTTON_BOX); + +/** + * vn_handler_new: + * + * Creates a new handler + * + * Return value: #GtkWidget created. + **/ +GtkWidget * vn_handler_new () +{ + return g_object_new (VN_TYPE_HANDLER, NULL); +} + +/** + * vn_handler_new_with_form: + * @form: #DbIterator where the handler will be created + * + * Creates a new handler into a iterator gived + * + * Return value: #GtkWidget created. + **/ +GtkWidget * vn_handler_new_with_iterator (DbIterator * iterator) +{ + return g_object_new (VN_TYPE_HANDLER, "iterator", iterator, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_handler_on_add_activated (GtkAction * action, VnHandler * obj) +{ + db_iterator_insert (obj->iterator); +} + +static void vn_handler_on_remove_activated (GtkAction * action, VnHandler * obj) +{ + GtkWidget * toplevel; + GtkWidget * dialog; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (obj)); + + dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel) + ,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT + ,GTK_MESSAGE_QUESTION + ,GTK_BUTTONS_OK_CANCEL + ,_("Are you sure you want to delete the selected record?") + ); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) + db_iterator_delete (obj->iterator); + + gtk_widget_destroy (dialog); +} + +static void vn_handler_on_save_activated (GtkAction * action, VnHandler * obj) +{ + db_iterator_perform_operations (obj->iterator); +} + +static void vn_handler_on_undo_activated (GtkAction * action, VnHandler * obj) +{ + GtkWidget * toplevel; + GtkWidget * dialog; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (obj)); + + dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel) + ,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT + ,GTK_MESSAGE_QUESTION + ,GTK_BUTTONS_OK_CANCEL + ,_("Are you sure that you want to undo all changes?") + ); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) + db_iterator_reverse_operations (obj->iterator); + + gtk_widget_destroy (dialog); +} + +static void vn_handler_on_refresh_activated (GtkAction * action, VnHandler * obj) +{ + db_iterator_refresh (obj->iterator); +} + +static void vn_handler_on_move_activated (GtkAction * action, VnHandler * obj) +{ + DbIteratorMove move; + + if (action == obj->move_previous) + move = DB_ITERATOR_MOVE_PREVIOUS; + else if (action == obj->move_next) + move = DB_ITERATOR_MOVE_NEXT; + else if (action == obj->move_last) + move = DB_ITERATOR_MOVE_LAST; + else + move = DB_ITERATOR_MOVE_FIRST; + + db_iterator_move_to (obj->iterator, move); +} + +static void vn_handler_refresh_save_undo_status (VnHandler * obj) +{ + gboolean sensitive = obj->iterator + && db_iterator_has_pending_operations (obj->iterator); + + if (obj->show_flags & VN_HANDLER_SHOW_SAVE) + gtk_action_set_sensitive (obj->save, sensitive); + + if (obj->show_flags & VN_HANDLER_SHOW_UNDO) + gtk_action_set_sensitive (obj->undo, sensitive); +} + +static void vn_handler_refresh_scroll_status (VnHandler * obj) +{ + if (obj->show_flags & VN_HANDLER_SHOW_SCROLL) + { + gboolean sensitive; + gint row = db_iterator_get_row (obj->iterator); + + sensitive = row > 0; + gtk_action_set_sensitive (obj->move_first, sensitive); + gtk_action_set_sensitive (obj->move_previous, sensitive); + + sensitive = row != -1 && row < db_iterator_get_nrows (obj->iterator) - 1; + gtk_action_set_sensitive (obj->move_next, sensitive); + gtk_action_set_sensitive (obj->move_last, sensitive); + } +} + +static void vn_handler_on_data_changed (DbIterator * iterator, VnHandler * obj) +{ + if (obj->show_flags & VN_HANDLER_SHOW_ADD) + { + gboolean sensitive = + db_iterator_get_update_flags (iterator) & DB_MODEL_INSERT + && (db_iterator_get_nrows (iterator) < 1 || !obj->simple_record); + + gtk_action_set_sensitive (obj->add, sensitive); + } + + vn_handler_refresh_save_undo_status (obj); + vn_handler_refresh_scroll_status (obj); +} + +static void vn_handler_on_row_num_changed (DbIterator * iterator, VnHandler * obj) +{ + if (obj->show_flags & VN_HANDLER_SHOW_REMOVE) + { + gboolean sensitive = + db_iterator_get_update_flags (iterator) & DB_MODEL_DELETE + && db_iterator_get_row (iterator) != -1; + + gtk_action_set_sensitive (obj->remove, sensitive); + } + + vn_handler_refresh_scroll_status (obj); +} + +static void vn_handler_on_operations_done (DbIterator * iterator, VnHandler * obj) +{ + vn_handler_refresh_save_undo_status (obj); +} + +static void vn_handler_on_status_changed (DbIterator * iterator, gboolean ready, VnHandler * obj) +{ + gtk_action_group_set_sensitive (obj->group, ready); + vn_handler_on_row_num_changed (iterator, obj); + vn_handler_on_data_changed (iterator, obj); +} + +static void vn_handler_refresh_status (VnHandler * obj) +{ + if (!obj->iterator) + gtk_action_group_set_sensitive (obj->group, FALSE); + else + vn_handler_on_status_changed (obj->iterator, + db_iterator_is_ready (obj->iterator), obj); +} + +static VnHandlerShowFlags entries_flags[] = +{ + VN_HANDLER_SHOW_UNDO + ,VN_HANDLER_SHOW_SAVE + ,VN_HANDLER_SHOW_REFRESH + ,VN_HANDLER_SHOW_REMOVE + ,VN_HANDLER_SHOW_ADD + ,VN_HANDLER_SHOW_SCROLL + ,VN_HANDLER_SHOW_SCROLL + ,VN_HANDLER_SHOW_SCROLL + ,VN_HANDLER_SHOW_SCROLL +}; + +static GtkActionEntry entries[] = +{ + { + "undo" + ,"edit-undo" + ,NULL + ,NULL + ,N_("Undo changes") + ,G_CALLBACK (vn_handler_on_undo_activated) + },{ + "save" + ,"document-save" + ,NULL + ,NULL + ,N_("Save changes") + ,G_CALLBACK (vn_handler_on_save_activated) + },{ + "refresh" + ,"view-refresh" + ,NULL + ,NULL + ,N_("Refresh data") + ,G_CALLBACK (vn_handler_on_refresh_activated) + },{ + "remove" + ,"list-remove" + ,NULL + ,NULL + ,N_("Remove record") + ,G_CALLBACK (vn_handler_on_remove_activated) + },{ + "add" + ,"list-add" + ,NULL + ,NULL + ,N_("Add record") + ,G_CALLBACK (vn_handler_on_add_activated) + },{ + "move-first" + ,"go-first" + ,NULL + ,NULL + ,N_("Move to the first row") + ,G_CALLBACK (vn_handler_on_move_activated) + },{ + "move-previous" + ,"go-previous" + ,NULL + ,NULL + ,N_("Move to the previous row") + ,G_CALLBACK (vn_handler_on_move_activated) + },{ + "move-next" + ,"go-next" + ,NULL + ,NULL + ,N_("Move to the next row") + ,G_CALLBACK (vn_handler_on_move_activated) + },{ + "move-last" + ,"go-last" + ,NULL + ,NULL + ,N_("Move to the last row") + ,G_CALLBACK (vn_handler_on_move_activated) + } +}; + +static guint n_entries = G_N_ELEMENTS (entries); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_handler_get_iterator: + * @obj: a #VnHandler + * + * Gets the iterator handled by #VnHandler + * + * Return value: (transfer none) (allow-none): the #DbIterator + **/ +DbIterator * vn_handler_get_iterator (VnHandler * obj) +{ + return obj->iterator; +} + +/** + * vn_handler_set_iterator: + * @obj: a #VnHandler + * @iterator: the iterator to be handled + * + * Sets the iterator handled by #VnHandler + **/ +void vn_handler_set_iterator (VnHandler * obj, DbIterator * iterator) +{ + g_return_if_fail (VN_IS_HANDLER (obj)); + g_return_if_fail (DB_IS_ITERATOR (iterator) || !iterator); + + if (obj->iterator) + { + g_object_disconnect (obj->iterator + ,"any-signal", vn_handler_on_status_changed, obj + ,"any-signal", vn_handler_on_data_changed, obj + ,"any-signal", vn_handler_on_row_num_changed, obj + ,"any-signal", vn_handler_on_operations_done, obj + ,NULL + ); + g_clear_object (&obj->iterator); + } + if (iterator) + { + obj->iterator = g_object_ref (iterator); + g_object_connect (iterator + ,"signal::status-changed", vn_handler_on_status_changed, obj + ,"signal::data-changed", vn_handler_on_data_changed, obj + ,"signal::row-num-changed", vn_handler_on_row_num_changed, obj + ,"signal::operations-done", vn_handler_on_operations_done, obj + ,NULL + ); + } + + vn_handler_refresh_status (obj); +} + +/** + * vn_handler_set_show_flags: + * @obj: a #VnHandler + * @show_flags: the buttons to be shown + * + * Sets the buttons that will be shown on the interface. + **/ +void vn_handler_set_show_flags (VnHandler * obj, VnHandlerShowFlags show_flags) +{ + gint i; + gint items; + GList * j; + + g_return_if_fail (VN_IS_HANDLER (obj)); + + for (j = gtk_action_group_list_actions (obj->group); j; j = j->next) + gtk_action_group_remove_action (obj->group, j->data); + + items = gtk_toolbar_get_n_items (obj->toolbar); + + for (i = 0; i < items; i++) + { + GtkToolItem * tool_item = gtk_toolbar_get_nth_item (obj->toolbar, 0); + gtk_container_remove (GTK_CONTAINER (obj->toolbar), GTK_WIDGET (tool_item)); + } + + obj->show_flags = show_flags; + + for (i = 0; i < n_entries; i++) + { + if (obj->show_flags & entries_flags[i]) + { + gtk_action_group_add_actions (obj->group, &entries[i], 1, obj); + + GtkAction * action = gtk_action_group_get_action (obj->group, entries[i].name); + + GtkToolItem * item = GTK_TOOL_ITEM (gtk_action_create_tool_item (action)); + gtk_tool_item_set_homogeneous (item, TRUE); + gtk_toolbar_insert (obj->toolbar, item, -1); + } + } + + if (obj->show_flags & VN_HANDLER_SHOW_ADD) + obj->add = gtk_action_group_get_action (obj->group, "add"); + + if (obj->show_flags & VN_HANDLER_SHOW_REMOVE) + obj->remove = gtk_action_group_get_action (obj->group, "remove"); + + if (obj->show_flags & VN_HANDLER_SHOW_SAVE) + obj->save = gtk_action_group_get_action (obj->group, "save"); + + if (obj->show_flags & VN_HANDLER_SHOW_UNDO) + obj->undo = gtk_action_group_get_action (obj->group, "undo"); + + if (obj->show_flags & VN_HANDLER_SHOW_SCROLL) + { + obj->move_first = gtk_action_group_get_action (obj->group, "move-first"); + obj->move_previous = gtk_action_group_get_action (obj->group, "move-previous"); + obj->move_next = gtk_action_group_get_action (obj->group, "move-next"); + obj->move_last = gtk_action_group_get_action (obj->group, "move-last"); + } + + vn_handler_refresh_status (obj); +} + +/** + * vn_handler_set_simple_record: + * @obj: a #VnHandler + * @simple: %FALSE if model allows multiple records, %TRUE otherwise + * + * Sets if it is used to handle a iterator with a single record. + **/ +void vn_handler_set_simple_record (VnHandler * obj, gboolean simple) +{ + g_return_if_fail (VN_IS_HANDLER (obj)); + + obj->simple_record = simple; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_FORM = 1 + ,PROP_SHOW_FLAGS + ,PROP_SIMPLE_RECORD +}; + +static void vn_handler_set_property (VnHandler * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_FORM: + vn_handler_set_iterator (obj, g_value_get_object (value)); + break; + case PROP_SHOW_FLAGS: + vn_handler_set_show_flags (obj, g_value_get_flags (value)); + break; + case PROP_SIMPLE_RECORD: + vn_handler_set_simple_record (obj, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_handler_get_property (VnHandler * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_FORM: + g_value_set_object (value, obj->iterator); + break; + case PROP_SHOW_FLAGS: + g_value_set_flags (value, obj->show_flags); + break; + case PROP_SIMPLE_RECORD: + g_value_set_boolean (value, obj->simple_record); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_handler_init (VnHandler * obj) +{ + GdkRGBA color = {0.0, 0.0, 0.0, 0.0}; + + obj->add = NULL; + obj->remove = NULL; + obj->move_first = NULL; + obj->move_previous = NULL; + obj->move_next = NULL; + obj->move_last = NULL; + obj->iterator = NULL; + + gtk_box_set_spacing (GTK_BOX (obj), 6); + gtk_box_set_homogeneous (GTK_BOX (obj), FALSE); + gtk_button_box_set_layout (GTK_BUTTON_BOX (obj), GTK_BUTTONBOX_END); + + obj->group = gtk_action_group_new ("form-handler"); + + obj->toolbar = GTK_TOOLBAR (g_object_new (GTK_TYPE_TOOLBAR + ,"icon-size", GTK_ICON_SIZE_LARGE_TOOLBAR + ,"toolbar-style", GTK_TOOLBAR_BOTH_HORIZ + ,"show-arrow", FALSE + ,NULL + )); + gtk_widget_override_background_color (GTK_WIDGET (obj->toolbar), + GTK_STATE_FLAG_NORMAL, &color); + gtk_box_pack_end (GTK_BOX (obj), GTK_WIDGET (obj->toolbar), FALSE, FALSE, 0); +} + +static void vn_handler_finalize (VnHandler * obj) +{ + vn_handler_set_iterator (obj, NULL); + G_OBJECT_CLASS (vn_handler_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_handler_class_init (VnHandlerClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_handler_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_handler_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_handler_get_property; + + g_object_class_install_property (k, PROP_FORM, + g_param_spec_object ("iterator" + ,"Iterator" + ,"The handled iterator" + ,DB_TYPE_ITERATOR + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SHOW_FLAGS, + g_param_spec_flags ("show-flags" + ,_("Show flags") + ,_("Sets the buttons that will be shown on the interface") + ,VN_TYPE_HANDLER_SHOW_FLAGS + ,VN_HANDLER_SHOW_UNDO | VN_HANDLER_SHOW_SAVE | VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SIMPLE_RECORD, + g_param_spec_boolean ("simple-record" + ,_("Simple record") + ,_("Sets if it is used to handle a iterator with a single record") + ,FALSE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} + +GType vn_handler_show_flags_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GFlagsValue values[] = + { + {VN_HANDLER_SHOW_REFRESH, "VN_HANDLER_SHOW_REFRESH", "refresh"} + ,{VN_HANDLER_SHOW_UNDO, "VN_HANDLER_SHOW_UNDO", "undo"} + ,{VN_HANDLER_SHOW_SAVE, "VN_HANDLER_SHOW_SAVE", "save"} + ,{VN_HANDLER_SHOW_REMOVE, "VN_HANDLER_SHOW_REMOVE", "remove"} + ,{VN_HANDLER_SHOW_ADD, "VN_HANDLER_SHOW_ADD", "add"} + ,{VN_HANDLER_SHOW_SCROLL, "VN_HANDLER_SHOW_SCROLL", "scroll"} + ,{0, NULL, NULL} + }; + + type = g_flags_register_static + (g_intern_static_string ("VnHandlerShowFlags"), values); + } + + return type; +} diff --git a/vn/vn-handler.h b/vn/vn-handler.h new file mode 100644 index 0000000..d11e962 --- /dev/null +++ b/vn/vn-handler.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_HANDLER_H +#define VN_HANDLER_H + +#include +#include + +#define VN_TYPE_HANDLER (vn_handler_get_type ()) +#define VN_IS_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_HANDLER)) +#define VN_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_HANDLER , VnHandler)) +#define VN_HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_HANDLER, VnHandlerClass)) +#define VN_HANDLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_HANDLER, VnHandlerClass)) + +#define VN_TYPE_HANDLER_SHOW_FLAGS (vn_handler_show_flags_get_type ()) + +typedef struct _VnHandler VnHandler; +typedef struct _VnHandlerClass VnHandlerClass; + +typedef enum +{ + VN_HANDLER_SHOW_REFRESH = 1 << 0 + ,VN_HANDLER_SHOW_UNDO = 1 << 1 + ,VN_HANDLER_SHOW_SAVE = 1 << 2 + ,VN_HANDLER_SHOW_REMOVE = 1 << 3 + ,VN_HANDLER_SHOW_ADD = 1 << 4 + ,VN_HANDLER_SHOW_SCROLL = 1 << 5 +} +VnHandlerShowFlags; + +struct _VnHandler +{ + GtkHButtonBox parent; + DbIterator * iterator; + VnHandlerShowFlags show_flags; + gboolean simple_record; + GtkToolbar * toolbar; + GtkActionGroup * group; + GtkAction * move_first; + GtkAction * move_previous; + GtkAction * move_next; + GtkAction * move_last; + GtkAction * add; + GtkAction * remove; + GtkAction * save; + GtkAction * undo; +}; + +struct _VnHandlerClass +{ + /* */ + GtkHButtonBoxClass parent; +}; + +GType vn_handler_get_type (); +GType vn_handler_show_flags_get_type () G_GNUC_CONST; + +GtkWidget * vn_handler_new (); +GtkWidget * vn_handler_new_with_iterator (DbIterator * iterator); +DbIterator * vn_handler_get_iterator (VnHandler * obj); +void vn_handler_set_iterator (VnHandler * obj, DbIterator * iterator); +void vn_handler_set_show_flags (VnHandler * obj, VnHandlerShowFlags show_flags); +void vn_handler_set_simple_record (VnHandler * obj, gboolean simple); + +#endif diff --git a/vn/vn-login.c b/vn/vn-login.c new file mode 100644 index 0000000..520597b --- /dev/null +++ b/vn/vn-login.c @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-login.h" + +#define LOGIN_UI _GUI_DIR"/login.glade" + +#define IS_DEFINED(string) (string && g_strcmp0 (string, "")) + +typedef struct +{ + VnLogin * obj; + gchar * user; + gchar * pass; + DbConn * conn; + GThread * thread; + gboolean connected; + GError * error; +} +ConnectData; + +void vn_login_cb_gui_logout (VnGui * gui, VnLogin * obj); +void vn_login_cb_gui_exit (VnGui * gui, VnLogin * obj); + +G_DEFINE_TYPE (VnLogin, vn_login, G_TYPE_OBJECT); + +/** + * vn_login_new: + * + * Creates a new Gui object. + * + * Return value: the created #VnLogin + **/ +VnLogin * vn_login_new (const gchar * application_id) +{ + return g_object_new (VN_TYPE_LOGIN, "application-id", application_id, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +/* + * Frees the #ConnectData struct + */ +void connect_data_free (ConnectData * connect_data) +{ + if (connect_data->error) + g_error_free (connect_data->error); + + g_free (connect_data->user); + g_free (connect_data->pass); + g_object_unref (connect_data->conn); + g_object_unref (connect_data->obj); + g_thread_unref (connect_data->thread); + g_free (connect_data); +} + +/* + * Shows the login window to the user. + */ +static void vn_login_show (VnLogin * obj) +{ + gboolean autologin = FALSE; + + if (!obj->window) + { + gchar * user; + gchar * pass; + VnBuilder * builder = vn_builder_new (); + + if (gtk_builder_add_from_file (GTK_BUILDER (builder), LOGIN_UI, NULL)) + { + obj->window = vn_builder_get (builder, "window"); + obj->user = vn_builder_get (builder, "user"); + obj->pass = vn_builder_get (builder, "password"); + obj->remember = vn_builder_get (builder, "remember"); + obj->connect = vn_builder_get (builder, "connect"); + obj->settings_button = vn_builder_get (builder, "settings"); + obj->spinner = vn_builder_get (builder, "spinner"); + obj->settings_dialog = vn_builder_get (builder, "settings-dialog"); + obj->plugin = vn_builder_get (builder, "plugin"); + obj->host = vn_builder_get (builder, "host"); + obj->schema = vn_builder_get (builder, "schema"); + obj->ssl_ca = vn_builder_get (builder, "ssl-ca"); + gtk_builder_connect_signals (GTK_BUILDER (builder), obj); + + user = g_settings_get_string (obj->settings, "user"); + pass = g_settings_get_string (obj->settings, "pass"); + + if (user && g_strcmp0 (user, "")) + gtk_entry_set_text (obj->user, user); + + if (pass && g_strcmp0 (pass, "")) + { + gchar * aux = pass; + pass = gvn_decode (aux); + gtk_entry_set_text (obj->pass, pass); + gtk_toggle_button_set_active (obj->remember, TRUE); + g_free (aux); + autologin = TRUE; + } + + g_free (user); + g_free (pass); + } + + g_object_unref (builder); + } + + gtk_widget_show_all (GTK_WIDGET (obj->window)); + gtk_widget_grab_focus (GTK_WIDGET (obj->user)); + gtk_widget_hide (obj->spinner); + + if (autologin) + gtk_button_clicked (obj->connect); +} + +/* + * Enables/disables the #GtkSpinner at login dialog. + */ +static void vn_login_set_loading (VnLogin * obj, gboolean loading) +{ + gtk_widget_set_sensitive (GTK_WIDGET (obj->connect), !loading); + gtk_widget_set_sensitive (GTK_WIDGET (obj->settings_button), !loading); + gtk_widget_set_sensitive (GTK_WIDGET (obj->user), !loading); + gtk_widget_set_sensitive (GTK_WIDGET (obj->pass), !loading); + gtk_widget_set_sensitive (GTK_WIDGET (obj->remember), !loading); + + if (loading) + gtk_widget_show (obj->spinner); + else + gtk_widget_hide (obj->spinner); +} + +/* + * Frees the GUI object. + */ +static void vn_login_free_gui (VnLogin * obj) +{ + if (obj->gui) + { + g_object_disconnect (obj->gui + ,"any_signal", vn_login_cb_gui_exit, obj + ,"any_signal", vn_login_cb_gui_logout, obj + ,NULL + ); + g_clear_object (&obj->gui); + } +} + +/* + * Shows the settings dialog. + */ +void vn_login_cb_settings_clicked (GtkButton * button, VnLogin * obj) +{ + gchar * plugin = g_settings_get_string (obj->settings, "plugin"); + gchar * host = g_settings_get_string (obj->settings, "host"); + gchar * schema = g_settings_get_string (obj->settings, "schema"); + gchar * ssl_ca = g_settings_get_string (obj->settings, "ssl-ca"); + + gtk_entry_set_text (obj->plugin, plugin); + gtk_entry_set_text (obj->host, host); + gtk_entry_set_text (obj->schema, schema); + gtk_entry_set_text (obj->ssl_ca, ssl_ca); + gtk_dialog_run (obj->settings_dialog); + + g_free (plugin); + g_free (host); + g_free (schema); + g_free (ssl_ca); +} + +/* + * Hides the settings dialog. + */ +void vn_login_cb_settings_cancel_clicked (GtkButton * button, VnLogin * obj) +{ + gtk_widget_hide (GTK_WIDGET (obj->settings_dialog)); +} + +/* + * Hides the settings dialog when escape is pressed. + */ +void vn_login_settings_on_delete_event (GtkWidget * settings_dialog) +{ + gtk_widget_hide (settings_dialog); +} + +/* + * Applies the changes made on settings dialog. + */ +void vn_login_cb_settings_apply_clicked (GtkButton * button, VnLogin * obj) +{ + const gchar * plugin = gtk_entry_get_text (obj->plugin); + const gchar * host = gtk_entry_get_text (obj->host); + const gchar * schema = gtk_entry_get_text (obj->schema); + const gchar * ssl_ca = gtk_entry_get_text (obj->ssl_ca); + + g_settings_set_string (obj->settings, "plugin", plugin); + g_settings_set_string (obj->settings, "host", host); + g_settings_set_string (obj->settings, "schema", schema); + g_settings_set_string (obj->settings, "ssl-ca", ssl_ca); + gtk_widget_hide (GTK_WIDGET (obj->settings_dialog)); +} + +/* + * Shows the login dialog when user logout from GUI. + */ +void vn_login_cb_gui_logout (VnGui * gui, VnLogin * obj) +{ + g_settings_set_string (obj->settings, "pass", ""); + vn_login_free_gui (obj); + vn_login_show (obj); +} + +/* + * Destroys the login dialog when user exits from GUI. + */ +void vn_login_cb_gui_exit (VnGui * gui, VnLogin * obj) +{ + vn_login_free_gui (obj); + gtk_widget_destroy (GTK_WIDGET (obj->window)); +} + +/* + * Saves the login information and opens the main GUI. + */ +static gboolean vn_login_done (ConnectData * connect_data) +{ + VnLogin * obj = connect_data->obj; + + if (connect_data->connected) + { + const gchar * user; + + user = gtk_entry_get_text (obj->user); + g_settings_set_string (obj->settings, "user", user); + + if (gtk_toggle_button_get_active (obj->remember)) + { + const gchar * pass = gtk_entry_get_text (obj->pass); + gchar * aux = gvn_encode (pass); + + gtk_toggle_button_set_active (obj->remember, FALSE); + g_settings_set_string (obj->settings, "pass", aux); + g_free (aux); + } + + gtk_entry_set_text (obj->pass, ""); + gtk_widget_hide (GTK_WIDGET (obj->window)); + obj->gui = vn_gui_new (obj->app, connect_data->conn); + g_object_connect (obj->gui + ,"signal::logout", vn_login_cb_gui_logout, obj + ,"signal::exit", vn_login_cb_gui_exit, obj + ,NULL + ); + vn_gui_open (obj->gui); + } + else + { + GtkWidget * dialog; + + dialog = gtk_message_dialog_new (obj->window + ,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT + ,GTK_MESSAGE_ERROR + ,GTK_BUTTONS_OK + ,_("Login error") + ); + gtk_window_set_title (GTK_WINDOW (dialog), _("Login error")); + + if (connect_data->error) + { + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", connect_data->error->message); + + if (connect_data->error->code == DB_CONN_ERROR_BAD_LOGIN) + gtk_entry_set_text (obj->pass, ""); + } + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (GTK_WIDGET (dialog)); + vn_login_show (obj); + } + + vn_login_set_loading (obj, FALSE); + return FALSE; +} + +/* + * Thread function that tries to open the connection asynchronously. + */ +static void vn_login_thread (ConnectData * connect_data) +{ + gchar * plugin; + gchar * host; + gchar * schema; + gchar * ssl_ca; + VnLogin * obj = connect_data->obj; + + plugin = g_settings_get_string (obj->settings, "plugin"); + host = g_settings_get_string (obj->settings, "host"); + schema = g_settings_get_string (obj->settings, "schema"); + ssl_ca = g_settings_get_string (obj->settings, "ssl-ca"); + + if (IS_DEFINED (plugin) && IS_DEFINED (host) && IS_DEFINED (schema)) + { + if (db_conn_load_plugin (connect_data->conn, plugin, &connect_data->error)) + { + if (IS_DEFINED (ssl_ca)) + db_conn_set_ssl (connect_data->conn, ssl_ca); + + connect_data->connected = db_conn_open (connect_data->conn + ,host + ,schema + ,connect_data->user + ,connect_data->pass + ,&connect_data->error + ); + } + } + else + connect_data->error = g_error_new ( + VN_LOGIN_LOG_DOMAIN + ,VN_LOGIN_ERR_BAD_SETTINGS + ,_("Bad connection settings, please check it.") + ); + + g_free (plugin); + g_free (host); + g_free (schema); + g_free (ssl_ca); + + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) vn_login_done, connect_data, (GDestroyNotify) connect_data_free); +} + +/* + * Opens the connection. + */ +void vn_login_cb_connect_clicked (GtkButton * button, VnLogin * obj) +{ + ConnectData * connect_data; + + vn_login_set_loading (obj, TRUE); + + connect_data = g_new (ConnectData, 1); + connect_data->obj = g_object_ref (obj); + connect_data->user = g_strdup (gtk_entry_get_text (obj->user)); + connect_data->pass = g_strdup (gtk_entry_get_text (obj->pass)); + connect_data->conn = db_conn_new (); + connect_data->connected = FALSE; + connect_data->error = NULL; + connect_data->thread = g_thread_new ("vn-login", + (GThreadFunc) vn_login_thread, connect_data); +} + +/* + * Closes the application when login window is destroyed. + */ +void vn_login_cb_destroyed (GtkWidget * window, VnLogin * obj) +{ + obj->window = NULL; + gtk_main_quit (); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_login_run: + * @obj: a #VnLogin + * + * Starts the main gui application. + **/ +void vn_login_run (VnLogin * obj) +{ + g_return_if_fail (VN_IS_LOGIN (obj)); + + if (g_application_register (G_APPLICATION (obj->app), NULL, NULL)) + { + g_object_set (gtk_settings_get_default (), + "gtk-button-images", TRUE, NULL + ); + vn_login_show (obj); + gtk_main (); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_APPLICATION_ID = 1 +}; + +static void vn_login_set_property (VnLogin * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_APPLICATION_ID: + { + const gchar * id = g_value_get_string (value); + obj->app = gtk_application_new (id, G_APPLICATION_FLAGS_NONE); + obj->settings = g_settings_new (id); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_login_get_property (VnLogin * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_login_init (VnLogin * obj) +{ + obj->gui = NULL; + obj->window = NULL; + obj->settings = NULL; + obj->app = NULL; +} + +static void vn_login_finalize (VnLogin * obj) +{ + vn_login_free_gui (obj); + g_clear_object (&obj->settings); + g_clear_object (&obj->app); + G_OBJECT_CLASS (vn_login_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_login_class_init (VnLoginClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) vn_login_finalize; + klass->set_property = (GObjectSetPropertyFunc) vn_login_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_login_get_property; + + g_object_class_install_property (klass, PROP_APPLICATION_ID, + g_param_spec_string ("application-id" + ,_("Application id") + ,_("The application identifier") + ,"app.default" + ,G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/vn/vn-login.h b/vn/vn-login.h new file mode 100644 index 0000000..a40d813 --- /dev/null +++ b/vn/vn-login.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_LOGIN_H +#define VN_LOGIN_H + +#include +#include + +#define VN_LOGIN_LOG_DOMAIN (g_quark_from_string ("VnLogin")) + +#define VN_TYPE_LOGIN (vn_login_get_type ()) +#define VN_LOGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_LOGIN, VnLogin)) +#define VN_IS_LOGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_LOGIN)) +#define VN_LOGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_LOGIN, VnLoginClass)) +#define VN_IS_LOGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_LOGIN)) +#define VN_LOGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_LOGIN, VnLoginClass)) + +typedef struct _VnLogin VnLogin; +typedef struct _VnLoginClass VnLoginClass; + +#include "vn-gui.h" + +struct _VnLogin +{ + GObject parent; + + GtkApplication * app; + VnGui * gui; + GtkWindow * window; + GtkEntry * user; + GtkEntry * pass; + GtkToggleButton * remember; + GtkButton * connect; + GtkButton * settings_button; + GtkWidget * spinner; + + GtkDialog * settings_dialog; + GtkEntry * plugin; + GtkEntry * host; + GtkEntry * schema; + GtkEntry * ssl_ca; + GSettings * settings; +}; + +struct _VnLoginClass +{ + GObjectClass parent; +}; + +typedef enum +{ + VN_LOGIN_ERR_BAD_SETTINGS = 1 +} +VnLoginError; + +GType vn_login_get_type (); +VnLogin * vn_login_new (); +void vn_login_run (VnLogin * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-mod.c b/vn/vn-mod.c new file mode 100644 index 0000000..fe94bd2 --- /dev/null +++ b/vn/vn-mod.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-mod.h" + +struct _VnModPrivate +{ + gchar * name; + gchar * data_dir; + gchar * text_domain; + gchar * ui; + GModule * module; + GtkActionGroup * actions; +}; + +G_DEFINE_TYPE (VnMod, vn_mod, G_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_mod_activate: + * @obj: the #VnMod + * + * Activates the module. + * Virtual: activate + **/ +void vn_mod_activate (VnMod * obj) +{ + g_return_if_fail (VN_IS_MOD (obj)); + + VN_MOD_GET_CLASS (obj)->activate (obj); +} + +const gchar * vn_mod_get_name (VnMod * obj) +{ + g_return_val_if_fail (VN_IS_MOD (obj), NULL); + + return obj->priv->name; +} + +const gchar * vn_mod_get_text_domain (VnMod * obj) +{ + g_return_val_if_fail (VN_IS_MOD (obj), NULL); + + return obj->priv->text_domain; +} + +const gchar * vn_mod_get_data_dir (VnMod * obj) +{ + g_return_val_if_fail (VN_IS_MOD (obj), NULL); + + return obj->priv->data_dir; +} + +const gchar * vn_mod_get_ui (VnMod * obj) +{ + g_return_val_if_fail (VN_IS_MOD (obj), NULL); + + return obj->priv->ui; +} + +void vn_mod_set_ui (VnMod * obj, gchar * ui) +{ + g_return_if_fail (VN_IS_MOD (obj)); + + g_free (obj->priv->ui); + obj->priv->ui = ui; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_NAME = 1 + ,PROP_DATA_DIR + ,PROP_MODULE + ,PROP_TEXT_DOMAIN +}; + +static void vn_mod_set_property (VnMod * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + obj->priv->name = g_value_dup_string (value); + obj->priv->text_domain = g_strdup_printf ("hedera-%s", obj->priv->name); + bindtextdomain (obj->priv->text_domain, _HEDERA_LOCALE_DIR); + break; + case PROP_DATA_DIR: + obj->priv->data_dir = g_value_dup_string (value); + break; + case PROP_MODULE: + obj->priv->module = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_mod_get_property (VnMod * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_value_set_string (value, obj->priv->name); + break; + case PROP_DATA_DIR: + g_value_set_string (value, obj->priv->data_dir); + break; + case PROP_MODULE: + g_value_set_pointer (value, obj->priv->module); + break; + case PROP_TEXT_DOMAIN: + g_value_set_string (value, obj->priv->text_domain); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_mod_init (VnMod * obj) +{ + obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, VN_TYPE_MOD, VnModPrivate); + obj->priv->name = NULL; + obj->priv->data_dir = NULL; + obj->priv->module = NULL; + obj->priv->text_domain = NULL; + obj->priv->ui = NULL; + obj->priv->actions = NULL; +} + +static void vn_mod_finalize (VnMod * obj) +{ + g_free (obj->priv->name); + g_free (obj->priv->data_dir); + g_free (obj->priv->text_domain); + g_free (obj->priv->ui); + g_object_unref (obj->priv->actions); + G_OBJECT_CLASS (vn_mod_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_mod_class_init (VnModClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) vn_mod_finalize; + klass->set_property = (GObjectSetPropertyFunc) vn_mod_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_mod_get_property; + g_type_class_add_private (klass, sizeof (VnModPrivate)); + + g_object_class_install_property (klass, PROP_NAME, + g_param_spec_string ("name" + ,"Name" + ,"The module name" + ,NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_DATA_DIR, + g_param_spec_string ("data-dir" + ,"Data directory" + ,"The directory in which the data of the module is allocated" + ,NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_MODULE, + g_param_spec_pointer ("module" + ,"Module" + ,"The GModule" + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE + )); + g_object_class_install_property (klass, PROP_TEXT_DOMAIN, + g_param_spec_string ("text-domain" + ,"Text domain" + ,"The domain in wich the module is" + ,NULL + ,G_PARAM_READABLE + )); +} diff --git a/vn/vn-mod.h b/vn/vn-mod.h new file mode 100644 index 0000000..e023d96 --- /dev/null +++ b/vn/vn-mod.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_MOD_H +#define VN_MOD_H + +#include "vn-builder.h" + +#define VN_TYPE_MOD (vn_mod_get_type ()) +#define VN_MOD(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_MOD, VnMod)) +#define VN_IS_MOD(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_MOD)) +#define VN_MOD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_MOD, VnModClass)) +#define VN_IS_MOD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_MOD)) +#define VN_MOD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_MOD, VnModClass)) + +typedef struct _VnMod VnMod; +typedef struct _VnModClass VnModClass; +typedef struct _VnModPrivate VnModPrivate; + +#include "vn-gui.h" + +typedef GType (* VnModGetTypeFunc) (); +typedef void (* VnModActivateFunc) (VnMod * obj); + +struct _VnMod +{ + GObject parent; + VnModPrivate * priv; +}; + + +struct _VnModClass +{ + GObjectClass parent; + void (* activate) (VnMod * obj); +}; + +GType vn_mod_get_type (); +void vn_mod_activate (VnMod * obj); +const gchar * vn_mod_get_name (VnMod * obj); +const gchar * vn_mod_get_text_domain (VnMod * obj); +const gchar * vn_mod_get_data_dir (VnMod * obj); +const gchar * vn_mod_get_ui (VnMod * obj); +void vn_mod_set_ui (VnMod * obj, gchar * ui); + +#endif \ No newline at end of file diff --git a/vn/vn-model.c b/vn/vn-model.c new file mode 100644 index 0000000..ff48c1f --- /dev/null +++ b/vn/vn-model.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#include "vn-model.h" + +/** + * vn_model_new: + * @model: a #DbModel + * + * Creates a new #VnModel with @model as the data model. + * + * Return value: (transfer full): a #VnModel. + **/ +VnModel * vn_model_new (DbModel * model) +{ + return g_object_new (VN_TYPE_MODEL, "model", model, NULL); +} + +/** + * vn_gtk_tree_iter_from_db_iter: + * @dest_iter: the #GtkTreeIter to set using the data on @src_iter + * @src_iter: a valid #DbIter + * + * Sets a #GtkTreeIter with the data of a #DbIter. This function is mostly used + * internally. + **/ +void gtk_tree_iter_from_db_iter (GtkTreeIter * dest_iter, const DbIter * src_iter) +{ + dest_iter->stamp = src_iter->stamp; + dest_iter->user_data = src_iter->data; +} + +/** + * vn_gtk_tree_iter_to_db_iter: + * @src_iter: a valid #GtkTreeIter + * @dest_iter: the #DbIter to set using the data on @src_iter + * + * Sets a with #DbIter the data of a #GtkTreeIter. This function is mostly used + * internally. + **/ +void gtk_tree_iter_to_db_iter (const GtkTreeIter * src_iter, DbIter * dest_iter) +{ + dest_iter->stamp = src_iter->stamp; + dest_iter->data = src_iter->user_data; +} + +/** + * vn_model_iter_is_valid: + * @iter: a #GtkTreeIter + * @model: a #GtkTreeModel + * + * Checks if @iter is a valid #GtkTreeIter pointing inside #GtkTreeModel. + **/ +gboolean vn_model_iter_is_valid (GtkTreeIter * iter, GtkTreeModel * model) +{ + DbIter db_iter; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE); + + gtk_tree_iter_to_db_iter (iter, &db_iter); + return db_model_iter_is_valid (&db_iter, VN_MODEL (model)->model); +} + +static gint vn_model_get_nrows (GtkTreeModel * obj) +{ + return db_model_get_nrows (VN_MODEL (obj)->model); +} + +//++++ Broadcast of GtkTreeModel and GtkTreeSortable signals: + +static void vn_model_on_line_updated (DbModel * model, DbIter * db_iter, GtkTreeModel * obj) +{ + GtkTreeIter iter; + gtk_tree_iter_from_db_iter (&iter, db_iter); + + GtkTreePath * path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, db_model_get_path (model, db_iter)); + + gtk_tree_model_row_changed (obj, path, &iter); + + gtk_tree_path_free (path); +} + +static void vn_model_on_line_inserted (DbModel * model, DbIter * db_iter, GtkTreeModel * obj) +{ + GtkTreeIter iter; + gtk_tree_iter_from_db_iter (&iter, db_iter); + + GtkTreePath * path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, + db_model_get_path (model, db_iter)); + + gtk_tree_model_row_inserted (obj, path, &iter); + + gtk_tree_path_free (path); +} + +static void vn_model_on_line_deleted (DbModel * model, gint position, GtkTreeModel * obj) +{ + GtkTreePath * path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, position); + + if (gtk_tree_path_get_indices (path)) + gtk_tree_model_row_deleted (obj, path); + + gtk_tree_path_free (path); +} + +static void vn_model_on_lines_reordered (DbModel * model, guint col, gint * new_order, GtkTreeModel * obj) +{ + GtkTreePath * path = gtk_tree_path_new (); + gtk_tree_model_rows_reordered (obj, path, NULL, new_order); + gtk_tree_path_free (path); +} + +static void vn_model_on_sort_changed (DbModel * model, GtkTreeSortable * obj) +{ + g_signal_emit_by_name (obj, "sort-column-changed"); +} + +//++++ Implementation of GtkTreeModel methods (using DbModel methods) + +static GtkTreeModelFlags vn_model_get_flags (GtkTreeModel * obj) +{ + return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY; +} + +static gint vn_model_get_ncols (GtkTreeModel * obj) +{ + return db_model_get_ncols (VN_MODEL (obj)->model); +} + +static GType vn_model_get_column_type (GtkTreeModel * obj, gint index_) +{ + return db_model_get_column_type (VN_MODEL (obj)->model, index_); +} + +static gboolean vn_model_get_iter (GtkTreeModel * obj, GtkTreeIter * iter, + GtkTreePath * path) +{ + DbIter db_iter; + gint p = gtk_tree_path_get_indices (path)[0]; + + gboolean ret = db_model_get_iter (VN_MODEL (obj)->model, &db_iter, p); + + if (ret) + gtk_tree_iter_from_db_iter (iter, &db_iter); + + return ret; +} + +static GtkTreePath * vn_model_get_path (GtkTreeModel * obj, GtkTreeIter * iter) +{ + GtkTreePath * path; + DbIter db_iter; + + gtk_tree_iter_to_db_iter (iter, &db_iter); + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, + db_model_get_path (VN_MODEL (obj)->model, &db_iter)); + + return path; +} + +static void vn_model_get_value (GtkTreeModel * obj, GtkTreeIter * iter, + gint column, GValue * value) +{ + const GValue * v; + DbIter db_iter; + + gtk_tree_iter_to_db_iter (iter, &db_iter); + + v = db_model_get_value (VN_MODEL (obj)->model, &db_iter, column, NULL); + + if (v) + { + g_value_init (value, G_VALUE_TYPE (v)); + g_value_copy (v, value); + } + else + g_value_init (value, GVN_TYPE_NULL); +} + +static gboolean vn_model_iter_next (GtkTreeModel * obj, GtkTreeIter * iter) +{ + DbIter db_iter; + gtk_tree_iter_to_db_iter (iter, &db_iter); + + gboolean ret_val = db_model_iter_next (VN_MODEL (obj)->model, &db_iter); + + iter->user_data = db_iter.data; + + return ret_val; +} + +static gboolean vn_model_iter_previous (GtkTreeModel * obj, GtkTreeIter * iter) +{ + DbIter db_iter; + gtk_tree_iter_to_db_iter (iter, &db_iter); + + gboolean ret_val = db_model_iter_prev (VN_MODEL (obj)->model, &db_iter); + + iter->user_data = db_iter.data; + + return ret_val; +} + +static gboolean vn_model_iter_children (GtkTreeModel * obj, GtkTreeIter * iter, + GtkTreeIter * parent) +{ + if (parent == NULL) + { + DbIter db_iter; + + gboolean ret_val = db_model_get_iter_first (VN_MODEL (obj)->model + ,&db_iter); + + if (ret_val) + gtk_tree_iter_from_db_iter (iter, &db_iter); + + return ret_val; + } + + return FALSE; +} + +static gboolean vn_model_iter_has_child (GtkTreeModel * obj, GtkTreeIter * iter) +{ + return FALSE; +} + +static gint vn_model_iter_n_children (GtkTreeModel * obj, GtkTreeIter * iter) +{ + if (iter == NULL) + return vn_model_get_nrows (obj); + + return 0; +} + +static gboolean vn_model_iter_nth_child (GtkTreeModel * obj, GtkTreeIter * iter + ,GtkTreeIter * parent, gint n) +{ + if (parent == NULL) + { + DbIter db_iter; + gboolean ret = db_model_get_iter (VN_MODEL (obj)->model, &db_iter, n); + + if (ret) + gtk_tree_iter_from_db_iter (iter, &db_iter); + } + + return FALSE; +} + +static gboolean vn_model_iter_parent (GtkTreeModel * obj, GtkTreeIter * iter, + GtkTreeIter * child) +{ + return FALSE; +} + +//++++ Implementation of GtkTreeSortable methods (using DbModel methods) + +static gboolean vn_model_get_sort_column_id (GtkTreeSortable * obj, + gint * sort_column_id, + GtkSortType * order) +{ + return db_model_get_sort_column_id (VN_MODEL (obj)->model + ,sort_column_id, (DbSortType *) order); +} + +static void vn_model_set_sort_column_id (GtkTreeSortable * obj, gint sort_column_id, + GtkSortType order) +{ + DbSortType db_order; + + if (order == GTK_SORT_ASCENDING) + db_order = DB_SORT_ASCENDING; + else + db_order = DB_SORT_DESCENDING; + + db_model_set_sort_column_id (VN_MODEL (obj)->model, sort_column_id, db_order); +} + +static void vn_model_set_sort_func (GtkTreeSortable *sortable, gint sort_column_id, + GtkTreeIterCompareFunc sort_func, gpointer user_data, GDestroyNotify destroy) +{ + g_log (g_quark_to_string (VN_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, _("Function vn_model_set_sort_func not implemented")); +} + +static void vn_model_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc sort_func, gpointer user_data, GDestroyNotify destroy) +{ + g_log (g_quark_to_string (VN_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, _("Function vn_model_set_default_sort_func not implemented")); +} + +static gboolean vn_model_has_default_sort_func (GtkTreeSortable * obj) +{ + return TRUE; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +typedef enum +{ + PROP_MODEL = 1 +} +VnModelProp; + +static void vn_model_set_property (VnModel * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_MODEL: + obj->model = g_value_get_object (value); + g_object_ref_sink (obj->model); + g_object_connect (obj->model + ,"signal-after::line-updated", vn_model_on_line_updated, obj + ,"signal::line-inserted", vn_model_on_line_inserted, obj + ,"signal-after::line-deleted", vn_model_on_line_deleted, obj + ,"signal::lines-reordered", vn_model_on_lines_reordered, obj + ,"signal::sort-changed", vn_model_on_sort_changed, obj, NULL); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void vn_model_get_property (VnModel * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_MODEL: + g_value_set_object (value, obj->model); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_model_finalize (VnModel * obj) +{ + GObjectClass * parent; + parent = g_type_class_peek_parent (VN_MODEL_GET_CLASS (obj)); + + g_object_disconnect (obj->model + ,"any_signal", vn_model_on_line_updated, obj + ,"any_signal", vn_model_on_line_inserted, obj + ,"any_signal", vn_model_on_line_deleted, obj + ,"any_signal", vn_model_on_lines_reordered, obj + ,"any_signal", vn_model_on_sort_changed, obj, NULL); + + g_object_unref (obj->model); + + parent->finalize (G_OBJECT (obj)); +} + +static void vn_model_class_init (VnModelClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->set_property = (GObjectSetPropertyFunc) vn_model_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_model_get_property; + klass->finalize = (GObjectFinalizeFunc) vn_model_finalize; + + g_object_class_install_property (klass, PROP_MODEL, + g_param_spec_object ("model" + ,"Model" + ,"The #DbModel with the data used by the #VnModel" + ,DB_TYPE_MODEL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); +} + +// tienen que implementarse con métodos heredados de db-model +static void vn_model_tree_model_interface_init (GtkTreeModelIface * iface) +{ + iface->get_flags = vn_model_get_flags; + iface->get_n_columns = vn_model_get_ncols; + iface->get_column_type = vn_model_get_column_type; + iface->get_iter = vn_model_get_iter; + iface->get_path = vn_model_get_path; + iface->get_value = vn_model_get_value; + iface->iter_next = vn_model_iter_next; + iface->iter_previous = vn_model_iter_previous; + + iface->iter_children = vn_model_iter_children; + iface->iter_has_child = vn_model_iter_has_child; + iface->iter_n_children = vn_model_iter_n_children; + iface->iter_nth_child = vn_model_iter_nth_child; + iface->iter_parent = vn_model_iter_parent; +} + +static void vn_model_tree_sortable_interface_init (GtkTreeSortableIface * iface) +{ + iface->get_sort_column_id = vn_model_get_sort_column_id; + iface->set_sort_column_id = vn_model_set_sort_column_id; + iface->set_sort_func = vn_model_set_sort_func; + iface->set_default_sort_func = vn_model_set_default_sort_func; + iface->has_default_sort_func = vn_model_has_default_sort_func; + +} + +static void vn_model_init (VnModel * obj) +{ + obj->model = NULL; +} + +G_DEFINE_TYPE_WITH_CODE (VnModel, vn_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + vn_model_tree_model_interface_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, + vn_model_tree_sortable_interface_init) +); diff --git a/vn/vn-model.h b/vn/vn-model.h new file mode 100644 index 0000000..1783fb8 --- /dev/null +++ b/vn/vn-model.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_MODEL_H +#define VN_MODEL_H + +#include +#include +#include + +#define VN_TYPE_MODEL (vn_model_get_type()) +#define VN_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VN_TYPE_MODEL, VnModel)) +#define VN_IS_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VN_TYPE_MODEL)) +#define VN_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VN_TYPE_MODEL, VnModelClass)) +#define VN_IS_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VN_TYPE_MODEL)) +#define VN_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VN_TYPE_MODEL, VnModelClass)) + +#define VN_MODEL_LOG_DOMAIN (g_quark_from_string ("VnModel")) + +#define gtk_tree_iter_from_db_iter(d,s) vn_gtk_tree_iter_from_db_iter(d,s) +#define gtk_tree_iter_to_db_iter(s,d) vn_gtk_tree_iter_to_db_iter(s,d) + +typedef struct _VnModel VnModel; +typedef struct _VnModelClass VnModelClass; + +struct _VnModel +{ + GObject parent; + DbModel * model; +}; + +struct _VnModelClass +{ + /* */ + GObjectClass parent; +}; + +GType vn_model_get_type (); +VnModel * vn_model_new (DbModel * model); +gboolean vn_model_iter_is_valid (GtkTreeIter * iter, GtkTreeModel * model); +void vn_gtk_tree_iter_from_db_iter (GtkTreeIter * dest_iter + ,const DbIter * src_iter); +void vn_gtk_tree_iter_to_db_iter (const GtkTreeIter * src_iter + ,DbIter * dest_iter); + +#endif \ No newline at end of file diff --git a/vn/vn.h b/vn/vn.h new file mode 100644 index 0000000..0eb78e1 --- /dev/null +++ b/vn/vn.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * 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 . + */ + +#ifndef VN_H +#define VN_H + +#include +#include +#include "vn-model.h" +#include "vn-handler.h" +#include "vn-grid.h" +#include "vn-builder.h" +#include "vn-login.h" +#include "vn-gui.h" +#include "vn-mod.h" +#include "vn-form.h" +#include "vn-batch.h" +#include "vn-field.h" +#include "vn-column.h" +#include "field/field.h" +#include "column/column.h" + +#endif diff --git a/vn/vn.pc.in b/vn/vn.pc.in new file mode 100644 index 0000000..3b93c7b --- /dev/null +++ b/vn/vn.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/hedera +includedir=@includedir@/hedera + +Name: Vn +Description: Graphical UI Module for Hedera Library +Version: @VERSION@ +Requires: db sql gvn gtk+-3.0 +Libs: -L${libdir} -lvn -lvnfield -lvncolumn +Cflags: -I${includedir}/vn