# HG changeset patch # User Jacob Dawid # Date 1302540249 -7200 # Node ID 4a718f51baa3ed45d86766dd614c24fdd9af479d # Parent d5566981b3acae9cef5918ed2f295b11473a628a Added qcodeedit source. diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/GPL.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/GPL.txt Mon Apr 11 18:44:09 2011 +0200 @@ -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 -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/README.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/README.txt Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,175 @@ + +---------------------------------------------------------------------------------- +---------------------------------------------------------------------------------- + + QCodeEdit, copyright (c) 2006-2009 Luc Bruant aka fullmetalcoder, + is a free and open source software + + QCodeEdit sources are part of Edyuk and are thus available under GNU General Public + License version 3 (GPL v3), as published by the Free Software Foundation. + +---------------------------------------------------------------------------------- +---------------------------------------------------------------------------------- + +QCodeEdit is a project aiming at the creation of a flexible and powerful text +editing framework for Qt4. It has started as a sub-project of Edyuk, copyright (c) +Luc Bruant, a free and open source IDE. As it proved successful it is also +released as a standalone library to make it easy for everyone to use such +a framework within other apps without having to reinvent the wheel. + +Writing closed source applications with QCodeEdit is possible after buying a +proper license. For more informations about pricing and licensing conditions +please contact the author directly +Note that you will still need a Qt commercial license for that or, starting with +Qt 4.5, a LGPL one. + +QCodeEdit depends on Qt 4.3 or newer, copyright (c) Nokia Corporation, which can +be downloaded at : ftp://ftp.trolltech.com/qt/sources + +More information about Qt and Qt Software (formerly Trolltech) : +http://www.qtsoftware.com + +Hoping you'll like it. + + +The author would like to thank all the people who contributed to QCodeEdit in various ways : + + * testing, reporting bugs, making suggestions : + Jeremy Sonander, from Saros Inc + Phil Martinot + Benito van der Zander + Ulrich Van Den Hekke + Boris Barbulovski + + * contributing patches : + Jerome Vizcaino + Benito van der Zander + Ulrich Van Den Hekke + Boris Barbulovski + + * funding (by buying commercial licenses) : + Saros Inc + Movimento SE + + * spreading the word : + Johan Thelin (posted a blog post that appeared on PlanetKDE) + +(If you have been forgotten send an email to the author and the list will be updated) + + +IMPORTANT : If you encounter any sort of troubles trying to build or run QCodeEdit +please send a bug report with as many details as possible (including but not limited +to : OS, Qt version, compiler, QCodeEdit version, compile log, backtrace, ...) to the +team using either of these : + * staff@qcodeedit.edyuk.org + * Edyuk task tracker on Sf.net : http://sf.net/projects/edyuk + * Edyuk webissues server (needs a WebIssues client) : http://edyuk.org/webissues/ + - login : anonymous + - password : anonymous + * QtCentre dedicated thread in Qt Software section : http://www.qtcentre.org/forum + + +In case you don't understand, blocks of text enclosed between lines of minus signs are +shell commands. The dollar signs just stand for command prompt. + + + >>> Building : + +------------------------------------------------------------------------------------------ +$ qmake +$ make +------------------------------------------------------------------------------------------ + +This will build the library and the example. You may want to alter the build mode to force +either debug or release. If so just pass the mode you want to make, e.g. : + +------------------------------------------------------------------------------------------ +$ make release +------------------------------------------------------------------------------------------ +or +------------------------------------------------------------------------------------------ +$ make debug +------------------------------------------------------------------------------------------ + +Finally, with admins rights/root privilegdes, you can install QCodeEdit so as to be able +to use it simply : + +------------------------------------------------------------------------------------------ +$ qmake +$ make install +------------------------------------------------------------------------------------------ + +NB : the extra "qmake" is NEEDED to ensure that binaries will be located and copied properly. +NB 2 : Only the "make install" command requires root priviledges, "qmake" can and should +always be run as normal user. +NB 3 : Apart from libs and headers, QCodeEdit also provides a .prf file which makes it +easier to use the lib in another project. Under non-Unix platforms it is recommended to +copy the files manually (and possibly edit them to fit your needs). + + + >>> Using within an app : + +To have one of your app building with QCodeEdit just add the following line to your project +file (this won't work if you have not installed QCodeEdit as described above....) : + +CONFIG += qcodeedit + +If you did not install QCodeEdit as described above you will have to either update the file +qcodeedit.prf or inspect it to determine what project variables need to be adjusted and how. + +Then, add proper headers in your sources and start coding. :D + + + >>> Testing : + +A very basic example is provided which open a list of files passed as parameters +and try to highlight them according to their file extension. Only a few language +definitions are provided : + * C++ (with Doxygen support) + * PHP + * XML + * Doxygen alone (for .dox files) + * QMake project files + * Python + * QtScript/JavaScript + * C# (WIP) + * Lua (WIP) + * LaTex (WIP) + * BibTex (WIP) + +If you write a new one for your own use (or modify an existing one to suit you needs) +please consider contributing it back to the project. + +------------------------------------------------------------------------------------------ +$ example/example [file] +------------------------------------------------------------------------------------------ + +Note : on Unix system it is recommended to use the script due to the need of setting +the LD_LIBRARY_PATH variable properly : + +------------------------------------------------------------------------------------------ +$ ./example.sh [file] +------------------------------------------------------------------------------------------ + +NB : [file] stands for a filename. If omitted a minimal string will be loaded and +considered as C++ source code + + + >>> Generating documentation [needs Doxygen : http://www.doxygen.org] : + +------------------------------------------------------------------------------------------ +$ doxygen +------------------------------------------------------------------------------------------ + +NB : + * This will create the documentation in the doc folder. Just open doc/html/index.html + + + >>> Fetching bleeding edge sources [needs Subversion : http://subversion.tigris.org] : + +------------------------------------------------------------------------------------------ +$ svn co http://edyuk.svn.sf.net/svnroot/edyuk/trunk/3rdparty/qcodeedit2 +------------------------------------------------------------------------------------------ + +NB : Using a graphical client this command extends to a "checkout" action using the above +repository URL. diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocument.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocument.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,6617 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2008 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qdocument.h" + +/* + Document model : + + Goals : + * KISS : Keep It Simple Stupid + * FAST : ... + * LIGHTWEIGHT : reduce memory usage + * FLEXIBLE : allow punctual bidi through QTextLayout + + Implementation : + QDocument + QDocumentPrivate + + QDocumentLine and QDocumentLineHandle => equivalent of QTextBlock + QDocumentCursor and QDocumentCursorHandle => equivalent of QTextCursor + + Note : + The KISS principle has been kinda mistreated in the private API due to + the addition of some complex features which where not planned to be + supported when defining the goals (e.g line wrapping, variable width + fonts, ...). Such a compromission is affordable but should be avoided + whenever possible in the future. And of course the public API should + never suffer from such a thing. +*/ + +/*! + \ingroup document + @{ +*/ + +/*! + \class QDocument + + \brief A class storing a document + + QCE uses an architecture very similar to that of QTextEdit/QTextDocument + which closely ressembles model/view. The document holds all the textual + and formatting data. It offers some (mostly indirect) ways of modifying + its content and is usable without any GUI. + + In QCE, a document is merely a list of QDocumentLine objects on which some + extra formatting can be applied and which can be wrapped/hidden in various + ways. + + The document model has been designed with three goals in mind : +
    +
  • performance +
  • flexibility +
  • low memory usage +
+ + QDocument supports Bidi by using QTextLayout on lines that require it and + prefers custom rendering in other cases to achieve the above goals. + + All the actual text editing is done through QDocumentCursor objects. + + \see QDocumentLine + \see QDocumentCursor +*/ + +#include "qdocument_p.h" +#include "qdocumentcommand.h" + +#include "qformat.h" +#include "qformatscheme.h" +#include "qlanguagedefinition.h" +#include "qlinemarksinfocenter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int m_spaceSignOffset = 2; + +static QPoint m_spaceSign[] = { + QPoint(2, -1), + QPoint(2, 0), + QPoint(3, 0) +}; + +inline static bool isWord(QChar c) +{ return c.isLetterOrNumber() || (c == QLatin1Char('_')); } + +int QDocument::screenLength(const QChar *d, int l, int tabStop) +{ + if ( tabStop == 1 ) + return l; + + int idx, column = idx = 0; + + while ( idx < l ) + { + QChar c = d[idx]; + + if ( c == QLatin1Char('\t') ) + { + int taboffset = tabStop - (column % tabStop); + column += taboffset; + } else { + ++column; + } + + ++idx; + } + + //qDebug("%s : %i", qPrintable(QString(d, l)), column); + + return column; +} + +QString QDocument::screenable(const QChar *d, int l, int tabStop) +{ + if ( tabStop == 1 ) + return QString(d, l); + + QString fragment; + int idx, column = idx = 0; + + while ( idx < l ) + { + QChar c = d[idx]; + + if ( c == QLatin1Char('\t') ) + { + int taboffset = tabStop - (column % tabStop); + + fragment += QString(taboffset, QLatin1Char(' ')); + column += taboffset; + } else { + fragment += c; + ++column; + } + + ++idx; + } + + return fragment; +} + +struct InitStruct +{ + InitStruct() + { + qRegisterMetaType("QDocumentIterator"); + qRegisterMetaType("QDocumentConstIterator"); + } +}; + +static InitStruct init_inst; + +/*! + \brief ctor +*/ +QDocument::QDocument(QObject *p) + : QObject(p), m_impl(new QDocumentPrivate(this)) +{ + if ( !QDocumentPrivate::m_font ) + { + // must not happen if config dialog plugged in... + setFont(QFont("Monospace", 10)); + } + + setText(QString()); + setLineEnding(QDocument::Conservative); + + connect(&(m_impl->m_commands) , SIGNAL( cleanChanged(bool) ), + this , SIGNAL( cleanChanged(bool) ) ); + + connect(&(m_impl->m_commands) , SIGNAL( canUndoChanged(bool) ), + this , SIGNAL( undoAvailable(bool) ) ); + + connect(&(m_impl->m_commands) , SIGNAL( canRedoChanged(bool) ), + this , SIGNAL( redoAvailable(bool) ) ); + + connect(this , + SIGNAL( lineDeleted(QDocumentLineHandle*) ), + QLineMarksInfoCenter::instance(), + SLOT ( lineDeleted(QDocumentLineHandle*) ) ); + +} + +/*! + \brief dtor +*/ +QDocument::~QDocument() +{ + delete m_impl; +} + +/*! + \brief Clear the content of the document +*/ +void QDocument::clear() +{ + setText(QString()); +} + +/*! + \return whether there commands to undo on the command stack +*/ +bool QDocument::canUndo() const +{ + return m_impl ? m_impl->m_commands.canUndo() : false; +} + +/*! + \return whether there are commands to redo on the command stack +*/ +bool QDocument::canRedo() const +{ + return m_impl ? m_impl->m_commands.canRedo() : false; +} + +/*! + \brief Undo the last editing operation +*/ +void QDocument::undo() +{ + if ( m_impl ) + { + m_impl->m_commands.undo(); + m_impl->m_lastModified = QDateTime::currentDateTime(); + } +} + +/*! + \brief Redo the last undone editing operation +*/ +void QDocument::redo() +{ + if ( m_impl ) + { + m_impl->m_commands.redo(); + m_impl->m_lastModified = QDateTime::currentDateTime(); + } + +} + +/*! + \return The content of the document + \param mode extra processing to perform on text +*/ +QString QDocument::text(int mode) const +{ + QString s; + + if ( !m_impl || m_impl->m_lines.isEmpty() ) + return s; + + int line = 0; + int prevIndent = 0, curIndent = 0, nextIndent = m_impl->m_lines.at(0)->nextNonSpaceChar(0); + + if ( nextIndent < 0 ) + nextIndent = 0; + + foreach ( QDocumentLineHandle *l, m_impl->m_lines ) + { + prevIndent = curIndent; + curIndent = nextIndent; + nextIndent = ++line < m_impl->m_lines.count() ? m_impl->m_lines.at(line)->nextNonSpaceChar(0) : 0; + + if ( nextIndent < 0 ) + nextIndent = 0; + + QString buf = l->text(); + int avgIndent = qMax(prevIndent, nextIndent); + + if ( (mode & RestoreTrailingIndent) && buf.isEmpty() && avgIndent ) + { + buf = QString(avgIndent, '\t'); + } else if ( mode & RemoveTrailingWS ) { + + int len = 0, idx = buf.length(); + + while ( --idx >= 0 ) + { + if ( !buf.at(idx).isSpace() ) + break; + + ++len; + } + + ++idx; + + if ( len && (idx || !(mode & PreserveIndent)) ) + buf.remove(idx, len); + } + + s += buf + m_impl->m_lineEndingString; + } + + //s.chop(m_impl->m_lineEndingString.count()); + return s; +} + +/*! + \return The content of the document + \param removeTrailing whether to remove trailing whitespaces + \param preserveIndent whether to keep trailing whitespaces when they are indent +*/ +QString QDocument::text(bool removeTrailing, bool preserveIndent) const +{ + int mode = 0; + + if ( removeTrailing ) + mode |= RemoveTrailingWS; + + if ( preserveIndent ) + mode |= PreserveIndent; + + return text(mode); +} + +/*! + \brief Set the content of the document +*/ +void QDocument::setText(const QString& s) +{ + if ( !m_impl ) + return; + + int last = 0, idx = 0; + + m_impl->m_deleting = true; + + //qDeleteAll(m_impl->m_lines); + foreach ( QDocumentLineHandle *h, m_impl->m_lines ) + { + h->m_doc = 0; + h->deref(); + } + + QDocumentCommand::discardHandlesFromDocument(this); + + m_impl->m_lines.clear(); + m_impl->m_marks.clear(); + m_impl->m_status.clear(); + m_impl->m_hidden.clear(); + m_impl->m_wrapped.clear(); + m_impl->m_matches.clear(); + m_impl->m_largest.clear(); + m_impl->m_commands.clear(); + + m_impl->m_deleting = false; + + if ( s.isEmpty() ) + m_impl->m_lines << new QDocumentLineHandle(QString(), this); + + m_impl->_nix = 0; + m_impl->_dos = 0; + m_impl->_mac = 0; + + while ( idx < s.length() ) + { + if ( s.at(idx) == '\n' ) + { + if ( (idx > 0) && (s.at(idx - 1) == '\r') ) + { + ++(m_impl->_dos); + + m_impl->m_lines << new QDocumentLineHandle( + s.mid(last, idx - last - 1), + this + ); + } else { + ++(m_impl->_nix); + + m_impl->m_lines << new QDocumentLineHandle( + s.mid(last, idx - last), + this + ); + } + + last = ++idx; + } else if ( s.at(idx) == '\r' ) { + ++idx; + if ( idx >= s.length() || (s.at(idx) != '\n') ) + { + ++(m_impl->_mac); + + m_impl->m_lines << new QDocumentLineHandle( + s.mid(last, idx - last - 1), + this + ); + + last = idx; + } + } else { + ++idx; + } + } + + if ( idx != last ) + { + m_impl->m_lines << new QDocumentLineHandle( + s.mid(last, s.length() - last), + this + ); + + } +// +// if ( (idx > 0) && ((idx - 1) < s.length()) && ((s.at(idx - 1) == '\n') || (s.at(idx - 1) == '\r')) ) +// m_impl->m_lines << new QDocumentLineHandle(this); +// + + //qDebug("[one go] dos : %i; nix : %i", m_impl->_dos, m_impl->_nix); + + m_impl->m_lastModified = QDateTime::currentDateTime(); + + if ( lineEnding() == Conservative ) + setLineEnding(Conservative); + + m_impl->setWidth(); + m_impl->setHeight(); + + emit lineCountChanged(lineCount()); + + m_impl->emitContentsChange(0, m_impl->m_lines.count()); +} + +/*! + \brief Start a chunk loading + + It is possible to load document contents in one piece + or by chunks. To achieve the later you have to proceed as follows : + + \code + QDocument doc; + doc.startChunkLoading(); + + // fetch data and add it using doc.addChunk(); + + doc.stopChunkLoading(); + \endcode + + \see addChunk(const QString&) + \see stopChunkLoading() +*/ +void QDocument::startChunkLoading() +{ + if ( !m_impl ) + return; + + m_impl->m_deleting = true; + + //qDeleteAll(m_impl->m_lines); + foreach ( QDocumentLineHandle *h, m_impl->m_lines ) + { + h->m_doc = 0; + h->deref(); + } + + m_impl->m_lines.clear(); + m_impl->m_marks.clear(); + m_impl->m_status.clear(); + m_impl->m_hidden.clear(); + m_impl->m_wrapped.clear(); + m_impl->m_matches.clear(); + m_impl->m_largest.clear(); + m_impl->m_commands.clear(); + + m_impl->m_deleting = false; + + m_impl->_nix = 0; + m_impl->_dos = 0; + m_impl->_mac = 0; + + m_leftOver.clear(); +} + +/*! + \brief Stop chunk loading + + \see startChunkLoading() +*/ +void QDocument::stopChunkLoading() +{ + if ( m_leftOver.count() ) + { + m_impl->m_lines << new QDocumentLineHandle( + m_leftOver, + this + ); + + m_leftOver.clear(); + + } else { + //m_impl->m_lines << new QDocumentLineHandle(this); + } + + //qDebug("[chunk] dos : %i; nix : %i", m_impl->_dos, m_impl->_nix); + + m_impl->m_lastModified = QDateTime::currentDateTime(); + + if ( lineEnding() == Conservative ) + setLineEnding(Conservative); + + m_impl->setWidth(); + m_impl->setHeight(); + + emit lineCountChanged(lineCount()); + + emit m_impl->emitContentsChange(0, m_impl->m_lines.count()); +} + +/*! + \return The format scheme used by the document +*/ +QFormatScheme* QDocument::formatScheme() const +{ + return m_impl ? m_impl->m_formatScheme : 0; +} + +/*! + \brief Set the format scheme used by the document +*/ +void QDocument::setFormatScheme(QFormatScheme *f) +{ + if ( m_impl ) + m_impl->setFormatScheme(f); +} + +/*! + \return the language definition set to the document +*/ +QLanguageDefinition* QDocument::languageDefinition() const +{ + return m_impl ? m_impl->m_language : 0; +} + +/*! + \brief Set the language definition +*/ +void QDocument::setLanguageDefinition(QLanguageDefinition *f) +{ + if ( m_impl ) + m_impl->m_language = f; +} + +/*! + \brief Update the formatting of the whole document + This function is only useful when changing the language definition + of a non-empty document. Make sure you do not call it more often + than needed. +*/ +void QDocument::highlight() +{ + if ( m_impl ) + m_impl->emitContentsChange(0, lines()); +} + +/*! + \brief Add a chunk of text to the document +*/ +void QDocument::addChunk(const QString& txt) +{ + if ( !m_impl || txt.isEmpty() ) + return; + + m_leftOver += txt; + int idx = 0, last = 0; + + while ( idx < m_leftOver.length() ) + { + if ( m_leftOver.at(idx) == '\n' ) + { + if ( (idx > 0) && (m_leftOver.at(idx - 1) == '\r') ) + { + ++(m_impl->_dos); + + m_impl->m_lines << new QDocumentLineHandle( + m_leftOver.mid(last, idx - last - 1), + this + ); + } else { + ++(m_impl->_nix); + + m_impl->m_lines << new QDocumentLineHandle( + m_leftOver.mid(last, idx - last), + this + ); + } + + last = ++idx; + } else if ( m_leftOver.at(idx) == '\r' ) { + ++idx; + if ( idx >= m_leftOver.length() || (m_leftOver.at(idx) != '\n') ) + { + ++(m_impl->_mac); + + m_impl->m_lines << new QDocumentLineHandle( + m_leftOver.mid(last, idx - last - 1), + this + ); + + last = idx; + } + } else { + ++idx; + } + } + + if ( idx != last ) + m_leftOver = m_leftOver.mid(last); + else + m_leftOver.clear(); + +} + +/*! + \brief Print the content of the document + \param pr printer to use + + \note the printer MUST be initialized (probably using a printing dialog) +*/ +void QDocument::print(QPrinter *pr) +{ + QRect fit = pr->pageRect(); + + if ( pr->printRange() == QPrinter::Selection ) + { + qWarning("printing selection not implemented yet"); + return; + } + + if ( fit.width() < width() ) + { + // TODO: got to temporarily wrap text to fit page size + + qWarning("temporary wrapping not implementated yet"); + } + + const int lineCount = lines(); + const int linesPerPage = fit.height() / m_impl->m_lineSpacing; + int pageCount = lineCount / linesPerPage; + + if ( lineCount % linesPerPage ) + ++pageCount; + + //qDebug("%i lines per page -> %i pages", linesPerPage, pageCount); + + const int pageWidth = fit.width(); + const int pageHeight = linesPerPage * m_impl->m_lineSpacing; + + int firstPage = pr->fromPage(), lastPage = pr->toPage(); + + if ( !lastPage ) + lastPage = pageCount - 1; + + QPainter p(pr); + PaintContext cxt; + cxt.xoffset = 0; + cxt.yoffset = firstPage * pageHeight; + cxt.width = pageWidth; + cxt.height = pageHeight - m_impl->m_lineSpacing; + cxt.palette = QApplication::palette(); + cxt.fillCursorRect = false; + cxt.blinkingCursor = false; + + for ( int i = firstPage; i <= lastPage; ++i ) + { + draw(&p, cxt); + + cxt.yoffset += pageHeight; + + if ( i != lastPage ) + { + pr->newPage(); + p.translate(0, -pageHeight); + } + } +} + +/*! + \return The line ending policy of the document + + The line ending policy determines how line endings + are used when saving the document (which includes + fetching the document's text()). + + It can either be conservative (auto detect upon loading + and do not modify when saving later on) or enforce + a particular line ending (either local line ending + or a specific value). +*/ +QDocument::LineEnding QDocument::lineEnding() const +{ + return m_impl ? m_impl->m_lineEnding : Local; +} + +/*! + \return the lin endings detected upon loading + + This should only ever take the the Window of Linux value + if a document has been loaded. If no content has been + loaded it will fall back to Local. +*/ +QDocument::LineEnding QDocument::originalLineEnding() const +{ + if ( !m_impl ) + return Local; + + bool dosVSnix = m_impl->_dos > m_impl->_nix; + bool dosVSmac = m_impl->_dos > m_impl->_mac; + bool nixVSmac = m_impl->_nix > m_impl->_mac; + + // nothing loaded thus far + if ( !(dosVSnix || dosVSmac || nixVSmac) ) + return Local; + + return dosVSnix ? (dosVSmac ? Windows : OldMac) : (nixVSmac ? Unix : OldMac); +} + +/*! + \brief Set the line ending policy of the document +*/ +void QDocument::setLineEnding(LineEnding le) +{ + if ( !m_impl ) + return; + + m_impl->m_lineEnding = le; + QString& les = m_impl->m_lineEndingString; + + /* + Notes about "local" line endings : + * Dos & co : well defined CRLF + * Unix & co : well defined LF + * Mac : CR up until OS 9 LF afterwards (OSX is Unix-based) + + Guess about macros (to be confirmed/infirmed) : + Q_OS_UNIX *should* be set under OSX + Q_OS_MAC *might* be set under OSX + + => order #ifdefs to try avoiding troubles + */ + #ifdef Q_OS_WIN + const QString loc_les = "\r\n"; + #elif defined(Q_OS_UNIX) + const QString loc_les = "\n"; + #elif defined(Q_OS_MAC) + const QString loc_les = "\r"; + #else + + const QString loc_les = "\n"; + #endif + + switch ( le ) + { + case Conservative : + switch ( originalLineEnding() ) + { + case Windows : + les = "\r\n"; + break; + case OldMac : + les = "\r"; + break; + case Unix : + les = "\n"; + default: + les = loc_les; + break; + } + break; + + case Unix : + les = "\n"; + break; + + case OldMac : + les = "\r"; + break; + + case Windows : + les = "\r\n"; + break; + + default : + les = "\n"; + break; + } + + emit lineEndingChanged(le); +} + +/*! + \return the font used by ALL documents to render their content + + This font is also used to do calculations (such as converting + (line, column) cursor position to (x, y) document position (or + the inverse transformation)) + + \note this limitation is historic and may disappear + in future versions +*/ +QFont QDocument::font() +{ + return *(QDocumentPrivate::m_font); +} + +/*! + \brief Set the font of ALL documents + + \note this limitation is historic and may disappear + in future versions +*/ +void QDocument::setFont(const QFont& f) +{ + QDocumentPrivate::setFont(f); + //emit contentsChanged(); +} + +/*! + \return The font metrics used by ALL documents + + \note this limitation is historic and may disappear + in future versions +*/ +const QFontMetrics& QDocument::fontMetrics() +{ + return *(QDocumentPrivate::m_fontMetrics); +} + +/*! + \return The default tab stop common to ALL documents + + \note this limitation is historic and may disappear + in future versions +*/ +int QDocument::tabStop() +{ + return QDocumentPrivate::m_defaultTabStop; +} + +/*! + \brief Set the default tab stop common to all documents + + \note this limitation is historic and may disappear + in future versions +*/ +void QDocument::setTabStop(int n) +{ + QDocumentPrivate::m_defaultTabStop = n; + + foreach ( QDocumentPrivate *d, QDocumentPrivate::m_documents ) + { + d->m_tabStop = n; + d->emitFormatsChanged(); + } +} + +/*! + \return the whitesapce display mode +*/ +QDocument::WhiteSpaceMode QDocument::showSpaces() +{ + return QDocumentPrivate::m_showSpaces; +} + +/*! + \brief Set the whitespace display mode +*/ +void QDocument::setShowSpaces(WhiteSpaceMode m) +{ + QDocumentPrivate::m_showSpaces = m; + + foreach ( QDocumentPrivate *d, QDocumentPrivate::m_documents ) + d->emitFormatsChanged(); + +} + +/*! + \brief Set the edit cursor + + Archaic concept designed for use in QEditor + (is it still used???) +*/ +QDocumentCursor* QDocument::editCursor() const +{ + return m_impl ? m_impl->m_editCursor : 0; +} + +/*! + \brief Set the edit cursor + + \see editCursor() +*/ +void QDocument::setEditCursor(QDocumentCursor *c) +{ + if ( m_impl ) + m_impl->m_editCursor = c; + +} + +/*! + \return the width of the document, in pixels + + The width of the document is that of longest text line. +*/ +int QDocument::width() const +{ + return m_impl ? m_impl->m_width : 0; +} + +/*! + \return the height of the document, in pixels +*/ +int QDocument::height() const +{ + return m_impl ? m_impl->m_height : 0; +} + +/*! + \return The width constraint imposed on that document + + Setting a width constraint on a document achieves line + wrapping. +*/ +int QDocument::widthConstraint() const +{ + return (m_impl && m_impl->m_constrained) ? m_impl->m_width : 0; +} + +/*! + \return the number of text lines in the document + + The number of visual lines may differ from that of text + lines as soon as line wrapping and/or folding are enabled. + + \deprecated Use lineCount() instead +*/ +int QDocument::lines() const +{ + return m_impl ? m_impl->m_lines.count() : 0; +} + +/*! + \return the number of text lines in the document + + The number of visual lines may differ from that of text + lines as soon as line wrapping and/or folding are enabled. +*/ +int QDocument::lineCount() const +{ + return m_impl ? m_impl->m_lines.count() : 0; +} + +/*! + \return the number of visual lines in the document + \deprecated Use visualLineCount() instead +*/ +int QDocument::visualLines() const +{ + return m_impl ? m_impl->visualLine(m_impl->m_lines.count() - 1) : 0; +} + +/*! + \return the number of visual lines in the document +*/ +int QDocument::visualLineCount() const +{ + return m_impl ? m_impl->visualLine(m_impl->m_lines.count() - 1) : 0; +} + +/*! + \brief Convert a text (logical) line number int a visual line number + + \note this is not a 1:1 mapping as logical lines can span over several visual lines +*/ +int QDocument::visualLineNumber(int textLineNumber) const +{ + return m_impl ? m_impl->visualLine(textLineNumber) : -1; +} + +/*! + \brief Convert a visual line number int a text (logical) line number + + \note this is not a 1:1 mapping as logical lines can span over several visual lines +*/ +int QDocument::textLineNumber(int visualLineNumber) const +{ + return m_impl ? m_impl->textLine(visualLineNumber) : -1; +} + +/*! + \brief Clear the width constraint, if any +*/ +void QDocument::clearWidthConstraint() +{ + if ( m_impl ) + m_impl->setWidth(0); +} + +/*! + \brief Set a new width constraint + \param width maximum width to allow + + Passing a value inferior (or equal) to zero clear the width constraint, if any. +*/ +void QDocument::setWidthConstraint(int width) +{ + if ( m_impl ) + m_impl->setWidth(qMax(0, width)); +} + +/*! + \return the line object at a given line number + \param line Text line to acces +*/ +QDocumentLine QDocument::line(int line) const +{ + return QDocumentLine(m_impl->at(line)); +} + +/*! + \return the line number corresponding to a given document y coordinate + \param ypos Y document coordinate of the target + \param wrap if not null, will be set to the wrap offset (position of the + visual line among the sublines of the wrapped text line). + +*/ +int QDocument::lineNumber(int ypos, int *wrap) const +{ + int ln = ypos / m_impl->m_lineSpacing; + + return m_impl->textLine(ln, wrap); +} + +/*! + \return the line object to which an iterator points +*/ +QDocumentLine QDocument::line(QDocumentConstIterator iterator) const +{ + return (m_impl && (m_impl->constEnd() != iterator)) ? QDocumentLine(*iterator) : QDocumentLine(); +} + +/*! + \return A cursor operating on the document, placed at a given position + \param line target line number (text line) + \param column target text column +*/ +QDocumentCursor QDocument::cursor(int line, int column) const +{ + return QDocumentCursor(const_cast(this), line, column); +} + +/*! + \return the document line which contains a given (document-wide) text position + + \note The sole purpose of this method is to have an API close to that of QTextDocument. + This method being ridiculously slow it is recommended to avoid it whenever possible. +*/ +QDocumentLine QDocument::findLine(int& position) const +{ + if ( !m_impl ) + return QDocumentLine(); + + return QDocumentLine(m_impl->lineForPosition(position)); +} + +/*! + \return The Y document coordinate of a given line + \param ln textual line number +*/ +int QDocument::y(int ln) const +{ + if ( !m_impl ) + return -1; + + return m_impl->m_lineSpacing * m_impl->visualLine(ln); +} + +/*! + \overload + + \return The Y document coordinate of a given line + \param l line object + + \note Significantly slower than the line number based version. +*/ +int QDocument::y(const QDocumentLine& l) const +{ + qDebug("bad perf..."); + + return y(l.lineNumber()); +} + +/*! + \return the rectangle (in document position) occupied by the line + \param line textual line number + + \note the width of the returned rectangle is the DOCUMENT's width +*/ +QRect QDocument::lineRect(int line) const +{ + const int yoff = y(line); + + return (yoff != -1) ? QRect(0, yoff, width(), this->line(line).lineSpan() * m_impl->m_lineSpacing) : QRect(); +} + +/*! + \overload + \return the rectangle (in document position) occupied by the line + + \note the width of the returned rectangle is the DOCUMENT's width + \note Significantly slower than the line number based version. +*/ +QRect QDocument::lineRect(const QDocumentLine& l) const +{ + //return lineRect(l.lineNumber()); + const int yoff = y(l); + + return (yoff != -1) ? QRect(0, yoff, width(), m_impl->m_lineSpacing) : QRect(); +} + +/*! + \return the line at a given document position +*/ +QDocumentLine QDocument::lineAt(const QPoint& p) const +{ + if ( !m_impl ) + return QDocumentLine(); + + return line(lineNumber(p.y())); +} + +/*! + \return a document iterator pointing to the first line +*/ +QDocumentConstIterator QDocument::begin() const +{ + Q_ASSERT(m_impl); + + return m_impl->m_lines.constBegin(); +} + +/*! + \return a document iterator pointing past the last line +*/ +QDocumentConstIterator QDocument::end() const +{ + Q_ASSERT(m_impl); + + return m_impl->m_lines.constEnd(); +} + +/*! + \return a document iterator pointing to a given line +*/ +QDocumentConstIterator QDocument::iterator(int ln) const +{ + Q_ASSERT(m_impl); + + return begin() + ln; +} + +/*! + \overload + \note If you can avoid using this method, do so unless performance really isn't an issue +*/ +QDocumentConstIterator QDocument::iterator(const QDocumentLine& l) const +{ + Q_ASSERT(m_impl); + + QDocumentConstIterator it = begin(), e = end(); + + while ( (*it != l.handle()) && (it != e) ) + ++it; + + return it; +} + +/*! + \brief Convert a document (or viewport) (x, y) position to a (line, column) cursor position + \param p document position + \param line where the line number will be stored + \param column where the column (text position within line) will be stored +*/ +void QDocument::cursorForDocumentPosition(const QPoint& p, int& line, int& column) const +{ + if ( !m_impl ) + return; + + int wrap = 0; + line = lineNumber(p.y(), &wrap); + QDocumentLine l = this->line(line); + + if ( !l.isValid() ) + return; + + //qDebug("%i %i", line, wrap); + column = l.documentOffsetToCursor(p.x(), wrap * QDocumentPrivate::m_lineSpacing); + + //qDebug("(%i, %i) -> (%i [+%i], %i)", p.x(), p.y(), line, wrap, column); +} + +/*! + \return The cursor nearest to a document (x, y) position +*/ +QDocumentCursor QDocument::cursorAt(const QPoint& p) const +{ + int ln = -1, col = -1; + + cursorForDocumentPosition(p, ln, col); + + return QDocumentCursor(const_cast(this), ln, col); +} + +/*! + \brief Draw the contents of the document + \param p painter to use + \param cxt paint context (specifies what part of the document to draw, among other things) +*/ +void QDocument::draw(QPainter *p, PaintContext& cxt) +{ + m_impl->draw(p, cxt); +} + +/*! + \brief Execute a document command (editing operation) +*/ +void QDocument::execute(QDocumentCommand *cmd) +{ + if ( m_impl && cmd ) + m_impl->execute(cmd); + +} + +/*! + \return The default line ending policy +*/ +QDocument::LineEnding QDocument::defaultLineEnding() +{ + return QDocumentPrivate::m_defaultLineEnding; +} + +/*! + \brief Set the default line ending policy + + \note The line ending policy of existing documents is changed accordingly +*/ +void QDocument::setDefaultLineEnding(QDocument::LineEnding le) +{ + QDocumentPrivate::m_defaultLineEnding = le; + + foreach ( QDocumentPrivate *d, QDocumentPrivate::m_documents ) + { + d->m_doc->setLineEnding(le); + } +} + +/*! + \return The default format scheme used to draw document contents +*/ +QFormatScheme* QDocument::defaultFormatScheme() +{ + return QDocumentPrivate::m_defaultFormatScheme; +} + +/*! + \brief Set the default format scheme + + \note Existing documents using the default format scheme will see their format scheme changed +*/ +void QDocument::setDefaultFormatScheme(QFormatScheme *f) +{ + foreach ( QDocumentPrivate *d, QDocumentPrivate::m_documents ) + { + if ( d->m_formatScheme == QDocumentPrivate::m_defaultFormatScheme ) + d->setFormatScheme(f); + } + + QDocumentPrivate::m_defaultFormatScheme = f; +} + +/*! + \deprecated + \brief Compatibility alias for defaultFormatScheme() +*/ +QFormatScheme* QDocument::formatFactory() +{ + return defaultFormatScheme(); +} + +/*! + \deprecated + \brief Compatibility alias for setDefaultFormatScheme() +*/ +void QDocument::setFormatFactory(QFormatScheme *f) +{ + setDefaultFormatScheme(f); +} + +/*! + \brief Begin a macro + + Macro in QDocument map directly to macro in the underlying undo stack +*/ +void QDocument::beginMacro() +{ + if ( m_impl ) + m_impl->beginChangeBlock(); + +} + +/*! + \brief End a macro +*/ +void QDocument::endMacro() +{ + if ( m_impl ) + m_impl->endChangeBlock(); + +} + +/*! + \brief Get an available group id for matches +*/ +int QDocument::getNextGroupId() +{ + return m_impl ? m_impl->getNextGroupId() : -1; +} + +void QDocument::releaseGroupId(int groupId) +{ + if ( m_impl ) + m_impl->releaseGroupId(groupId); +} + +/*! + \brief Clear matches +*/ +void QDocument::clearMatches(int gid) +{ + if ( m_impl ) + { + m_impl->clearMatches(gid); + } +} + +/*! + \brief Highlight the matched sequences + + \note Both position are BEFORE the matched characters (cursor position). +*/ +void QDocument::addMatch(int gid, int line, int pos, int len, int format) +{ + if ( m_impl ) + { + m_impl->addMatch(gid, line, pos, len, format); + } +} + +/*! + \ +*/ +void QDocument::flushMatches(int gid) +{ + if ( m_impl ) + { + m_impl->flushMatches(gid); + } +} + +/*! + \return Whether the document is in a clean state + + This is undo stak terminology. A clean document is one + whose undo stack is at the same index it was upon last + save/load. In other words : clean = !modified +*/ +bool QDocument::isClean() const +{ + return m_impl ? m_impl->m_commands.isClean() : true; +} + +/*! + \brief Set the document to clean state + + This method does not go back to clean state but tell + the stack that the current state is to be considered + as the clean state. +*/ +void QDocument::setClean() +{ + if ( m_impl ) + { + m_impl->m_commands.setClean(); + //m_impl->m_status.clear(); + + QHash >::iterator it = m_impl->m_status.begin(); + + while ( it != m_impl->m_status.end() ) + { + it->second = it->first; + ++it; + } + } +} + +/*! + \return Whether a given line has been modified since last save/load +*/ +bool QDocument::isLineModified(const QDocumentLine& l) const +{ + if ( !m_impl ) + return false; + + QHash >::const_iterator it = m_impl->m_status.constFind(l.handle()); + + //if ( it != m_impl->m_status.constEnd() ) + // qDebug("%i vs %i", it->first, it->second); + + return it != m_impl->m_status.constEnd() ? it->first != it->second : false; +} + +/*! + \return Whether a given line has been modified since last load +*/ +bool QDocument::hasLineEverBeenModified(const QDocumentLine& l) const +{ + return m_impl ? m_impl->m_status.contains(l.handle()) : false; +} + +/*! + \return the maximum number of marks on a single line + + This is meant for the line mark panel to smartly adapt its size. +*/ +int QDocument::maxMarksPerLine() const +{ + return m_impl ? m_impl->maxMarksPerLine() : 0; +} + +/*! + \brief Find the next mark of a given type + \return the line on which a mark was found + \param id mark identifier to find + \param from line from which to start search + \param until line until which to search + + \a from and \a until can be negatives, in which case they + indicate positions from the end of the document (i.e -1 is + last line, -2 the line before last line and so on). + + If \a until is inferior to \a from and no matching mark + is found in the [from, end] range then the [0, until] + range will also be searched. +*/ +int QDocument::findNextMark(int id, int from, int until) const +{ + return m_impl ? m_impl->findNextMark(id, from, until) : -1; +} + +/*! + \brief Find the previous mark of a given type + \return the line on which a mark was found + \param id mark identifier to find + \param from line from which to start search + \param until line until which to search + + \a from and \a until can be negatives, in which case they + indicate positions from the end of the document (i.e -1 is + last line, -2 the line before last line and so on). + + If \a until is superior to \a from and no matching mark + is found in the [0, from] range then the [until, end] + range will also be searched (both range being searched backward, + of course). +*/ +int QDocument::findPreviousMark(int id, int from, int until) const +{ + return m_impl ? m_impl->findPreviousMark(id, from, until) : -1; +} + +/*! + \return the date/time of the last modification of the document + + If the document has not been modified since its load the date/time + of last modification (as reported by QFileInfo) will be returned. +*/ +QDateTime QDocument::lastModified() const +{ + return m_impl ? m_impl->m_lastModified : QDateTime(); +} + +/*! + \brief set the date/time of the last modification of the document + + You should not need to use that EVER. It is only provided for use + in QEditor (and possibly in some panels). +*/ +void QDocument::setLastModified(const QDateTime& d) +{ + if ( m_impl ) + m_impl->m_lastModified = d; +} + +///////////////////////// +// QDocumentLineHandle +///////////////////////// +//static quint32 q_line_counter = 0; + +/*! + \class QDocumentLineHandle + \brief Private implementation of a document line +*/ + +/*! + \ +*/ +QDocumentLineHandle::QDocumentLineHandle(QDocument *d) + : m_doc(d) +#if QT_VERSION >= 0x040400 + , m_ref(1) +#endif + , m_indent(0) + , m_state(QDocumentLine::LayoutDirty) + , m_layout(0) +{ + #if QT_VERSION < 0x040400 + m_ref.init(1); + #endif +} + +/*! + \ +*/ +QDocumentLineHandle::QDocumentLineHandle(const QString& s, QDocument *d) + : m_text(s) + , m_doc(d) +#if QT_VERSION >= 0x040400 + , m_ref(1) +#endif + , m_indent(0) + , m_state(QDocumentLine::LayoutDirty) + , m_layout(0) +{ + #if QT_VERSION < 0x040400 + m_ref.init(1); + #endif +} + +QDocumentLineHandle::~QDocumentLineHandle() +{ + Q_ASSERT(!m_ref); + + if ( m_doc && m_doc->impl() ) + m_doc->impl()->emitLineDeleted(this); +} + +int QDocumentLineHandle::count() const +{ + return m_text.count(); +} + +int QDocumentLineHandle::length() const +{ + return m_text.length(); +} + +int QDocumentLineHandle::line() const +{ + return (m_doc && m_doc->impl()) ? m_doc->impl()->indexOf(this) : -1; +} + +int QDocumentLineHandle::position() const +{ + return (m_doc && m_doc->impl()) ? m_doc->impl()->position(this) : -1; +} + +QString QDocumentLineHandle::text() const +{ + return m_text; +} + +int QDocumentLineHandle::indent() const +{ + int l = nextNonSpaceChar(0); + return QDocument::screenLength(m_text.constData(), l == -1 ? m_text.length() : l, m_doc->tabStop()); +} + +int QDocumentLineHandle::nextNonSpaceChar(uint pos) const +{ + const int len = m_text.length(); + const QChar *unicode = m_text.unicode(); + + for ( int i = pos; i < len; ++i ) + { + if ( !unicode[i].isSpace() ) + return i; + } + + return -1; +} + +int QDocumentLineHandle::previousNonSpaceChar(int pos) const +{ + const int len = m_text.length(); + const QChar *unicode = m_text.unicode(); + + if ( pos < 0 ) + pos = 0; + + if ( pos >= len ) + pos = len - 1; + + for ( int i = pos; i >= 0; --i ) + { + if ( !unicode[i].isSpace() ) + return i; + } + + return -1; +} + +QDocument* QDocumentLineHandle::document() const +{ + return m_doc; +} + +bool QDocumentLineHandle::hasFlag(int s) const +{ + return m_state & s; +} + +void QDocumentLineHandle::setFlag(int s, bool y) const +{ + if ( y ) + m_state |= s; + else + m_state &= ~s; +} + +QDocumentLineHandle* QDocumentLineHandle::next() const +{ + return (m_doc && m_doc->impl()) ? m_doc->impl()->next(this) : 0; +} + +QDocumentLineHandle* QDocumentLineHandle::previous() const +{ + return (m_doc && m_doc->impl()) ? m_doc->impl()->previous(this) : 0; +} + +void QDocumentLineHandle::updateWrap() const +{ + m_indent = 0; + m_frontiers.clear(); + + if ( !m_doc->impl()->m_constrained ) + { + if ( m_layout ) + setFlag(QDocumentLine::LayoutDirty, true); + + return; + } + + const int tabStop = m_doc->impl()->m_tabStop; + const int maxWidth = m_doc->widthConstraint(); + + if ( m_layout ) + { + layout(); + } else if ( QDocumentPrivate::m_fixedPitch ) { + + int idx = 0, minx = 0, lastBreak = 0, lastWidth = 0, lastX = 0, rx, + x = QDocumentPrivate::m_leftMargin, column = 0, cwidth; + + QChar c; + int indent = 0; + const int sw = QDocumentPrivate::m_spaceWidth; + + while ( idx < m_text.length() && m_text.at(idx).isSpace() ) + { + c = m_text.at(idx); + + if ( c.unicode() == '\t' ) + { + int taboffset = tabStop - (column % tabStop); + + column += taboffset; + cwidth = sw * taboffset; + } else { + ++column; + cwidth = sw; + } + + x += cwidth; + ++idx; + } + + indent = idx; + minx = rx = x; + + if ( (minx + sw) >= maxWidth ) + { + //qWarning("Please stop shrinking so aggressively.\nNo attempt will be made to show something decent"); + // shrinking too aggresively (or too much spaces...) ungraceful fallback + + indent = idx = 0; + minx = rx = x = QDocumentPrivate::m_leftMargin; + } + + m_indent = minx - QDocumentPrivate::m_leftMargin; + + while ( idx < m_text.length() ) + { + if ( c.isSpace() ) //!isWord(c) || !isWord(m_text.at(idx)) ) + { + lastX = rx; + lastWidth = x; + lastBreak = idx; + } + + c = m_text.at(idx); + + if ( c.unicode() == '\t' ) + { + int taboffset = tabStop - (column % tabStop); + + column += taboffset; + cwidth = sw * taboffset; + } else { + ++column; + cwidth = sw; + } + + if ( x + cwidth > maxWidth ) + { + if ( lastBreak == (m_frontiers.count() ? m_frontiers.last().first : indent) ) + { + // perfect cut or fallback to aggressive cut + m_frontiers << qMakePair(idx, rx); + lastBreak = idx; + lastWidth = x; + lastX = rx; + x = minx; + } else if ( lastBreak < idx ) { + // word cut at a non-ideal position + m_frontiers << qMakePair(lastBreak, lastX); + x = minx + (x - lastWidth); + } else { + m_frontiers << qMakePair(idx, rx); + x = minx; + } + } + + rx += cwidth; + x += cwidth; + ++idx; + } + } else { + QMediumArray composited = compose(); + + int idx = 0, minx = 0, lastBreak = 0, lastWidth = 0, lastX = 0, rx, + x = QDocumentPrivate::m_leftMargin, column = 0, cwidth; + + QChar c; + int indent = 0; + int fmt = 0; + const QVector& fonts = m_doc->impl()->m_fonts; + + while ( idx < m_text.length() && m_text.at(idx).isSpace() ) + { + c = m_text.at(idx); + fmt = idx < composited.count() ? composited[idx] : 0; + QFontMetrics fm(fonts.at(fmt)); + + if ( c.unicode() == '\t' ) + { + int taboffset = tabStop - (column % tabStop); + + column += taboffset; + cwidth = fm.width(' ') * taboffset; + } else { + ++column; + cwidth = fm.width(c); + } + + x += cwidth; + ++idx; + } + + indent = idx; + minx = rx = x; + + if ( (minx + QDocumentPrivate::m_spaceWidth) >= maxWidth ) + { + //qWarning("Please stop shrinking so aggressively.\nNo attempt will be made to show something decent"); + + indent = idx = 0; + minx = rx = x = QDocumentPrivate::m_leftMargin; + } + + m_indent = minx - QDocumentPrivate::m_leftMargin; + + while ( idx < m_text.length() ) + { + if ( c.isSpace() ) //!isWord(c) || !isWord(m_text.at(idx)) ) + { + lastX = rx; + lastWidth = x; + lastBreak = idx; + } + + c = m_text.at(idx); + fmt = idx < composited.count() ? composited[idx] : 0; + QFontMetrics fm(fonts.at(fmt)); + + if ( c.unicode() == '\t' ) + { + int taboffset = tabStop - (column % tabStop); + + column += taboffset; + cwidth = fm.width(' ') * taboffset; + } else { + ++column; + cwidth = fm.width(c); + } + + if ( x + cwidth > maxWidth ) + { + if ( lastBreak == (m_frontiers.count() ? m_frontiers.last().first : indent) ) + { + // perfect cut or fallback to aggressive cut + m_frontiers << qMakePair(idx, rx); + lastBreak = idx; + lastWidth = x; + lastX = rx; + x = minx; + } else if ( lastBreak < idx ) { + // word cut at a non-ideal position + m_frontiers << qMakePair(lastBreak, lastX); + x = minx + (x - lastWidth); + } else { + m_frontiers << qMakePair(idx, rx); + x = minx; + } + } + + rx += cwidth; + x += cwidth; + ++idx; + } + } +} + +int QDocumentLineHandle::cursorToX(int cpos) const +{ + cpos = qBound(0, cpos, m_text.length()); + + if ( m_layout ) + { + int xoff = QDocumentPrivate::m_leftMargin, coff = 0, line = m_frontiers.count(); + + for ( int i = 0; i < m_frontiers.count(); ++i ) + { + if ( m_frontiers.at(i).first >= cpos ) + { + line = i; + + break; + } + } + + if ( line ) + { + //coff = m_frontiers.at(line - 1).first; + xoff = m_frontiers.at(line - 1).second; + } + + //qDebug("c:%i (wrap:%i) => c2x(x - %i) + %i", cpos, line, coff, xoff); + return qRound(m_layout->lineAt(line).cursorToX(cpos - coff)) + xoff; + } + + int tabStop = m_doc->impl()->m_tabStop; + + if ( QDocumentPrivate::m_fixedPitch ) + { + return QDocument::screenLength(m_text.constData(), cpos, tabStop) + * QDocumentPrivate::m_spaceWidth + + QDocumentPrivate::m_leftMargin; + } + + //qDebug("c->x(%i) unsafe computations...", cpos); + + QMediumArray composited = compose(); + const QVector& fonts = m_doc->impl()->m_fonts; + + if ( (composited.count() < cpos) || fonts.isEmpty() ) + return QDocumentPrivate::m_fontMetrics->width(m_text.left(cpos)); + + int idx = 0, column = 0, cwidth; + int screenx = QDocumentPrivate::m_leftMargin; + + while ( idx < cpos ) + { + QChar c = m_text.at(idx); + QFontMetrics fm(fonts.at(idx < composited.count() ? composited[idx] : 0)); + + if ( c == '\t' ) + { + int taboffset = tabStop - (column % tabStop); + + column += taboffset; + cwidth = fm.width(' ') * taboffset; + } else { + ++column; + cwidth = fm.width(c); + } + + screenx += cwidth; + ++idx; + } + + //qDebug("cursorToX(%i) = %i", cpos, screenx); + + return screenx; +} + +int QDocumentLineHandle::xToCursor(int xpos) const +{ + //qDebug("x->c(%i) unsafe computations...", xpos); + if ( m_layout ) + { + int xoff = QDocumentPrivate::m_leftMargin, coff = 0, line = m_frontiers.count(); + + for ( int i = 0; i < m_frontiers.count(); ++i ) + { + if ( m_frontiers.at(i).second >= xpos ) + { + line = i; + + break; + } + } + + if ( line ) + { + //coff = m_frontiers.at(line - 1).first; + xoff = m_frontiers.at(line - 1).second; + } + + //qDebug("x:%i (wrap:%i) => x2c(x - %i) + %i", xpos, line, xoff, coff); + return m_layout->lineAt(line).xToCursor(xpos - xoff) + coff; + } + + int screenx = xpos; + int tabStop = m_doc->impl()->m_tabStop; + const QVector& fonts = m_doc->impl()->m_fonts; + + if ( QDocumentPrivate::m_fixedPitch ) + { + int screenPos = + (screenx - QDocumentPrivate::m_leftMargin / 2) + / + QDocumentPrivate::m_spaceWidth + ; + + if ( tabStop == 1 ) + return screenPos; + + int idx = 0, column = 0; + + while ( (column < screenPos) && (idx < m_text.length()) ) + { + QChar c = m_text.at(idx); + + if ( c == QLatin1Char('\t') ) + { + int taboffset = tabStop - (column % tabStop); + column += taboffset; + } else { + ++column; + } + + ++idx; + } + + return idx; + } else { + if ( screenx <= QDocumentPrivate::m_leftMargin ) + return 0; + + QMediumArray composited = compose(); + + int idx = 0, x = 0, column = 0, cwidth; + screenx -= QDocumentPrivate::m_leftMargin; + + while ( idx < m_text.length() ) + { + QFontMetrics fm(fonts.at(idx < composited.count() ? composited[idx] : 0)); + + if ( m_text.at(idx) == '\t' ) + { + int taboffset = tabStop - (column % tabStop); + + column += taboffset; + cwidth = fm.width(' ') * taboffset; + } else { + ++column; + cwidth = fm.width(m_text.at(idx)); + } + + int mid = (x + (cwidth / 2) + 1); + + if ( screenx <= mid ) + return idx; + else if ( screenx <= (x + cwidth) ) + return idx + 1; + + x += cwidth; + ++idx; + } + + return m_text.length(); + } +} + +int QDocumentLineHandle::wrappedLineForCursor(int cpos) const +{ + int wrap = m_frontiers.count(); + + for ( int i = 0; i < m_frontiers.count(); ++i ) + { + if ( m_frontiers.at(i).first > cpos ) + { + wrap = i; + break; + } + } + + return wrap; +} + +int QDocumentLineHandle::documentOffsetToCursor(int x, int y) const +{ + int wrap = y / QDocumentPrivate::m_lineSpacing; + + if ( wrap > m_frontiers.count() ) + { + // return an invalid value instead? + //qDebug("a bit too far : (%i, %i)", x, y); + //wrap = m_frontiers.count(); + + return m_text.length(); + } + + if ( m_frontiers.count() ) + { + //qDebug("(%i, %i) : %i", x, y, wrap); + x = qMin(x, m_doc->widthConstraint()); + } + + int cpos = 0; + int max = m_text.length(); + + if ( wrap < m_frontiers.count() ) + max = m_frontiers.at(wrap).first - 1; + + if ( wrap > 0 ) + cpos = m_frontiers.at(wrap - 1).first; + + x -= QDocumentPrivate::m_leftMargin; + + int idx = cpos, column = 0; + const int ts = m_doc->tabStop(); + + if ( m_layout ) + { + cpos = m_layout->lineAt(wrap).xToCursor(x); + } else if ( QDocumentPrivate::m_fixedPitch ) { + if ( wrap ) + x -= m_indent; + + while ( (idx < max) && (x > 0) ) + { + bool tab = m_text.at(idx).unicode() == '\t'; + int cwidth = QDocumentPrivate::m_spaceWidth; + + if ( tab ) + { + int coff = ts - (column % ts); + column += coff; + cwidth *= coff; + } else { + ++column; + } + + int thresold = (2 * cwidth) / 3; + + if ( x <= thresold ) + break; + + x -= cwidth; + ++idx; + } + + cpos = idx; + } else { + if ( !hasFlag(QDocumentLine::FormatsApplied) ) + compose(); + + if ( wrap ) + x -= m_indent; + + const QVector& fonts = m_doc->impl()->m_fonts; + + while ( (idx < max) && (x > 0) ) + { + int fid = idx < m_cache.count() ? m_cache[idx] : 0; + + if ( fid < 0 ) + fid = 0; + + QFontMetrics fm = (fid < fonts.count()) ? QFontMetrics(fonts.at(fid)) : m_doc->fontMetrics(); + + QChar c = m_text.at(idx); + bool tab = c.unicode() == '\t'; + int cwidth = 0; + + if ( tab ) + { + int coff = ts - (column % ts); + column += coff; + cwidth = fm.width(' '); + cwidth *= coff; + } else { + ++column; + cwidth = fm.width(c); + } + + int thresold = (2 * cwidth) / 3; + + if ( x <= thresold ) + break; + + x -= cwidth; + ++idx; + } + + cpos = idx; + } + + return cpos; +} + +void QDocumentLineHandle::cursorToDocumentOffset(int cpos, int& x, int& y) const +{ + if ( cpos > m_text.length() ) + cpos = m_text.length(); + else if ( cpos < 0 ) + cpos = 0; + + int idx = 0; + int wrap = wrappedLineForCursor(cpos); + + x = QDocumentPrivate::m_leftMargin; + y = wrap * QDocumentPrivate::m_lineSpacing; + + if ( wrap ) + { + idx = m_frontiers.at(wrap - 1).first; + } + + int column = 0; + const int ts = m_doc->tabStop(); + + if ( m_layout ) + { + x += m_layout->lineAt(wrap).cursorToX(cpos); + } else if ( QDocumentPrivate::m_fixedPitch ) { + if ( wrap ) + x += m_indent; + + while ( idx < cpos ) + { + bool tab = m_text.at(idx).unicode() == '\t'; + int cwidth = QDocumentPrivate::m_spaceWidth; + + if ( tab ) + { + int coff = ts - (column % ts); + column += coff; + cwidth *= coff; + } else { + ++column; + } + + x += cwidth; + ++idx; + } + } else { + if ( !hasFlag(QDocumentLine::FormatsApplied) ) + compose(); + + if ( wrap ) + x += m_indent; + + const QVector& fonts = m_doc->impl()->m_fonts; + + while ( idx < cpos ) + { + int fid = idx < m_cache.count() ? m_cache[idx] : 0; + + if ( fid < 0 ) + fid = 0; + + QFontMetrics fm = (fid < fonts.count()) ? QFontMetrics(fonts.at(fid)) : m_doc->fontMetrics(); + + QChar c = m_text.at(idx); + bool tab = c.unicode() == '\t'; + int cwidth = 0; + + if ( tab ) + { + int coff = ts - (column % ts); + column += coff; + cwidth = fm.width(' '); + cwidth *= coff; + } else { + ++column; + cwidth = fm.width(c); + } + + x += cwidth; + ++idx; + } + } +} + +QPoint QDocumentLineHandle::cursorToDocumentOffset(int cpos) const +{ + QPoint p; + cursorToDocumentOffset(cpos, p.rx(), p.ry()); + return p; + #if 0 + int y = 0; + int x = cursorToX(cpos); + + if ( m_frontiers.isEmpty() ) + return QPoint(x, y); + + int first = m_frontiers.at(0).first; + + if ( cpos < first ) + return QPoint(x, y); + + int wrappedX = 0; + int fns = nextNonSpaceChar(0), off = 0; + + if ( fns != -1 && fns < first ) + off = cursorToX(fns); + else + off = QDocumentPrivate::m_leftMargin; + + for ( int j = 0; j < m_frontiers.count(); ++j ) + { + if ( m_frontiers[j].first <= cpos ) + { + wrappedX = m_frontiers[j].second; + y += QDocumentPrivate::m_lineSpacing; + } else { + break; + } + } + + return QPoint(x - wrappedX + off, y); + #endif +} + +void QDocumentLineHandle::clearOverlays() +{ + m_overlays.clear(); + + //setFlag(QDocumentLine::LayoutDirty, true); + setFlag(QDocumentLine::FormatsApplied, false); + //applyOverlays(); +} + +void QDocumentLineHandle::addOverlay(const QFormatRange& over) +{ + m_overlays << over; + + //setFlag(QDocumentLine::LayoutDirty, true); + setFlag(QDocumentLine::FormatsApplied, false); + //applyOverlays(); +} + +void QDocumentLineHandle::removeOverlay(const QFormatRange& over) +{ + int i = m_overlays.removeAll(over); + + //setFlag(QDocumentLine::LayoutDirty, true); + if ( i ) + setFlag(QDocumentLine::FormatsApplied, false); + //applyOverlays(); +} + +void QDocumentLineHandle::shiftOverlays(int position, int offset) +{ + if ( offset > 0 ) + { + for ( int i = 0; i < m_overlays.count(); ++i ) + { + QFormatRange& r = m_overlays[i]; + + if ( r.offset >= position ) + { + r.offset += offset; + } else if ( r.offset + r.length > position ) { + m_overlays.removeAt(i); + --i; + } + } + } else if ( offset < 0 ) { + const int max = position - offset; + + for ( int i = 0; i < m_overlays.count(); ++i ) + { + QFormatRange& r = m_overlays[i]; + + if ( r.offset >= max ) + { + r.offset += offset; + } else if ( r.offset + r.length >= position ) { + m_overlays.removeAt(i); + --i; + } + } + } + + setFlag(QDocumentLine::FormatsApplied, false); +} + +void QDocumentLineHandle::setFormats(const QVector& fmts) +{ + m_formats = fmts; + + while ( m_formats.count() > m_text.length() ) + m_formats.pop_back(); + + while ( m_formats.count() < m_text.length() ) + m_formats.append(0); + + //setFlag(QDocumentLine::LayoutDirty, true); + setFlag(QDocumentLine::FormatsApplied, false); + //applyOverlays(); +} + +QMediumArray QDocumentLineHandle::compose() const +{ + //QMediumArray m_composited(m_text.length()); + if ( hasFlag(QDocumentLine::FormatsApplied) ) + return m_cache; + + m_cache.resize(m_text.length()); + + for ( int i = 0; i < qMin(m_formats.count(), m_text.length()); ++i ) + m_cache[i] = m_formats.at(i); + + for ( int i = m_formats.count(); i < m_text.length(); ++i ) + m_cache[i] = 0; + + // compositing formats and overlays + foreach ( const QFormatRange& r, m_overlays ) + { + int beg = qMax(0, r.offset); + int end = qMin(r.offset + r.length, m_cache.count()); + + for ( int i = beg; i < end; ++i ) + { + m_cache[i] = r.format; + } + } + + setFlag(QDocumentLine::FormatsApplied, true); + + return m_cache; +} + +QList QDocumentLineHandle::decorations() const +{ + if ( !hasFlag(QDocumentLine::FormatsApplied) ) + compose(); + + // turning format "map" into ranges that QTextLayout can understand... + QList m_ranges; + + int fid = 0; + QTextLayout::FormatRange r; + r.start = r.length = -1; + + int i = 0; + + //if ( m_cache.isEmpty() ) + // qWarning("empty cache..."); + + while ( i < m_cache.count() ) + { + while ( (i < m_cache.count()) && !m_cache[i] ) + ++i; + + if ( i >= m_cache.count() ) + break; + + fid = m_cache[i]; + + r.start = i; + r.format = m_doc->formatScheme()->format(fid).toTextCharFormat(); + + while ( (i < m_cache.count()) && (m_cache[i] == fid) ) + ++i; + + if ( i >= m_cache.count() ) + break; + + r.length = i - r.start; + m_ranges << r; + + r.start = r.length = -1; + } + + if ( r.start != -1 ) + { + r.length = m_cache.count() - r.start; + m_ranges << r; + } + + return m_ranges; +} + +void QDocumentLineHandle::applyOverlays() const +{ + if ( !m_layout ) + return; + + //m_layout->setAdditionalFormats(decorations()); + + //setFlag(QDocumentLine::FormatsApplied, true); +} + +void QDocumentLineHandle::layout() const +{ + bool needLayout = false; + static QList m_layoutRequirements = QList() + << QChar::DirR + << QChar::DirAL + << QChar::DirRLE + << QChar::DirRLO + << QChar::DirPDF + << QChar::DirAN; + + for ( int i = 0; (i < m_text.length()) && !needLayout; ++i ) + { + QChar c = m_text.at(i); + + needLayout = m_layoutRequirements.contains(c.direction()); + } + + if ( needLayout ) + { + //qDebug("layout needed at line %i", this->line()); + + if ( !m_layout ) + { + m_layout = new QTextLayout(m_text, QDocument::font()); + } else { + m_layout->setText(m_text); + //m_layout->setFont(config()->font()); + } + + m_layout->setCacheEnabled(false); + // Initial setup of the QTextLayout. + + // Tab width + QTextOption opt; + opt.setFlags(QTextOption::IncludeTrailingSpaces); + opt.setTabStop(m_doc->tabStop() * QDocumentPrivate::m_spaceWidth); + + //opt.setWrapMode(QTextOption::NoWrap); + opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + + // Find the first strong character in the string. + // If it is an RTL character, set the base layout direction of the string to RTL. + // + // See http://www.unicode.org/reports/tr9/#The_Paragraph_Level (Sections P2 & P3). + // Qt's text renderer ("scribe") version 4.2 assumes a "higher-level protocol" + // (such as KatePart) will specify the paragraph level, so it does not apply P2 & P3 + // by itself. If this ever change in Qt, the next code block could be removed. + if ( m_text.isRightToLeft() ) + opt.setTextDirection(Qt::RightToLeft); + + m_layout->setTextOption(opt); + + // Syntax highlighting, inbuilt and arbitrary + //m_layout->setAdditionalFormats(m_ranges); + + // Begin layouting + m_layout->beginLayout(); + + m_frontiers.clear(); + + int i = 0, rx = 0, height = 0, minwidth = 0; + + forever + { + QTextLine line = m_layout->createLine(); + + if ( !line.isValid() ) + { + //if ( m_layout->lineCount() > 1 ) + // qWarning("Troubles expected on line %i", this->line()); + + break; + } + + if ( m_doc->widthConstraint() ) + line.setLineWidth(m_doc->widthConstraint() - QDocumentPrivate::m_leftMargin); + else + line.setNumColumns(m_text.length()); + + rx += qRound(line.naturalTextWidth()); //qRound(line.cursorToX(line.textLength())); + + if ( m_doc->impl()->m_constrained && m_layout->textOption().textDirection() == Qt::RightToLeft ) + { + line.setPosition(QPoint(qRound(qreal(m_doc->widthConstraint() - 2 * QDocumentPrivate::m_leftMargin) - line.naturalTextWidth()), height)); + } else { + line.setPosition(QPoint(minwidth, height)); + + if ( !i ) + { + m_indent = minwidth = cursorToX(nextNonSpaceChar(0)) - QDocumentPrivate::m_leftMargin; + + if ( minwidth < 0 || minwidth >= m_doc->widthConstraint() ) + minwidth = 0; + } + } + + m_frontiers << qMakePair(line.textStart() + line.textLength(), rx); + + ++i; + + height += QDocumentPrivate::m_lineSpacing; + //height += QDocument::fontMetrics().height(); + } + + m_frontiers.pop_back(); + + m_layout->endLayout(); + } else { + if ( m_layout ) + delete m_layout; + + m_layout = 0; + //updateWrap(); + } + + setFlag(QDocumentLine::LayoutDirty, false); +} + +struct RenderRange +{ + int position; + int length; + quint16 format; + int wrap; +}; + +void QDocumentLineHandle::draw( QPainter *p, + int xOffset, + int vWidth, + const QSmallArray& sel, + const QSmallArray& cursor, + const QPalette& pal, + bool fullSel) const +{ + if ( hasFlag(QDocumentLine::LayoutDirty) ) + layout(); + + if ( m_layout && !hasFlag(QDocumentLine::FormatsApplied) ) + m_layout->setAdditionalFormats(decorations()); + + QMediumArray m_composited = compose(); + + if ( m_layout ) + { + //if ( !hasFlag(QDocumentLine::FormatsApplied) ) + // applyOverlays(); + + const int lh = QDocument::fontMetrics().height(); + + QVector selections; + + QTextCharFormat fmt; + fmt.setBackground(pal.highlight()); + fmt.setForeground(pal.highlightedText()); + + QTextLayout::FormatRange range; + if ( fullSel ) + { + range.start = 0; + range.format = fmt; + range.length = m_text.length(); + selections << range; + } else { + for ( int i = 0; i < sel.count(); ++i ) + { + range.start = sel[i]; + range.format = fmt; + + if ( (i + 1) < sel.count() ) + { + // regular selection subset + range.length = sel[++i] - range.start; + + } else if ( m_layout->lineCount() ) { + // span to end of line, not only text + range.length = m_text.length() - range.start; + qreal lineWidth = m_layout->lineAt(m_layout->lineCount() - 1).naturalTextWidth(); + const int endX = QDocumentPrivate::m_leftMargin + qRound(lineWidth) - xOffset; + + QRect area(endX, lh * i, vWidth - endX, lh); + + p->fillRect(area, fmt.background()); + } + + selections << range; + } + } + + QPoint off(QDocumentPrivate::m_leftMargin, 0); + + m_layout->draw(p, off, selections); + + for ( int i = 0; i < cursor.count(); ++i ) + m_layout->drawCursor(p, off, cursor[i]); + + //m_layout->clearAdditionalFormats(); + } else if ( m_text.isEmpty() ) { + // enforce selection drawing on empty lines + if ( sel.count() == 1 ) + p->fillRect( + qMax(xOffset, QDocumentPrivate::m_leftMargin), + 0, + vWidth, + QDocumentPrivate::m_lineSpacing, + pal.highlight() + ); + + // enforce cursor drawing on empty lines + if ( cursor.count() && (xOffset < QDocumentPrivate::m_leftMargin) ) + p->drawLine( + QDocumentPrivate::m_leftMargin, + 0, + QDocumentPrivate::m_leftMargin, + QDocumentPrivate::m_lineSpacing + ); + + } else if ( true ) { + + QVector merged; + merged.fill(0, m_text.count()); + + QList ranges; + + // find start of trailing whitespaces + int last = m_text.length(); + + while ( (last > 0) && m_text.at(last - 1).isSpace() ) + --last; + + // TODO : format-level (not format id) merging of formats and opverlays... + + // merge selection ranges with the rest (formats + overlays) + if ( true ) //m_composited.count() || sel.count() ) + { + //int max = qMin(m_text.count(), m_composited.count()); + + for ( int i = 0; i < m_text.count(); ++i ) + { + if ( m_composited.count() > i ) + merged[i] = m_composited.at(i); + + // separate spaces to ease rendering loop + if ( m_text.at(i).isSpace() ) + merged[i] |= 0x4000; + } + + for ( int i = 0; i < sel.count(); i += 2 ) + { + int max = m_text.length(); + + if ( (i + 1) < sel.count() ) + max = qMin(sel[i + 1], max); + + for ( int j = sel[i]; j < max; ++j ) + merged[j] |= 0x8000; + } + } + + // generate render ranges + if ( merged.count() ) + { + int i = 0, wrap = 0, max = m_text.count(), + frontier = m_frontiers.count() ? m_frontiers.first().first : max; + + while ( i < max ) + { + RenderRange r; + r.position = i; + r.length = 1; + r.wrap = wrap; + r.format = merged.at(i); + + while ( ((i + 1) < frontier) && (merged.at(i + 1) == r.format) ) + { + ++r.length; + ++i; + } + + ranges << r; + ++i; + + if ( i == frontier ) + { + ++wrap; + frontier = wrap < m_frontiers.count() ? m_frontiers.at(wrap).first : max; + } + } + } else if ( m_frontiers.count() ) { + // no formatting (nor selection) : simpler + int i = 0, wrap = 0, max = m_text.count(), + frontier = m_frontiers.count() ? m_frontiers.first().first : max; + + while ( i < max ) + { + RenderRange r; + r.position = i; + r.length = 1; + r.wrap = wrap; + r.format = fullSel ? 0x8000 : 0; + + while ( ((i + 1) < frontier) ) + { + ++r.length; + ++i; + } + + ranges << r; + ++i; + + if ( i == frontier ) + { + ++wrap; + frontier = wrap < m_frontiers.count() ? m_frontiers.at(wrap).first : max; + } + } + } else { + // neither formatting nor line wrapping : simple drawText() + RenderRange r; + r.position = 0; + r.length = m_text.length(); + r.format = fullSel ? 0x8000 : 0; + + ranges << r; + } + + quint16 fmt = fullSel ? 0x8000 : 0; + QDocumentPrivate *d = m_doc->impl(); + const int ts = d->m_tabStop; + const int maxWidth = xOffset + vWidth; + const bool unbounded = sel.count() & 1; + const QColor ht = pal.highlightedText().color(); + + const bool showTabs = QDocument::showSpaces() & QDocument::ShowTabs, + showLeading = QDocument::showSpaces() & QDocument::ShowLeading, + showTrailing = QDocument::showSpaces() & QDocument::ShowTrailing; + + //const int fns = nextNonSpaceChar(0); + int indent = qMax(m_indent, QDocumentPrivate::m_leftMargin); + + int cidx = 0; + int rngIdx = 0; + int column = 0; + bool continuingWave = false, brokenWave = false; + int dir = 0; // 0 = down; 1 = up + int wrap = 0, xpos = QDocumentPrivate::m_leftMargin, ypos = 0; + bool leading = ranges.first().format & 0x4000, pastLead = false; + + foreach ( const RenderRange& r, ranges ) + { + ++rngIdx; + + if ( wrap != r.wrap ) + { + continuingWave = false; + + if ( fmt & 0x8000 ) + { + // finish selection + p->fillRect( + xpos, ypos, + maxWidth - xpos, QDocumentPrivate::m_lineSpacing, + pal.highlight() + ); + + } + + if ( pastLead && (r.format & 0x4000) ) + { + indent = QDocumentPrivate::m_leftMargin; + } + + ++wrap; + column = 0; + ypos += QDocumentPrivate::m_lineSpacing; + xpos = indent; + + if ( r.format & 0x8000 ) + { + // finish selection + p->fillRect( + QDocumentPrivate::m_leftMargin, ypos, + xpos, QDocumentPrivate::m_lineSpacing, + pal.highlight() + ); + + } + } + if ( leading && !(r.format & 0x4000) ) + { + //indent = xpos; + leading = false; + pastLead = true; + } + + // TODO : clip more accurately (i.e inside ranges) + if ( xpos > maxWidth ) + break; + + if ( r.format != fmt ) + { + d->tunePainter(p, r.format & 0x0fff); + + fmt = r.format; + } + + int rwidth = 0; + int tcol = column; + const QString rng = m_text.mid(r.position, r.length); + + if ( r.format & 0x4000 ) + { + foreach ( QChar c, rng ) + { + if ( c.unicode() == '\t' ) + { + int toff = ts - (tcol % ts); + rwidth += toff * QDocumentPrivate::m_spaceWidth; + tcol += toff; + } else { + rwidth += QDocumentPrivate::m_spaceWidth; + ++tcol; + } + } + } else { + column += r.length; + + if ( QDocumentPrivate::m_fixedPitch ) + rwidth = QDocumentPrivate::m_spaceWidth * r.length; + else + rwidth = p->fontMetrics().width(rng); + } + + if ( (xpos + rwidth) <= xOffset ) + { + xpos += rwidth; + + if ( r.format & 0x4000 ) + column = tcol; + + continue; + } + + QFormat format; + int xspos = xpos; + const QPen oldpen = p->pen(); + const int baseline = ypos + QDocumentPrivate::m_ascent; + + if ( d->m_formatScheme ) + format = d->m_formatScheme->format(fmt & 0x0fff); + + if ( fullSel || (fmt & 0x8000) ) + { + p->setPen(ht); + + p->fillRect(xpos, ypos, + rwidth, QDocumentPrivate::m_lineSpacing, + pal.highlight()); + + } else { + if ( format.foreground.isValid() ) + { + p->setPen(format.foreground); + } else { + p->setBrush(pal.text()); + } + + if ( format.background.isValid() ) + { + p->fillRect(xpos, ypos, + rwidth, QDocumentPrivate::m_lineSpacing, + format.background); + } + } + + if ( r.format & 0x4000 ) + { + // spaces + int max = r.position + r.length; + + if ( rngIdx == ranges.count() ) + ++max; + + for ( int i = r.position; i < max; ++i ) + { + while ( cidx < cursor.count() ) + { + if ( cursor.at(cidx) == i ) + { + p->drawLine(xpos, ypos, xpos, ypos + QDocumentPrivate::m_lineSpacing); + + ++cidx; + } else { + break; + } + } + + if ( i == r.position + r.length ) + break; + + bool isTab = m_text.at(i).unicode() == '\t'; + + if ( isTab ) + { + int toff = ts - (column % ts); + column += toff; + int xoff = toff * QDocumentPrivate::m_spaceWidth; + /* + if ( r.format & 0x8000 ) + { + p->fillRect(xpos, ypos, + xoff, QDocumentPrivate::m_lineSpacing, + pal.highlight()); + + } + */ + if ( showTabs ) + { + p->translate(xpos - m_spaceSignOffset, ypos + QDocumentPrivate::m_ascent); + p->drawPoints(m_spaceSign, sizeof(m_spaceSign) / sizeof(QPoint)); + p->translate(m_spaceSignOffset - xpos,-ypos - QDocumentPrivate::m_ascent); + } + + xpos += xoff; + } else { + ++column; + /* + if ( r.format & 0x8000 ) + { + p->fillRect(xpos, ypos, + QDocumentPrivate::m_spaceWidth, QDocumentPrivate::m_lineSpacing, + pal.highlight()); + + } + */ + if ( + ( + leading + && + showLeading + ) + || + ( + (r.position >= last) + && + showTrailing + ) + ) + { + p->translate(xpos - m_spaceSignOffset, ypos + QDocumentPrivate::m_ascent); + p->drawPoints(m_spaceSign, sizeof(m_spaceSign) / sizeof(QPoint)); + p->translate(m_spaceSignOffset - xpos,-ypos - QDocumentPrivate::m_ascent); + } + + xpos += QDocumentPrivate::m_spaceWidth; + } + } + + /* + if ( leading ) + { + //indent = xpos; + leading = false; + pastLead = true; + } + */ + + } else { + p->drawText(xpos, baseline, rng); + + while ( cidx < cursor.count() ) + { + const int xcoff = cursor.at(cidx) - r.position; + + if ( xcoff < 0 ) + { + ++cidx; + continue; + } + + if ( xcoff < (rngIdx == ranges.count() ? r.length + 1 : r.length) ) + { + int xcpos = xpos; + + if ( xcoff ) + { + if ( QDocumentPrivate::m_fixedPitch ) + { + xcpos += xcoff * QDocumentPrivate::m_spaceWidth; + } else { + xcpos += p->fontMetrics().width(rng.left(xcoff)); + } + } + + //qDebug("drawing cursor %i (col %i, x=%i)", cidx, cursor.at(cidx), xcpos); + + p->drawLine(xcpos, ypos, xcpos, ypos + QDocumentPrivate::m_lineSpacing); + + ++cidx; + } else { + break; + } + } + + xpos += rwidth; + } + + //qDebug("underline pos : %i", p->fontMetrics().underlinePos()); + + if ( format.linescolor.isValid() ) + p->setPen(format.linescolor); + + const int ydo = qMin(baseline + p->fontMetrics().underlinePos(), ypos + QDocumentPrivate::m_lineSpacing - 1); + const int yin = baseline - p->fontMetrics().strikeOutPos(); + const int yup = qMax(baseline - p->fontMetrics().overlinePos() + 1, ypos); + + if ( format.overline ) + { + p->drawLine(xspos, yup, xpos, yup); + } + + if ( format.strikeout ) + { + p->drawLine(xspos, yin, xpos, yin); + } + + if ( format.underline ) + { + p->drawLine(xspos, ydo, xpos, ydo); + } + + if ( format.waveUnderline ) + { + /* + those goddamn font makers take liberties with common sense + and make it so damn harder to figure proper y offset for wave + underline (keeping the regular underline pos make it look + weird or even invisible on some fonts... reference rendering + with DejaVu Sans Mono) + */ + + // we used fixed wave amplitude of 3 (well strictly speaking + // amplitude would be 1.5 but let's forget about waves and + // focus on getting that code to work... + + // gotta center things + const int ycenter = ypos + QDocumentPrivate::m_lineSpacing - 3; + /* + qMin( + ypos + (QDocumentPrivate::m_ascent + QDocumentPrivate::m_lineSpacing) / 2, + ypos + QDocumentPrivate::m_lineSpacing - 3 + );*/ + + //if (format.waveUnderlineForeground.isValid()) + // p->setPen(format.waveUnderlineForeground); + + int cp = 0; + brokenWave = false; + + while ( cp < rwidth ) + { + if ( !cp && !continuingWave ) + { + dir = 0; + p->drawLine(xspos, ycenter, xspos + 1, ycenter + 1); + ++cp; + } else if ( !cp && brokenWave ) { + if ( !dir ) + p->drawLine(xspos, ycenter, xspos + 1, ycenter + 1); + else + p->drawLine(xspos, ycenter, xspos + 1, ycenter - 1); + + } else { + if ( cp + 2 > rwidth) + { + if ( !dir ) + p->drawLine(xspos + cp, ycenter - 1, xspos + cp + 1, ycenter); + else + p->drawLine(xspos + cp, ycenter + 1, xspos + cp + 1, ycenter); + + // trick to keep current direction + dir ^= 1; + + brokenWave = true; + ++cp; + } else { + if ( !dir ) + p->drawLine(xspos + cp, ycenter - 1, xspos + cp + 2, ycenter + 1); + else + p->drawLine(xspos + cp, ycenter + 1, xspos + cp + 2, ycenter - 1); + + cp += 2; + } + } + + dir ^= 1; + } + + continuingWave = true; + } else { + continuingWave = false; + dir = 0; + } + + p->setPen(oldpen); + } + + if ( unbounded ) + p->fillRect( + xpos, ypos, + maxWidth - xpos, QDocumentPrivate::m_lineSpacing, + pal.highlight() + ); + + } else { + QChar c; + + int maxWidth = xOffset + vWidth; + + const bool wrapped = m_frontiers.count(); + + int fmt = 0; + + bool leading = true, unbounded = false, leftSel = false, + showTabs = QDocument::showSpaces() & QDocument::ShowTabs, + showLeading = QDocument::showSpaces() & QDocument::ShowLeading, + showTrailing = QDocument::showSpaces() & QDocument::ShowTrailing; + + int cwidth, + ypos = 0, + indent = 0, + idx = 0, column = 0, + selidx = 0, sellen = 0, wrap = 0, + xpos = QDocumentPrivate::m_leftMargin; + + const int ts = QDocument::tabStop(); + + // find start of trailing whitespaces + int last = m_text.length(); + + while ( (last > 0) && m_text.at(last - 1).isSpace() ) + --last; + + //p->save(); + + //qDebug("drawing from %i to %i", xpos, maxWidth); + + while ( idx < m_text.length() ) + { + if ( (selidx < sel.count()) && (idx == sel[selidx]) ) + { + if ( (selidx + 1) < sel.count() ) + { + sellen = sel[selidx + 1] - sel[selidx]; + selidx += 2; + } else { + // unbounded selection + unbounded = true; + + ++selidx; + sellen = m_text.length() - idx; + + p->fillRect( + xpos, ypos, + maxWidth - xpos, QDocumentPrivate::m_lineSpacing, + pal.highlight() + ); + } + } + + c = m_text.at(idx); + + if ( c == '\t' ) + { + int toff = ts - (column % ts); + cwidth = toff * QDocumentPrivate::m_spaceWidth; + column += toff - 1; + } else if ( c == ' ' ) { + cwidth = QDocumentPrivate::m_spaceWidth; + } else if ( idx < m_composited.count() ) { + int nfmt = m_composited[idx]; + + if ( fmt != nfmt ) + { + m_doc->impl()->tunePainter(p, nfmt); + fmt = nfmt; + } + + // make sure bold/italicized/.. chars are taken into account... + cwidth = p->fontMetrics().width(c); + } else { + // char uses default font... + cwidth = QDocumentPrivate::m_fontMetrics->width(c); + } + + if ( (xpos + cwidth) > xOffset ) + { + // MUST be done after setting the proper chararcter width! + if ( wrapped && (wrap < m_frontiers.count()) && (idx >= m_frontiers.at(wrap).first) ) + { + if ( sellen || leftSel ) + { + p->fillRect( + xpos, ypos, + maxWidth - xpos, QDocumentPrivate::m_lineSpacing, + pal.highlight() + ); + + } + + ++wrap; + xpos = indent; + ypos += QDocumentPrivate::m_lineSpacing; + + if ( sellen || leftSel ) + { + p->fillRect( + QDocumentPrivate::m_leftMargin, ypos, + xpos - QDocumentPrivate::m_leftMargin + (leftSel ? 0 : cwidth), + QDocumentPrivate::m_lineSpacing, + pal.highlight() + ); + + } + + } else { + if ( sellen ) + { + p->fillRect( + xpos, ypos, + cwidth, QDocumentPrivate::m_lineSpacing, + pal.highlight() + ); + + } + } + + const int baseline = ypos + QDocumentPrivate::m_ascent; + + if ( !c.isSpace() ) + { + if ( leading ) + indent = xpos; + + leading = false; + + if ( fullSel || sellen ) + p->setPen(pal.highlightedText().color()); + + p->drawText(xpos, baseline, QString(c)); + } else if ( + ( + (c == ' ') + && + ( + ( + leading + && + showLeading + ) + || + ( + (idx > last) + && + showTrailing + ) + ) + ) + || + ( + (c == '\t') + && + showTabs + ) + ) + { + //p->drawText(xpos - m_spaceSignOffset, ypos + QDocumentPrivate::m_ascent, QChar(0x231E)); + + p->translate(xpos - m_spaceSignOffset, ypos + QDocumentPrivate::m_ascent); + p->drawPoints(m_spaceSign, sizeof(m_spaceSign) / sizeof(QPoint)); + p->translate(m_spaceSignOffset - xpos,-ypos - QDocumentPrivate::m_ascent); + } + + for ( int cidx = 0; cidx < cursor.count(); ++cidx ) + { + if ( cursor[cidx] == idx ) + p->drawLine(xpos, ypos, xpos, ypos + QDocumentPrivate::m_lineSpacing); + } + + if ( idx < m_composited.count() ) + { + QFormat format = m_doc->impl()->m_formatScheme->format(m_composited[idx]); + + //qDebug("underline pos : %i", p->fontMetrics().underlinePos()); + + const int ydo = baseline + p->fontMetrics().underlinePos(); + const int yin = baseline - p->fontMetrics().strikeOutPos(); + const int yup = baseline - p->fontMetrics().overlinePos(); + + if ( format.overline ) + { + p->drawLine(xpos, yup, xpos + cwidth, yup); + } + + if ( format.strikeout ) + { + p->drawLine(xpos, yin, xpos + cwidth, yin); + } + + if ( format.underline ) + { + p->drawLine(xpos, ydo, xpos + cwidth, ydo); + } + + if ( format.waveUnderline ) + { + QPen oldpen = p->pen(); + p->setPen(QColor(255, 0, 0)); + + p->drawLine(xpos, ydo, + xpos + cwidth / 2 , ypos + QDocumentPrivate::m_lineSpacing - 1); + + p->drawLine(xpos + cwidth / 2, ypos + QDocumentPrivate::m_lineSpacing - 1, + xpos + cwidth, ydo); + + p->setPen(oldpen); + } + } + } else { + if ( wrapped && (wrap < m_frontiers.count()) && (idx >= m_frontiers.at(wrap).first) ) + { + ++wrap; + xpos = indent; + ypos += QDocumentPrivate::m_lineSpacing; + } + + if ( !c.isSpace() ) + { + if ( leading ) + indent = xpos; + + leading = false; + } + } + + if ( sellen ) + leftSel = !(--sellen); + else + leftSel = false; + + if ( leftSel ) + { + p->setPen(pal.text().color()); + fmt = 0; + } + + xpos += cwidth; + ++column; + ++idx; + + if ( !wrapped && xpos > maxWidth ) + break; + } + + // ensure drawing of cursor at EOL + for ( int cidx = 0; cidx < cursor.count(); ++cidx ) + { + if ( cursor[cidx] == idx ) + p->drawLine(xpos, ypos, xpos, ypos + QDocumentPrivate::m_lineSpacing); + } + + if ( wrapped && unbounded ) + p->fillRect( + xpos, ypos, + maxWidth - xpos, QDocumentPrivate::m_lineSpacing, + pal.highlight() + ); + + } +} + +////////////////// + + +///////////////////////// +// QDocumentCursorHandle +///////////////////////// +QDocumentCursorHandle::QDocumentCursorHandle(QDocument *d, int line) + : m_flags(ColumnMemory), m_doc(d), + #if QT_VERSION >= 0x040400 + m_ref(0), + #endif + m_begOffset(0), m_endOffset(0), m_max(0), m_begLine(line), m_endLine(-1) +{ + #if QT_VERSION < 0x040400 + m_ref.init(0); + #endif + + //m_blocks.push(0); + //qDebug("Cursor handle created : 0x%x", this); +} + +QDocumentCursorHandle::~QDocumentCursorHandle() +{ + //qDebug("Cursor handle deleted : 0x%x", this); + Q_ASSERT(!m_ref); + + QDocumentCommand::disableAutoUpdate(this); +} + +void QDocumentCursorHandle::copy(const QDocumentCursorHandle *c) +{ + if ( !c ) + return; + + m_begLine = c->m_begLine; + m_begOffset = c->m_begOffset; + m_endLine = c->m_endLine; + m_endOffset = c->m_endOffset; + m_flags = c->m_flags; + m_max = c->m_max; +} + +QDocument* QDocumentCursorHandle::document() const +{ + return m_doc; +} + +QDocumentCursorHandle* QDocumentCursorHandle::clone() const +{ + QDocumentCursorHandle *c = new QDocumentCursorHandle(m_doc); + c->copy(this); + + c->setAutoUpdated(isAutoUpdated()); + + return c; +} + +bool QDocumentCursorHandle::atEnd() const +{ + if ( !m_doc ) + return true; + + bool atLineEnd; + QDocumentLine l = m_doc->line(m_begLine); + + //qDebug("Cursor handle : 0x%x->atEnd() => 0x%x", this, m_begLine.handle()); + + if ( l.isValid() ) + { + atLineEnd = m_begOffset == l.length(); + l = m_doc->line(m_begLine + 1); + } else { + //qWarning("Invalid cursor..."); + return true; + } + + return l.isNull() && atLineEnd; +} + +bool QDocumentCursorHandle::atStart() const +{ + if ( !m_doc ) + return true; + + QDocumentLine l = m_doc->line(m_begLine - 1); + + return l.isNull() && !m_begOffset; +} + +bool QDocumentCursorHandle::atBlockEnd() const +{ + return atLineEnd(); +} + +bool QDocumentCursorHandle::atBlockStart() const +{ + return atLineStart(); +} + +bool QDocumentCursorHandle::atLineEnd() const +{ + if ( !m_doc ) + return true; + + QDocumentLine l = m_doc->line(m_begLine); + + return l.isValid() ? l.length() == m_begOffset : false; +} + +bool QDocumentCursorHandle::atLineStart() const +{ + if ( !m_doc ) + return true; + + QDocumentLine l = m_doc->line(m_begLine); + + return l.isValid() ? !m_begOffset : false; +} + +bool QDocumentCursorHandle::hasSelection() const +{ + if ( !m_doc ) + return false; + + QDocumentLine l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine); + + return l1.isValid() && l2.isValid(); +} + +bool QDocumentCursorHandle::isSilent() const +{ + return hasFlag(Silent); +} + +void QDocumentCursorHandle::setSilent(bool y) +{ + if ( y ) + setFlag(Silent); + else + clearFlag(Silent); +} + +bool QDocumentCursorHandle::isAutoUpdated() const +{ + return QDocumentCommand::isAutoUpdated(this); +} + +void QDocumentCursorHandle::setAutoUpdated(bool y) +{ + if ( y ) + QDocumentCommand::enableAutoUpdate(this); + else + QDocumentCommand::disableAutoUpdate(this); +} + +QDocumentLine QDocumentCursorHandle::line() const +{ + if ( !m_doc ) + return QDocumentLine(); + + return m_doc->line(m_begLine); +} + +QDocumentLine QDocumentCursorHandle::anchorLine() const +{ + if ( !m_doc ) + return QDocumentLine(); + + return m_endLine != -1 ? m_doc->line(m_endLine) : line(); +} + +int QDocumentCursorHandle::lineNumber() const +{ + return m_begLine; +} + +int QDocumentCursorHandle::anchorLineNumber() const +{ + return m_endLine != -1 ? m_endLine : m_begLine; +} + +int QDocumentCursorHandle::anchorColumnNumber() const +{ + if ( !m_doc ) + return -1; + + return m_doc->line(m_endLine).isValid() ? m_endOffset : m_begOffset; +} + +int QDocumentCursorHandle::visualColumnNumber() const +{ + return QDocument::screenLength( + line().text().constData(), + m_begOffset, + QDocument::tabStop() + ); + +} + +int QDocumentCursorHandle::columnNumber() const +{ + return m_begOffset; +} + +void QDocumentCursorHandle::setColumnNumber(int c, int m) +{ + if ( !m_doc ) + return; + + QDocumentLine l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine); + + if ( m & QDocumentCursor::KeepAnchor ) + { + if ( l2.isNull() ) + { + m_endLine = m_begLine; + m_endOffset = m_begOffset; + } + + m_begOffset = c; //qBound(0, c, l1.length()); + } else { + m_endLine = -1; + m_endOffset = 0; + m_begOffset = c; //qBound(0, c, l1.length()); + } + + refreshColumnMemory(); +} + +QPoint QDocumentCursorHandle::documentPosition() const +{ + if ( !m_doc ) + return QPoint(); + + return QPoint(0, m_doc->y(m_begLine)) + m_doc->line(m_begLine).cursorToDocumentOffset(m_begOffset); +} + +QPoint QDocumentCursorHandle::anchorDocumentPosition() const +{ + if ( !m_doc ) + return QPoint(); + + if ( m_endLine < 0 || m_endOffset < 0 ) + return documentPosition(); + + return QPoint(0, m_doc->y(m_endLine)) + m_doc->line(m_endLine).cursorToDocumentOffset(m_endOffset); +} + +QPolygon QDocumentCursorHandle::documentRegion() const +{ + QPolygon poly; + QPoint p = documentPosition(), ap = anchorDocumentPosition(); + + int w = m_doc->width(); + const int lm = m_doc->impl()->m_leftMargin; + const int ls = m_doc->impl()->m_lineSpacing - 1; + + if ( p == ap ) + { + poly + << p + << QPoint(p.x() + 1, p.y()) + << QPoint(p.x() + 1, p.y() + ls) + << QPoint(p.x(), p.y() + ls); + } else if ( p.y() == ap.y() ) { + poly + << p + << ap + << QPoint(ap.x(), ap.y() + ls) + << QPoint(p.x(), p.y() + ls); + } else if ( p.y() < ap.y() ) { + poly + << p + << QPoint(w, p.y()); + + if ( ap.x() < w ) + poly << QPoint(w, ap.y()) << ap; + + poly + << QPoint(ap.x(), ap.y() + ls) + << QPoint(lm, ap.y() + ls) + << QPoint(lm, p.y() + ls); + + if ( p.x() > lm ) + poly << QPoint(p.x(), p.y() + ls); + } else { + poly + << ap + << QPoint(w, ap.y()); + + if ( p.x() < w ) + poly << QPoint(w, p.y()) << p; + + poly + << QPoint(p.x(), p.y() + ls) + << QPoint(lm, p.y() + ls) + << QPoint(lm, ap.y() + ls); + + if ( ap.x() > lm ) + poly << QPoint(ap.x(), ap.y() + ls); + } + + return poly; +} + +int QDocumentCursorHandle::position() const +{ + if ( !m_doc ) + return -1; + + int pos = m_doc->line(m_begLine).position(); + + return (pos != -1) ? pos + m_begOffset : pos; +} + +void QDocumentCursorHandle::shift(int offset) +{ + if ( !m_doc ) + return; + + QDocumentLine l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine); + + if ( l1.isValid() ) + m_begOffset = qBound(0, m_begOffset + offset, l1.length()); + + if ( l2.isValid() ) + m_endOffset = qBound(0, m_endOffset + offset, l2.length()); +} + +void QDocumentCursorHandle::refreshColumnMemory() +{ + //m_max = m_doc->line(line).cursorToX(offset); + m_max = hasFlag(ColumnMemory) ? m_doc->line(m_begLine).cursorToDocumentOffset(m_begOffset).x() : 0; +} + +bool QDocumentCursorHandle::hasColumnMemory() const +{ + return hasFlag(ColumnMemory); +} + +void QDocumentCursorHandle::setColumnMemory(bool y) +{ + if ( y ) + setFlag(ColumnMemory); + else + clearFlag(ColumnMemory); +} + +void QDocumentCursorHandle::setPosition(int pos, int m) +{ + Q_UNUSED(pos) + Q_UNUSED(m) + + qWarning("Set position to cursor using character index : forbidden..."); + /* + if ( m == QDocumentCursor::MoveAnchor ) + { + m_begLine = m_doc->findLine(pos); + m_begOffset = (m_begLine.isValid() ? pos : 0); + + m_endLine = QDocumentLine(); + m_endOffset = 0; + + m_max = m_begLine.cursorToX(m_begOffset); + } else { + m_endLine = m_doc->findLine(pos); + m_endOffset = (m_begLine.isValid() ? pos : 0); + + //m_max = 0; + } + */ +} + +bool QDocumentCursorHandle::movePosition(int count, int op, int m) +{ + if ( !m_doc ) + return false; + + QDocumentLine l, l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine); + + int &line = m_begLine; + int &offset = m_begOffset; + static QRegExp wordStart("\\b\\w+$"), wordEnd("^\\w+\\b"); + + if ( !(m & QDocumentCursor::KeepAnchor) ) + { + m_endLine = -1; + m_endOffset = 0; + } else if ( !l2.isValid() ) { + m_endLine = m_begLine; + m_endOffset = m_begOffset; + } + + int beg = 0, end = m_doc->lines(); + + switch ( op ) + { + case QDocumentCursor::Left : + { + if ( atStart() ) + return false; + + int remaining = offset; + + do + { + if ( remaining >= count ) + { + offset = remaining - count; + break; + } else if ( line == beg ) { + offset = 0; + break; + } + + do + { + --line; + } while ( (line > beg) && m_doc->line(line).hasFlag(QDocumentLine::Hidden) ); + + //*line = *it; + + count -= remaining + 1; // jumping a line is one char + offset = remaining = m_doc->line(line).length(); + } while ( count && remaining ); + + refreshColumnMemory(); + + break; + } + + case QDocumentCursor::Right : + { + if ( atEnd() ) + return false; + + int remaining = m_doc->line(line).length() - offset; + + do + { + if ( remaining >= count ) + { + offset += count; + break; + } else if ( (line + 1) == end ) { + offset = remaining; + break; + } + + do + { + ++line; + } while ( ((line+1) < end) && m_doc->line(line).hasFlag(QDocumentLine::Hidden) ); + + //*line = *it; + + offset = 0; + count -= remaining + 1; + remaining = m_doc->line(line).length(); + } while ( count && remaining ); + + refreshColumnMemory(); + + break; + } + + case QDocumentCursor::Up : + { + if ( atStart() ) + return false; + + //qDebug("%i, %i : up", line, offset); + + if ( m & QDocumentCursor::ThroughWrap ) + { + QPoint p = documentPosition(); + + if ( hasColumnMemory() ) + p.rx() = qMax(p.x(), m_max); + + p.ry() -= QDocumentPrivate::m_lineSpacing * count; + + if ( p.y() >= 0 ) + { + m_doc->cursorForDocumentPosition(p, line, offset); + } else { + line = 0; + offset = 0; + } + + return true; + } + + while ( count && (line > beg) ) + { + --line; + + if ( !m_doc->line(line).hasFlag(QDocumentLine::Hidden) ) + --count; + + } + + //*line = QDocumentLine(*it); + //*offset = line->xToCursor(qMin(line->cursorToX(*offset), m_max), 0); + l = m_doc->line(line); + + if ( count ) + offset = 0; + else if ( m == QDocumentCursor::MoveAnchor ) + offset = l.xToCursor( + qMax( + l.cursorToX( + qMin( + offset, + l.length() + ) + ), + m_max + ) + ); + else + offset = qMin(l.length(), offset); + + break; + } + + case QDocumentCursor::Down : + { + if ( atEnd() ) + return false; + + if ( m & QDocumentCursor::ThroughWrap ) + { + QPoint p = documentPosition(); + + if ( hasColumnMemory() ) + p.rx() = qMax(p.x(), m_max); + + p.ry() += QDocumentPrivate::m_lineSpacing * count; + + int oldLine = line, oldCol = offset; + m_doc->cursorForDocumentPosition(p, line, offset); + if ( oldLine == line && oldCol == offset ) + offset = m_doc->line(line).length(); + return true; + } + + while ( count && ((line + 1) < end) ) + { + ++line; + + if ( !m_doc->line(line).hasFlag(QDocumentLine::Hidden) ) + --count; + + } + + //*line = QDocumentLine(*it); + l = m_doc->line(line); + + if ( count ) + offset = l.length(); + else if ( m == QDocumentCursor::MoveAnchor ) + offset = l.xToCursor( + qMax( + l.cursorToX( + qMin( + offset, + l.length() + ) + ), + m_max + ) + ); + else + offset = qMin(l.length(), offset); + + break; + } + + case QDocumentCursor::Start : + if ( atStart() ) + return false; + + m_max = offset = 0; + line = 0; //m_doc->line(0); + break; + + case QDocumentCursor::End : + if ( atEnd() ) + return false; + + line = end - 1; //QDocumentLine(*(m_doc->impl()->end() - 1)); + offset = m_doc->line(line).length(); + refreshColumnMemory(); + break; + + case QDocumentCursor::StartOfBlock : + if ( atBlockStart() ) + return false; + + m_max = offset = 0; + break; + + case QDocumentCursor::EndOfBlock : + if ( atBlockEnd() ) + return false; + + offset = m_doc->line(line).length(); + refreshColumnMemory(); + break; + + case QDocumentCursor::NextBlock : + + if ( atEnd() ) + return false; + + while ( ((line + 1) < end) && count ) + { + ++line; + + if ( !m_doc->line(line).hasFlag(QDocumentLine::Hidden) ) + --count; + + } + + if ( !count ) + { + //*line = *it; + offset = 0; + } else { + //*line = QDocumentLine(*(m_doc->impl()->end() - 1)); + offset = m_doc->line(line).length(); + } + + break; + + case QDocumentCursor::PreviousBlock : + + if ( atStart() ) + return false; + + offset = 0; + + while ( (line > beg) && count ) + { + --line; + + if ( !m_doc->line(line).hasFlag(QDocumentLine::Hidden) ) + --count; + + } + + if ( !count ) + { + //*line = *it; + offset = m_doc->line(line).length(); + } else { + offset = 0; + //*line = QDocumentLine(*(m_doc->impl()->begin())); + } + + //*line = *it; + + break; + + case QDocumentCursor::WordLeft : + case QDocumentCursor::PreviousWord : + { + if ( atStart() ) + return false; + + l = m_doc->line(line); + + //for ( int loop = 0; loop <= 1; ++loop ) + //{ + // while ( l.isValid() ) + + // -- patch -- + /* eats up white space */ + while ( (offset > 0) && !isWord(l.text().at(offset - 1)) ) + --offset; + + /* start of line */ + if ( offset == 0 ) + { + /* first line, first char => nothing to do */ + if( line == beg ) + return true; + + do + // -- patch -- + { +// //offset = qMin(offset, l.length() - 1); +// bool next = (l.length() && offset >= 0) ? isWord(l.text().at(offset)) : true; +// +// if ( loop ) +// next = !next; +// +// if ( !next ) +// break; +// +// if ( offset > 0 ) +// { +// --offset; +// } else if ( line != beg ) { +// do +// { +// //*line = *(--it); +// --line; +// l = m_doc->line(line); +// offset = l.length() - 1; +// } while ( l.isValid() && (line != beg) && l.hasFlag(QDocumentLine::Hidden) ); +// } else { +// break; +// } +// } +// } +// +// while ( l.isValid() ) +// { +// offset = qMin(offset, l.length()); +// bool next = (offset <= 0) ? false : isWord(l.text().at(offset - 1)); +// +// if ( !next ) +// break; +// +// --offset; + + // -- patch -- + --line; + l = m_doc->line(line); + offset = l.length(); + } while ( (line != beg) && l.isValid() && l.hasFlag(QDocumentLine::Hidden) ); + return true; + // -- patch -- + } + + // -- patch -- + /* eats up whole word */ + while ( (offset > 0) && isWord(l.text().at(offset - 1)) ) + --offset; + // -- patch -- + + refreshColumnMemory(); + + break; + } + + case QDocumentCursor::WordRight : + case QDocumentCursor::NextWord : + { + if ( atEnd() ) + return false; + + l = m_doc->line(line); + int lineLength = l.text().length(); + +// for ( int loop = 0; loop <= 1; ++loop ) + // -- patch -- + /* end of line */ + if ( offset == lineLength ) + { +// while ( l.isValid() ) + /* last line, last char => nothing to do */ + if ( line == end ) + return true; + // -- patch -- + do + { +// //offset = qBound(0, offset, l.length() - 1); +// bool next = (offset < l.length()) ? isWord(l.text().at(offset)) : true; +// +// if ( loop ) +// next = !next; +// +// if ( !next ) +// break; +// +// if ( offset < l.length() ) +// { +// ++offset; +// } else if ( (line + 1) != end ) { +// offset = 0; +// do +// { +// ++line; +// l = m_doc->line(line); +// } while ( l.isValid() && ((line + 1) != end) && (l.hasFlag(QDocumentLine::Hidden) || !l.length()) ); +// } else { + // -- patch -- + ++line; + l = m_doc->line(line); + offset = 0; + } while ( (line != end) && l.isValid() && l.hasFlag(QDocumentLine::Hidden) ); + + lineLength = l.text().length(); + /* empty line */ + if ( lineLength == 0 ) + return true; + + /* eats up white space */ + while ( !isWord(l.text().at(offset)) ) + { + ++offset; + /* move to end of line */ + if ( offset == lineLength ) + break; + // -- patch -- +// } + } + // -- patch -- + return true; + // -- patch -- + } + + // -- patch -- + /* next char */ + ++offset; + /* eats up whole word */ + while ( (offset < lineLength) && isWord(l.text().at(offset)) ) + ++offset; + + /* eats up white space */ + while ( (offset < lineLength) && !isWord(l.text().at(offset)) ) + ++offset; + // -- patch -- + + refreshColumnMemory(); + + break; + } + + case QDocumentCursor::StartOfWord : + { + int x = wordStart.indexIn(m_doc->line(line).text().left(offset)); + + if ( x != -1 ) + { + offset = x; + } else { + qDebug("failed to find SOW : %i + %i != %i", + x, wordStart.matchedLength(), offset); + + return false; + } + + refreshColumnMemory(); + + break; + } + + case QDocumentCursor::EndOfWord : + { + int x = wordEnd.indexIn(m_doc->line(line).text(), offset, QRegExp::CaretAtOffset); + + if ( x == offset ) + { + offset += wordEnd.matchedLength(); + } else { + qDebug("failed to find EOW"); + return false; + } + + refreshColumnMemory(); + + break; + } + + default: + qWarning("Unhandled move operation..."); + return false; + }; + + return true; +} + +void QDocumentCursorHandle::moveTo(const QDocumentCursor &c) +{ + if ( !c.isValid() || !m_doc ) + return; + + m_begLine = c.handle()->m_begLine; + m_begOffset = c.handle()->m_begOffset; + + m_endLine = -1; + m_endOffset = 0; + + refreshColumnMemory(); +} + +void QDocumentCursorHandle::moveTo(int line, int column) +{ + m_begLine = line; + m_begOffset = column; + + m_endLine = -1; + m_endOffset = 0; + + refreshColumnMemory(); +} + +void QDocumentCursorHandle::insertText(const QString& s, bool keepAnchor) +{ + if ( !m_doc || s.isEmpty() || m_doc->line(m_begLine).isNull() ) + return; + + bool sel = hasSelection(); + + if ( sel ) + { + beginEditBlock(); + removeSelectedText(keepAnchor); + } + + QDocumentCommand *command = new QDocumentInsertCommand( + m_begLine, + m_begOffset, + s, + m_doc + ); + + command->setKeepAnchor(keepAnchor); + command->setTargetCursor(this); + execute(command); + + if ( sel ) + endEditBlock(); +} + +void QDocumentCursorHandle::eraseLine() +{ + if ( !m_doc ) + return; + + QDocumentCommand *command = 0; + + if ( m_endLine == -1 ) + { + command = new QDocumentEraseCommand( + m_begLine, + 0, + m_begLine + 1, + 0, + m_doc + ); + } else { + command = new QDocumentEraseCommand( + qMin(m_begLine, m_endLine), + 0, + qMax(m_begLine, m_endLine) + 1, + 0, + m_doc + ); + } + + command->setTargetCursor(this); + execute(command); +} + +QChar QDocumentCursorHandle::nextChar() const +{ + if ( !m_doc ) + return QChar(); + + QDocumentLine l = m_doc->line(m_begLine); + + if ( !l.isValid() ) + return QChar(); + + return m_begOffset < l.length() ? l.text().at(m_begOffset) : QLatin1Char('\n'); +} + +QChar QDocumentCursorHandle::previousChar() const +{ + if ( !m_doc || (m_begLine <= 0 && m_begOffset <= 0) ) + return QChar(); + + QDocumentLine l = m_doc->line(m_begLine); + + if ( !l.isValid() || m_begOffset > l.length() ) + return QChar(); + + return m_begOffset ? l.text().at(m_begOffset - 1) : QLatin1Char('\n'); +} + +void QDocumentCursorHandle::deleteChar() +{ + if ( !m_doc ) + return; + + QDocumentLine l = m_doc->line(m_begLine); + + if ( l.isNull() || atEnd() ) + return; + + QDocumentCommand *command = 0; + + if ( !atLineEnd() ) + { + command = new QDocumentEraseCommand( + m_begLine, + m_begOffset, + m_begLine, + m_begOffset + 1, + m_doc + ); + + } else { + // merge two blocks... + command = new QDocumentEraseCommand( + m_begLine, + m_begOffset, + m_begLine + 1, + 0, + m_doc + ); + + } + + command->setTargetCursor(this); + command->setUndoOffset(-1); + execute(command); +} + +void QDocumentCursorHandle::deletePreviousChar() +{ + if ( !m_doc ) + return; + + QDocumentLine l = m_doc->line(m_begLine); + + if ( l.isNull() || atStart() ) + return; + + QDocumentCommand *command = 0; + + if ( !atLineStart() ) + { + command = new QDocumentEraseCommand( + m_begLine, + m_begOffset - 1, + m_begLine, + m_begOffset, + m_doc + ); + + } else { + // merge two blocks... + QDocumentLine prev = m_doc->line(m_begLine - 1); + + command = new QDocumentEraseCommand( + m_begLine - 1, + prev.length(), + m_begLine, + m_begOffset, + m_doc + ); + + } + + command->setTargetCursor(this); + execute(command); +} + +void QDocumentCursorHandle::execute(QDocumentCommand *c) +{ + if ( !m_doc ) + return; + + if ( isSilent() && !c->isSilent() ) + c->setSilent(isSilent()); + + if ( m_blocks.count() ) + { + c->redo(); + m_blocks.top()->addCommand(c); + + } else if ( m_doc ) { + //qDebug("Cursor handle executing command : 0x%x", this); + + m_doc->execute(c); + } +} + +void QDocumentCursorHandle::beginEditBlock() +{ + m_blocks.push(new QDocumentCommandBlock(m_doc)); +} + +void QDocumentCursorHandle::endEditBlock() +{ + if ( !m_doc || m_blocks.isEmpty() ) + return; + + //qDebug("Cursor handle executing command : 0x%x [block]", this); + + QDocumentCommandBlock *block = m_blocks.pop(); + + // special trick to prevent double redo() while getting rid of + // bugs occuring in when inserting/erasing in overlapping lines + // inside a command block + block->setWeakLock(true); + + m_doc->execute(block); +} + +QDocumentCursor QDocumentCursorHandle::selectionStart() const +{ + if ( !m_doc ) + return QDocumentCursor(); + + if ( !hasSelection() ) + return QDocumentCursor(clone()); + + QDocumentCursor pos(m_doc, m_begLine, m_begOffset), + anc(m_doc, m_endLine, m_endOffset); + + return (pos < anc) ? pos : anc; +} + +QDocumentCursor QDocumentCursorHandle::selectionEnd() const +{ + if ( !m_doc ) + return QDocumentCursor(); + + if ( !hasSelection() ) + return QDocumentCursor(clone()); + + QDocumentCursor pos(m_doc, m_begLine, m_begOffset), + anc(m_doc, m_endLine, m_endOffset); + + return (pos > anc) ? pos : anc; +} + +bool QDocumentCursorHandle::eq(const QDocumentCursorHandle *h) +{ + return (m_begLine == h->m_begLine) && (m_begOffset == h->m_begOffset); + /* + if ( !hasSelection() ) + return (m_begLine == h->m_begLine) && (m_begOffset == h->m_begOffset); + + return + (m_begLine == h->m_begLine) + && + (m_begOffset == h->m_begOffset) + && + (m_endLine == h->m_endLine) + && + (m_endOffset == h->m_endOffset) + ; + */ +} + +bool QDocumentCursorHandle::lt(const QDocumentCursorHandle *h) +{ + return + (m_begLine < h->m_begLine) + || + ((m_begLine == h->m_begLine) && (m_begOffset < h->m_begOffset)) + ; +} + +bool QDocumentCursorHandle::gt(const QDocumentCursorHandle *h) +{ + return + (m_begLine > h->m_begLine) + || + ((m_begLine == h->m_begLine) && (m_begOffset > h->m_begOffset)) + ; +} + +QString QDocumentCursorHandle::selectedText() const +{ + if ( !m_doc ) + return QString(); + + QDocumentLine l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine); + + if ( l1.isNull() || l2.isNull() ) + return QString(); + + QString s; + + if ( m_begLine == m_endLine ) + { + int min = qMin(m_begOffset, m_endOffset), + max = qMax(m_begOffset, m_endOffset); + + s = l1.text().mid(min, max - min); + } else if ( m_begLine < m_endLine ) { + s = l1.text().mid(m_begOffset); + + int it = m_begLine; + //QDocumentConstIterator it = m_doc->impl()->index(m_begLine.handle()); + + while ( ++it < m_endLine ) + { + s += '\n'; + s += m_doc->line(it).text(); + } + + s += '\n'; + s += l2.text().left(m_endOffset); + } else { + s = l2.text().mid(m_endOffset); + + int it = m_endLine; + //QDocumentConstIterator it = m_doc->impl()->index(m_endLine.handle()); + + while ( ++it < m_begLine ) + { + s += '\n'; + s += m_doc->line(it).text(); + } + + s += '\n'; + s += l1.text().left(m_begOffset); + } + + return s; +} + +void QDocumentCursorHandle::clearSelection() +{ + if ( m_doc && m_doc->line(m_endLine).isValid() ) + { + //m_begLine = m_endLine; + //m_begOffset = m_endOffset; + + m_endLine = -1; + m_endOffset = -1; + } +} + +void QDocumentCursorHandle::replaceSelectedText(const QString& text) +{ + int begline, begcol; + beginBoundary(begline, begcol); + + bool atStart = (begline == m_begLine && begcol == m_begOffset); + + if ( text.isEmpty() ) + { + removeSelectedText(); + } else { + insertText(text, true); + + /* + Adjust selection around the new text and preserve the order + of position and anchor + */ + /* + if ( atStart ) + { + m_endLine = m_begLine; + m_begLine = begline; + m_endOffset = m_begOffset; + m_begOffset = begcol; + } else { + m_endLine = begline; + m_endOffset = begcol; + } + */ + } + + //qDebug("[%i, %i] => ( (%i, %i), (%i, %i) )", begline, begcol, m_begLine, m_begOffset, m_endLine, m_endOffset); +} + +void QDocumentCursorHandle::select(QDocumentCursor::SelectionType t) +{ + if ( !m_doc || !m_doc->line(m_begLine).isValid() ) + return; + + if ( t == QDocumentCursor::LineUnderCursor ) + { + movePosition(1, QDocumentCursor::StartOfLine, QDocumentCursor::MoveAnchor); + movePosition(1, QDocumentCursor::EndOfLine, QDocumentCursor::KeepAnchor); + + } else if ( t == QDocumentCursor::WordUnderCursor ) { + + movePosition(1, QDocumentCursor::StartOfWord, QDocumentCursor::MoveAnchor); + movePosition(1, QDocumentCursor::EndOfWord, QDocumentCursor::KeepAnchor); + } +} + +void QDocumentCursorHandle::setSelectionBoundary(const QDocumentCursor& c) +{ + if ( + !m_doc + || + (m_begLine == -1) + || + ( + (c.lineNumber() == m_begLine) + && + (c.columnNumber() == m_begOffset) + ) + ) + return; + + //qDebug("setting new selection boundary... "); + + if ( !hasSelection() ) + { + m_endLine = m_begLine; + m_endOffset = m_begOffset; + } + + m_begLine = c.lineNumber(); + m_begOffset = c.columnNumber(); +} + +bool QDocumentCursorHandle::isWithinSelection(const QDocumentCursor& c) const +{ + if ( !hasSelection() ) //|| c.hasSelection() ) + return false; + + int minOff, maxOff, min, max; + + if ( m_begLine > m_endLine ) + { + max = m_begLine; + maxOff = m_begOffset; + + min = m_endLine; + minOff = m_endOffset; + } else { + min = m_begLine; + minOff = m_begOffset; + + max = m_endLine; + maxOff = m_endOffset; + } + + return (m_begLine == m_endLine) + ? + ( + (c.lineNumber() == m_begLine) + && + (qMin(m_begOffset, m_endOffset) <= c.columnNumber()) + && + (qMax(m_begOffset, m_endOffset) >= c.columnNumber()) + ) + : + ( + ( + (c.lineNumber() > min) + && + (c.lineNumber() < max) + ) + || + ( + (c.lineNumber() == min) + && + (minOff <= c.columnNumber()) + ) + || + ( + (c.lineNumber() == max) + && + (maxOff >= c.columnNumber()) + ) + ) + ; + +} + +/* + beware when modifying these as their current form handle the special + case of no selection (m_endLine == -1) and a hasty change may break + that behavior : no selection -> both boundary are the cursor pos = (m_begLine, m_begOffset) +*/ +void QDocumentCursorHandle::beginBoundary(int& begline, int& begcol) const +{ + if ( m_begLine < m_endLine ) + { + begline = m_begLine; + begcol = m_begOffset; + } else { + begline = m_endLine; + begcol = (m_begLine == m_endLine && m_begOffset < m_endOffset) ? m_begOffset : m_endOffset; + } +} + +void QDocumentCursorHandle::endBoundary(int& endline, int& endcol) const +{ + if ( m_begLine < m_endLine ) + { + endline = m_endLine; + endcol = m_endOffset; + } else { + endline = m_begLine; + endcol = (m_begLine == m_endLine && m_begOffset < m_endOffset) ? m_endOffset : m_begOffset; + } +} + +void QDocumentCursorHandle::boundaries(int& begline, int& begcol, int& endline, int& endcol) const +{ + beginBoundary(begline, begcol); + endBoundary(endline, endcol); + + /* + if ( m_begLine == m_endLine ) + { + begline = m_begLine; + endline = m_endLine; + if ( m_begOffset < m_endOffset ) + { + begcol = m_begOffset; + endcol = m_endOffset; + } else { + endcol = m_begOffset; + begcol = m_endOffset; + } + } else if ( m_begLine < m_endLine ) { + begline = m_begLine; + endline = m_endLine; + begcol = m_begOffset; + endcol = m_endOffset; + } else { + endline = m_begLine; + begline = m_endLine; + endcol = m_begOffset; + begcol = m_endOffset; + } + */ +} + +void QDocumentCursorHandle::substractBoundaries(int lbeg, int cbeg, int lend, int cend) +{ + int tlmin, tlmax, tcmin, tcmax; + + boundaries(tlmin, tcmin, tlmax, tcmax); + + bool begFirst = tlmin == m_begLine && tcmin == m_begOffset; + + if ( tlmax < lbeg || tlmin > lend || (tlmax == lbeg && tcmax < cbeg) || (tlmin == lend && tcmin > cend) ) + { + // no intersection + return; + } + + int numLines = lend - lbeg; + bool beyondBeg = (tlmin > lbeg || (tlmin == lbeg && tcmin >= cbeg)); + bool beyondEnd = (tlmax < lend || (tlmax == lend && tcmax <= cend)); + + if ( beyondBeg && beyondEnd ) + { + //qDebug("(%i, %i : %i, %i) erased as in (%i, %i : %i, %i)", tlmin, tcmin, tlmax, tcmax, lbeg, cbeg, lend, cend); + // cursor erased... + m_begLine = m_endLine = lbeg; + m_begOffset = m_endOffset = cbeg; + } else if ( beyondEnd ) { + //qDebug("beyond end"); + if ( begFirst ) + { + m_endLine = lbeg; + m_endOffset = cbeg; + } else { + m_begLine = lbeg; + m_begOffset = cbeg; + } + } else if ( beyondBeg ) { + //qDebug("beyond beg"); + if ( begFirst ) + { + m_begLine = lend; + m_begOffset = cend; + if ( numLines ) + { + m_begLine -= numLines; + m_endLine -= numLines; + } else { + m_begOffset = cbeg; + } + if ( m_begLine == m_endLine ) + m_endOffset -= (cend - cbeg); + } else { + m_endLine = lend; + m_endOffset = cend; + if ( numLines ) + { + m_endLine -= numLines; + m_begLine -= numLines; + } else { + m_endOffset = cbeg; + } + if ( m_begLine == m_endLine ) + m_begOffset -= (cend - cbeg); + } + } else { + int off = cend - cbeg; + + //qDebug("correcting by %i", off); + + if ( begFirst ) + { + m_endLine -= numLines; + if ( tlmax == lend ) + { + m_endOffset -= off; + } + } else { + m_begLine -= numLines; + if ( tlmax == lend ) + { + m_begOffset -= off; + } + } + } + + //qDebug("(%i, %i : %i, %i) corrected to (%i, %i : %i, %i)", tlmin, tcmin, tlmax, tcmax, m_begLine, m_begOffset, m_endLine, m_endOffset); +} + +void QDocumentCursorHandle::intersectBoundaries(int& lbeg, int& cbeg, int& lend, int& cend) const +{ + int tlmin, tlmax, tcmin, tcmax, clmin, clmax, ccmin, ccmax; + + boundaries(tlmin, tcmin, tlmax, tcmax); + clmin = lbeg; + clmax = lend; + ccmin = cbeg; + ccmax = cend; + + if ( tlmax < clmin || tlmin > clmax || (tlmax == clmin && tcmax < ccmin) || (tlmin == clmax && tcmin > ccmax) ) + { + lbeg = cbeg = lend = cend = -1; + return; + } + + if ( tlmin == clmin ) + { + lbeg = tlmin; + cbeg = qMax(tcmin, ccmin); + } else if ( tlmin < clmin ) { + lbeg = clmin; + cbeg = ccmin; + } else { + lbeg = tlmin; + cbeg = tcmin; + } + + if ( tlmax == clmax ) + { + lend = tlmax; + cend = qMin(tcmax, ccmax); + } else if ( tlmax < clmax ) { + lend = tlmax; + cend = tcmax; + } else { + lend = clmax; + cend = ccmax; + } +} + +void QDocumentCursorHandle::intersectBoundaries(QDocumentCursorHandle *h, int& lbeg, int& cbeg, int& lend, int& cend) const +{ + int tlmin, tlmax, tcmin, tcmax, clmin, clmax, ccmin, ccmax; + + boundaries(tlmin, tcmin, tlmax, tcmax); + h->boundaries(clmin, ccmin, clmax, ccmax); + + if ( tlmax < clmin || tlmin > clmax || (tlmax == clmin && tcmax < ccmin) || (tlmin == clmax && tcmin > ccmax) ) + { + lbeg = cbeg = lend = cend = -1; + return; + } + + if ( tlmin == clmin ) + { + lbeg = tlmin; + cbeg = qMax(tcmin, ccmin); + } else if ( tlmin < clmin ) { + lbeg = clmin; + cbeg = ccmin; + } else { + lbeg = tlmin; + cbeg = tcmin; + } + + if ( tlmax == clmax ) + { + lend = tlmax; + cend = qMin(tcmax, ccmax); + } else if ( tlmax < clmax ) { + lend = tlmax; + cend = tcmax; + } else { + lend = clmax; + cend = ccmax; + } +} + +QDocumentCursor QDocumentCursorHandle::intersect(const QDocumentCursor& c) const +{ + if ( !hasSelection() ) + { + //if ( c.hasSelection() && c.isWithinSelection(QDocumentCursor(this)) ) + // return QDocumentCursor(clone()); + + } else if ( !c.hasSelection() ) { + + if ( isWithinSelection(c) ) + return c; + + } else { + QDocumentCursorHandle *h = c.handle(); + + int lbeg, lend, cbeg, cend; + intersectBoundaries(h, lbeg, cbeg, lend, cend); + + if ( lbeg != -1 ) + { + QDocumentCursor c(m_doc, lbeg, cbeg); + + if ( lend != -1 && (lbeg != lend || cbeg != cend) ) + { + c.setSelectionBoundary(QDocumentCursor(m_doc, lend, cend)); + } + + return c; + } + } + + return QDocumentCursor(); +} + +void QDocumentCursorHandle::removeSelectedText(bool keepAnchor) +{ + if ( !m_doc ) + return; + + QDocumentLine l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine); + + if ( l1.isNull() || l2.isNull() ) + return; + + QDocumentCommand *c; + + if ( m_begLine < m_endLine ) + { + c = new QDocumentEraseCommand( + m_begLine, + m_begOffset, + m_endLine, + m_endOffset, + m_doc + ); + + } else if ( m_begLine > m_endLine ) { + c = new QDocumentEraseCommand( + m_endLine, + m_endOffset, + m_begLine, + m_begOffset, + m_doc + ); + + //m_begLine = m_endLine; + //m_begOffset = m_endOffset; + + } else { + c = new QDocumentEraseCommand( + m_begLine, + qMin(m_begOffset, m_endOffset), + m_endLine, + qMax(m_begOffset, m_endOffset), + m_doc + ); + + //m_begOffset = qMin(m_begOffset, m_endOffset); + //m_endLine = -1; + //m_endOffset = -1; + } + + c->setKeepAnchor(keepAnchor); + c->setTargetCursor(this); + execute(c); +} + +////////////////// + +///////////////////////// +// QDocumentPrivate +///////////////////////// + +template T* getStaticDefault() { static T _globStatInst; return &_globStatInst; } + +QFont* QDocumentPrivate::m_font = 0;// = QApplication::font(); +QFontMetrics* QDocumentPrivate::m_fontMetrics = 0;//(m_font); + +int QDocumentPrivate::m_defaultTabStop = 4; +QFormatScheme* QDocumentPrivate::m_defaultFormatScheme = getStaticDefault(); + +QList QDocumentPrivate::m_documents; + +bool QDocumentPrivate::m_fixedPitch; +int QDocumentPrivate::m_ascent;// = m_fontMetrics.ascent(); +int QDocumentPrivate::m_descent;// = m_fontMetrics.descent(); +int QDocumentPrivate::m_leading;// = m_fontMetrics.leading(); +int QDocumentPrivate::m_spaceWidth;// = m_fontMetrics.width(' '); +int QDocumentPrivate::m_lineHeight;// = m_fontMetrics.height(); +int QDocumentPrivate::m_lineSpacing;// = m_fontMetrics.lineSpacing(); + +int QDocumentPrivate::m_leftMargin = 5; +QDocument::WhiteSpaceMode QDocumentPrivate::m_showSpaces = QDocument::ShowNone; +QDocument::LineEnding QDocumentPrivate::m_defaultLineEnding = QDocument::Conservative; + +int QDocumentPrivate::m_wrapMargin = 15; + +QDocumentPrivate::QDocumentPrivate(QDocument *d) + : m_doc(d), + m_editCursor(0), + m_lastGroupId(-1), + m_constrained(false), + m_width(0), + m_height(0), + m_tabStop(m_defaultTabStop), + m_formatScheme(0), + m_language(0), + m_maxMarksPerLine(0), + _nix(0), + _dos(0), + _mac(0), + m_lineEnding(m_defaultLineEnding) +{ + m_documents << this; + updateFormatCache(); +} + +QDocumentPrivate::~QDocumentPrivate() +{ + m_marks.clear(); + m_largest.clear(); + + m_deleting = true; + + //qDeleteAll(m_lines); + foreach ( QDocumentLineHandle *h, m_lines ) + h->deref(); + + m_lines.clear(); + + m_deleting = false; + + m_commands.clear(); + + m_documents.removeAll(this); +} + +int QDocumentPrivate::findNextMark(int id, int from, int until) +{ + if ( from < 0 ) + from += m_lines.count(); + + QHash >::const_iterator e = m_marks.constEnd(); + + int max = until; + + if ( max < 0 ) + max += m_lines.count(); + else if ( max < from ) + max = m_lines.count() - 1; + + for ( int i = from; i <= max; ++i ) + { + QDocumentLineHandle *h = m_lines.at(i); + + QHash >::const_iterator it = m_marks.constFind(h); + + if ( it != e && it->contains(id) ) + return i; + + } + + if ( until > 0 && until < from ) + { + for ( int i = 0; i <= until; ++i ) + { + QDocumentLineHandle *h = m_lines.at(i); + + QHash >::const_iterator it = m_marks.constFind(h); + + if ( it != e && it->contains(id) ) + return i; + + } + } + + return -1; +} + +int QDocumentPrivate::findPreviousMark(int id, int from, int until) +{ + if ( from < 0 ) + from += m_lines.count(); + + if ( until < 0 ) + { + until += m_lines.count(); + } else if ( until >= m_lines.count() ) { + until = m_lines.count() - 1; + } + + QHash >::const_iterator e = m_marks.constEnd(); + + int min = until; + + if ( min > from ) + min = 0; + + for ( int i = from; i >= min; --i ) + { + QDocumentLineHandle *h = m_lines.at(i); + + QHash >::const_iterator it = m_marks.constFind(h); + + if ( it != e && it->contains(id) ) + return i; + + } + + if ( until > 0 && until > from ) + { + for ( int i = m_lines.count() - 1; i >= until; --i ) + { + QDocumentLineHandle *h = m_lines.at(i); + + QHash >::const_iterator it = m_marks.constFind(h); + + if ( it != e && it->contains(id) ) + return i; + + } + } + + return -1; +} + +void QDocumentPrivate::execute(QDocumentCommand *cmd) +{ + if ( !cmd ) + return; + + m_lastModified = QDateTime::currentDateTime(); + + //qDebug("adding a command..."); + + //cmd->setTarget(m_doc); + + m_commands.push(cmd); +} + +void QDocumentPrivate::draw(QPainter *p, QDocument::PaintContext& cxt) +{ + QDocumentLineHandle *h; + bool inSel = false, fullSel; + QList::iterator cit; + int i, realln, pos = 0, xOffset, + firstLine = qMax(0, cxt.yoffset / m_lineSpacing), + lastLine = qMax(0, firstLine + (cxt.height / m_lineSpacing)); + + if ( cxt.height % m_lineSpacing ) + ++lastLine; + + p->setFont(*m_font); + + QBrush bg, + base = cxt.palette.base(), + selbg = cxt.palette.highlight(), + alternate = QLineMarksInfoCenter::instance()->markType("current").color; + + if ( !alternate.color().isValid() ) + alternate = cxt.palette.alternateBase(); + + QSmallArray m_cursorLines(0), m_selectionBoundaries(0); + + int wrap = 0; + i = textLine(firstLine, &wrap); + firstLine -= wrap; + realln = firstLine; + + //qDebug("lines [%i, %i]", firstLine, lastLine); + + pos += firstLine * m_lineSpacing; + + // adjust first line to take selections into account... + foreach ( const QDocumentSelection& s, cxt.selections ) + { + if ( (s.startLine < i) && (s.endLine >= i) ) + { + inSel = true; + break; + } + } + + for ( ; realln <= lastLine; ++i ) + { + if ( i >= m_lines.count() ) + { + //qDebug("line %i not valid", i); + break; + } + + h = m_lines.at(i); + + // ugly workaround... + if( !m_fixedPitch ) + adjustWidth(i); + + const int wrap = h->m_frontiers.count(); + const bool wrapped = wrap; + + //if ( wrapped ) + // qDebug("line %i is wrapped over %i sublines", i, *wit); + + // selections stuff (must do it before whatever the visibility...) + m_selectionBoundaries.clear(); + + fullSel = false; + + if ( inSel ) + m_selectionBoundaries.prepend(0); + + foreach ( const QDocumentSelection& s, cxt.selections ) + { + if ( i == s.startLine ) + { + if ( !(m_selectionBoundaries.count() & 1) ) + m_selectionBoundaries.append(s.start); + + if ( i == s.endLine ) + { + m_selectionBoundaries.append(s.end); + } else { + //++selLevel; + inSel = true; + //selEnd = h->m_text.length(); + } + } else if ( inSel && (i == s.endLine) ) { + + if ( m_selectionBoundaries.count() % 2 ) + m_selectionBoundaries.append(s.end); + + //--selLevel; + inSel = false; + } + } + + if ( inSel && m_selectionBoundaries.count() == 1 && m_selectionBoundaries.at(0) == 0 ) + { + m_selectionBoundaries.clear(); + fullSel = true; + } + + if ( h->hasFlag(QDocumentLine::Hidden) ) + { + continue; + } else + ++realln; + + if ( wrapped ) + realln += wrap; + + m_cursorLines.clear(); + + bg = base; + + // idx = column = 0; + xOffset = m_leftMargin; // margin + + // cursor(s) stuff + cit = cxt.cursors.begin(); + + while ( cit != cxt.cursors.end() ) + { + if ( (*cit)->lineNumber() == i ) + { + if ( cxt.blinkingCursor ) + m_cursorLines.append((*cit)->columnNumber()); + + if ( cxt.fillCursorRect ) + bg = alternate; + + cit = cxt.cursors.erase(cit); + } else { + ++cit; + } + } + + cit = cxt.extra.begin(); + + while ( cit != cxt.extra.end() ) + { + if ( (*cit)->lineNumber() == i ) + { + m_cursorLines.append((*cit)->columnNumber()); + + cit = cxt.extra.erase(cit); + } else { + ++cit; + } + } + + qSort(m_cursorLines); + + QList m = marks(h); + + // line marks stuff + if ( m.count() ) + { + QLineMarksInfoCenter *mic = QLineMarksInfoCenter::instance(); + + QColor c = mic->markType(mic->priority(m)).color; + + if ( c.isValid() ) + bg = c; + + } + + if ( realln < firstLine ) + continue; + + //qDebug("drawing line %i (visual %i)", i, realln); + + p->fillRect(qMax(cxt.xoffset, m_leftMargin), pos, + cxt.width, m_lineSpacing, + fullSel ? selbg : bg); + + if ( wrapped ) + p->fillRect(qMax(cxt.xoffset, m_leftMargin), pos + m_lineSpacing, + cxt.width, m_lineSpacing * wrap, fullSel ? selbg : bg); + + //p->fillRect(cxt.xoffset, pos + 1, + // cxt.width, m_lineHeight, + // bg); + + p->save(); + + // simplify line drawing + p->translate(0, pos); + + // draw text + h->draw(p, cxt.xoffset, cxt.width, m_selectionBoundaries, m_cursorLines, cxt.palette, fullSel); + + // see above + p->translate(0, -pos); + + // draw fold rect indicator + if ( h->hasFlag(QDocumentLine::CollapsedBlockStart) ) + { + p->setBrush(Qt::NoBrush); + p->setPen(QPen(Qt::blue, 1, Qt::DotLine)); + + //p->drawRect(cxt.xoffset + 2, pos, + // cxt.width - 4, m_lineSpacing - 1); + + p->drawRect(m_leftMargin, pos, + cxt.width - 4, m_lineSpacing * (wrap + 1) - 1); + + } + + p->restore(); + + pos += m_lineSpacing; + + if ( wrapped ) + { + pos += m_lineSpacing * wrap; + } + + //qDebug("drawing line %i in %i ms", i, t.elapsed()); + } + + //qDebug("painting done"); // in %i ms...", t.elapsed()); +} + +int QDocumentPrivate::position(const QDocumentLineHandle *l) const +{ + int pos = 0; + + int idx = m_lines.indexOf(const_cast(l)); + + if ( idx == -1 ) + return -1; + + for ( int i = 0; i < idx; i++ ) + pos += m_lines.at(i)->length(); + + return pos; +} + +QDocumentLineHandle* QDocumentPrivate::lineForPosition(int& position) const +{ + int pos = 0, idx = 0; + + while ( (pos + m_lines.at(idx)->length()) < position ) + pos += m_lines.at(idx++)->length(); + + + return 0; +} + +void QDocumentPrivate::setWidth(int width) +{ + int oldConstraint = m_constrained; + m_constrained = width > 0; + + if ( m_constrained ) + { + int oldWidth = m_width; + + m_width = width; + + if ( oldConstraint && oldWidth < width ) + { + // expand : simply remove old wraps if possible + + QMap::iterator it = m_wrapped.begin(); + + while ( it != m_wrapped.end() ) + { + QDocumentLineHandle *h = it.key() < m_lines.count() ? m_lines.at(it.key()) : 0; + + if ( h ) + h->updateWrap(); + + int sz = h ? h->m_frontiers.count() : 0; + + if ( sz ) + { + //qDebug("changing wrap at line %i from %i to %i", it.key(), *it, sz); + *it = sz; + ++it; + } else { + //qDebug("removing wrap at line %i", it.key()); + it = m_wrapped.erase(it); + } + } + } else if ( oldWidth > width ) { + // shrink : scan whole document and create new wraps wherever needed + //qDebug("global width scan [constraint on]"); + //m_wrapped.clear(); + setWidth(); + } + } else { + //qDebug("global width scan [constraint off]"); + m_wrapped.clear(); + setWidth(); + } + + if ( m_editCursor ) + { + m_editCursor->refreshColumnMemory(); + } + + emitWidthChanged(); + setHeight(); + + emitFormatsChanged(); +} + +void QDocumentPrivate::setWidth() +{ + m_largest.clear(); + const int max = m_lines.count(); + + if ( m_constrained ) + { + int first = -1; + + for ( int i = 0; i < max; ++i ) + { + QDocumentLineHandle *l = m_lines.at(i); + int olw = l->m_frontiers.count(); + + l->updateWrap(); + + int lw = l->m_frontiers.count(); + + if ( olw == lw ) + continue; + + if ( lw ) + { + //qDebug("added wrap on line %i", line); + m_wrapped[i] = lw; + } else { + //qDebug("removed wrap on line %i", line); + m_wrapped.remove(i); + } + + if ( first != -1 ) + first = i; + } + + if ( first != -1 ) + emitFormatsChange(first, -1); + } else { + int oldWidth = m_width; + + m_width = 0; + + foreach ( QDocumentLineHandle *l, m_lines ) + { + if ( l->hasFlag(QDocumentLine::Hidden) ) + continue; + + l->m_frontiers.clear(); + + int w = l->cursorToX(l->length()); + + if ( w > m_width ) + { + m_width = w; + + m_largest.clear(); + m_largest << qMakePair(l, w); + } + } + + if ( m_width != oldWidth ) + emitWidthChanged(); + } +} + +static const int widthCacheSize = 5; + +void QDocumentPrivate::adjustWidth(int line) +{ + if ( line < 0 || line >= m_lines.count() ) + return; + + QDocumentLineHandle *l = m_lines.at(line); + + if ( m_constrained ) + { + int olw = l->m_frontiers.count(); + + l->updateWrap(); + + int lw = l->m_frontiers.count(); + + if ( olw == lw ) + return; + + if ( l->m_layout ) + l->setFlag(QDocumentLine::LayoutDirty); + + if ( lw ) + { + //qDebug("added wrap on line %i", line); + m_wrapped[line] = lw; + } else { + //qDebug("removed wrap on line %i", line); + m_wrapped.remove(line); + } + + emitFormatsChange(line, -1); + setHeight(); + + } else { + l->m_frontiers.clear(); + + int w = l->cursorToX(l->length()); + + if ( w > m_width ) + { + m_width = w; + emitWidthChanged(); + + m_largest.clear(); + m_largest << qMakePair(l, w); + } else if ( m_largest.count() && (m_largest.at(0).first == l) ) { + int old = m_largest.at(0).second; + + if ( w < old ) + setWidth(); + } + } +} + +void QDocumentPrivate::setHeight() +{ + int oldHeight = m_height; + int last = visualLine(m_lines.count() - 1) + 1; + + if ( m_lines.count() ) + last += m_lines.last()->m_frontiers.count(); + + m_height = last * m_lineSpacing; + + if ( oldHeight != m_height ) + emitHeightChanged(); +} + +void QDocumentPrivate::setFont(const QFont& f) +{ + if ( !m_font ) + { + m_font = new QFont; + m_fontMetrics = new QFontMetrics(*m_font); + } + + *m_font = f; + + // ensures the font is fixed pitch to avoid idsplay glitches + // and inconsistency of column selections + // :( does not work well... + //m_font->setFixedPitch(true); + + // set the styling so that if the font is not found Courier one will be used + m_font->setStyleHint(QFont::Courier, QFont::PreferQuality); + + *m_fontMetrics = QFontMetrics(*m_font); + + m_spaceWidth = m_fontMetrics->width(' '); + m_lineSpacing = m_fontMetrics->lineSpacing(); + m_ascent = m_fontMetrics->ascent(); + m_descent = m_fontMetrics->descent(); + m_leading = m_fontMetrics->leading(); + + m_lineHeight = m_fontMetrics->height(); + //m_lineHeight = m_ascent + m_descent - 2; + + m_fixedPitch = QFontInfo(*m_font).fixedPitch(); + + //if ( !m_fixedPitch ) + // qDebug("unsafe computations..."); + + foreach ( QDocumentPrivate *d, m_documents ) + { + d->updateFormatCache(); + d->setWidth(); + d->setHeight(); + } +} + +void QDocumentPrivate::setFormatScheme(QFormatScheme *f) +{ + m_formatScheme = f; + updateFormatCache(); +} + +void QDocumentPrivate::tunePainter(QPainter *p, int fid) +{ + if ( fid < m_fonts.count() ) + { + p->setFont(m_fonts.at(fid)); + //p->setPen(m_colors.at(fid)); + } else { + p->setFont(*m_font); + //p->setPen(Qt::black); + } +} + +void QDocumentPrivate::updateFormatCache() +{ + m_fonts.clear(); + + if ( !m_font ) + return; + + if ( !m_formatScheme ) + { + m_fonts << *m_font; + return; + } + + QFont f(*m_font); + const int end = m_formatScheme->formatCount(); + + m_fonts.reserve(end); + + for ( int i = 0; i < end; i++ ) + { + QFormat fmt = m_formatScheme->format(i); + + f.setWeight(fmt.weight); + f.setItalic(fmt.italic); + + m_fonts << f; + } + + //foreach ( QDocumentPrivate *d, m_documents ) + // d->emitFormatsChanged(); + + emitFormatsChanged(); +} + +void QDocumentPrivate::emitWidthChanged() +{ + if ( !m_doc ) + return; + + emit m_doc->widthChanged(m_width); + + emit m_doc->sizeChanged(QSize(m_width, m_height)); +} + +void QDocumentPrivate::emitHeightChanged() +{ + if ( !m_doc ) + return; + + emit m_doc->heightChanged(m_height); + + emit m_doc->sizeChanged(QSize(m_width, m_height)); +} + +void QDocumentPrivate::insertLines(int after, const QList& l) +{ + //qDebug("inserting : %i, %i", after, l.count()); + + int i = 0; + + foreach ( QDocumentLineHandle *h, l ) + { + h->setFlag(QDocumentLine::Hidden, false); + h->setFlag(QDocumentLine::CollapsedBlockStart, false); + h->setFlag(QDocumentLine::CollapsedBlockEnd, false); + h->m_frontiers.clear(); + } + + QMap::iterator it = m_hidden.begin(); + + while ( it != m_hidden.end() ) + { + if ( (it.key() <= after) && ((it.key() + *it) > after) ) + { + *it += l.count(); + + foreach ( QDocumentLineHandle *h, l ) + h->setFlag(QDocumentLine::Hidden, true); + } + + ++it; + } + + ++after; + updateHidden(after, l.count()); + updateWrapped(after, l.count()); + + while ( i < l.count() ) + { + // TODO : move (and abstract somehow) inside the line (handle?) + l.at(i)->m_context.reset(); + + m_lines.insert(after + i, l.at(i)); + + adjustWidth(after + i); + + ++i; + } + + emit m_doc->lineCountChanged(m_lines.count()); + setHeight(); +} + +void QDocumentPrivate::removeLines(int after, int n) +{ + if ( (after >= 0) && (after < m_lines.count()) ) + m_lines.at(after)->setFlag(QDocumentLine::CollapsedBlockStart, false); + + QMap::iterator it = m_hidden.begin(); + + //qDebug("translating %i", visualLine); + + while ( it != m_hidden.end() ) + { + if ( (it.key() >= after) && (it.key() < (after + n)) ) + { + int i = it.key(), end = i + *it, depth = 0; + + while ( i <= end ) + { + if ( !depth ) + m_lines.at(i)->setFlag(QDocumentLine::Hidden, false); + + if ( m_lines.at(i)->hasFlag(QDocumentLine::CollapsedBlockStart) ) + ++depth; + else if ( m_lines.at(i)->hasFlag(QDocumentLine::CollapsedBlockEnd) ) + --depth; + + ++i; + } + + it = m_hidden.erase(it); + + } else if ( (it.key() < after) && (it.key() + *it) >= after ) { + + if ( (it.key() + *it) > (after + n) ) + { + // fully inside + *it -= n; + ++it; + } else { + // goes beyond... + int i = it.key(), end = i + *it, depth = 0; + + while ( i <= end ) + { + if ( !depth ) + m_lines.at(i)->setFlag(QDocumentLine::Hidden, false); + + if ( m_lines.at(i)->hasFlag(QDocumentLine::CollapsedBlockStart) ) + ++depth; + else if ( m_lines.at(i)->hasFlag(QDocumentLine::CollapsedBlockEnd) ) + --depth; + + ++i; + } + + it = m_hidden.erase(it); + } + } else { + ++it; + } + } + + it = m_wrapped.begin(); + + while ( it != m_wrapped.end() ) + { + if ( (it.key() > after) && (it.key() <= (after + n)) ) + { + //qDebug("eraser %i", it.key()); + it = m_wrapped.erase(it); + } else { + ++it; + } + } + + ++after; + updateHidden(after, -n); + updateWrapped(after, -n); + m_lines.remove(after, n); + + emit m_doc->lineCountChanged(m_lines.count()); + setHeight(); +} + +QDocumentLineHandle* QDocumentPrivate::at(int line) const +{ + return ((line >= 0) && (line < m_lines.count())) ? m_lines.at(line) : 0; +} + +int QDocumentPrivate::indexOf(const QDocumentLineHandle *l) const +{ + return m_lines.indexOf(const_cast(l)); +} + +QDocumentIterator QDocumentPrivate::index(const QDocumentLineHandle *l) +{ + QDocumentIterator i = m_lines.begin(); + + int idx = indexOf(l); + + return (idx != -1) ? i + idx : m_lines.end(); +} + +QDocumentConstIterator QDocumentPrivate::index(const QDocumentLineHandle *l) const +{ + QDocumentConstIterator i = m_lines.constBegin(); + + int idx = indexOf(l); + + return (idx != -1) ? i + idx : m_lines.end(); +} + +QDocumentLineHandle* QDocumentPrivate::next(const QDocumentLineHandle *l) const +{ + if ( !l ) + return m_lines.count() ? m_lines.first() : 0; + + int idx = m_lines.indexOf(const_cast(l)); + + return ((idx != -1) && ((idx + 1) < m_lines.count())) ? m_lines.at(idx + 1) : 0; +} + +QDocumentLineHandle* QDocumentPrivate::previous(const QDocumentLineHandle *l) const +{ + if ( !l ) + return m_lines.count() ? m_lines.last() : 0; + + int idx = m_lines.indexOf(const_cast(l)); + + return ((idx != -1) && (idx > 0)) ? m_lines.at(idx - 1) : 0; +} + +void QDocumentPrivate::beginChangeBlock() +{ + //qDebug(""); + m_commands.beginMacro(QString()); +} + +void QDocumentPrivate::endChangeBlock() +{ + m_commands.endMacro(); + //qDebug(""); +} + +/*! + \brief Acquire group id +*/ +int QDocumentPrivate::getNextGroupId() +{ + if ( m_freeGroupIds.count() ) + return m_freeGroupIds.takeFirst(); + + return ++m_lastGroupId; +} + +/*! + \brief Relase group id +*/ +void QDocumentPrivate::releaseGroupId(int groupId) +{ + if ( groupId == m_lastGroupId ) + { + --m_lastGroupId; + while ( m_freeGroupIds.removeAll(m_lastGroupId) ) + { + --m_lastGroupId; + } + } else { + m_freeGroupIds << groupId; + } +} + +/*! + \brief Clear matches +*/ +void QDocumentPrivate::clearMatches(int groupId) +{ + QHash::iterator mit = m_matches.find(groupId); + + if ( mit == m_matches.end() ) + { + return; + } + + MatchList& matches = *mit; + + foreach ( const Match& m, matches ) + { + m.h->removeOverlay(m.range); + } + + matches.index = matches.count(); +} + +/*! + \brief Highlight the matched sequences + + \note Both position are BEFORE the matched characters (cursor position). +*/ +void QDocumentPrivate::addMatch(int groupId, int line, int pos, int len, int format) +{ + //qDebug("match (%i, %i, %i)", line, pos, len); + + Match m; + m.line = line; + m.h = at(line); + m.range = QFormatRange(pos, len, format); + m_matches[groupId] << m; + + m.h->addOverlay(m.range); +} + +void QDocumentPrivate::flushMatches(int groupId) +{ + QHash::iterator mit = m_matches.find(groupId); + + if ( mit == m_matches.end() ) + { + return; + } + + MatchList& matches = *mit; + + QMap areas; + + foreach ( const Match& m, matches ) + { + int n = 1; + int l = m.line; + + //qDebug("simple:(%i, %i)", l, 1); + + QMap::iterator tmp, it = areas.find(l); + + if ( it != areas.end() ) + continue; + + it = areas.insert(m.line, n); + + if ( it != areas.end() && (it - 1) != areas.end() ) + { + tmp = it - 1; + int off = tmp.key() + *tmp - l; + + if ( off >= 0 && (off < n) ) + { + *tmp += n - off; + it = areas.erase(it) - 1; + } + } + + if ( it != areas.end() && (it + 1) != areas.end() ) + { + tmp = it + 1; + int off = it.key() + *it - tmp.key(); + + if ( off >= 0 && (off < *tmp) ) + { + *it += *tmp; + areas.erase(tmp); + } + } + //emitFormatsChange(m.line, 1); + } + + // remove old matches + while ( matches.index ) + { + matches.removeFirst(); + --matches.index; + } + + // send update messages + QMap::const_iterator it = areas.constBegin(); + + while ( it != areas.constEnd() ) + { + //qDebug("merged:(%i, %i)", it.key(), *it); + emitFormatsChange(it.key(), *it); + + ++it; + } + + // update storage "meta-data" + if ( matches.isEmpty() ) + { + m_matches.remove(groupId); + + releaseGroupId(groupId); + } + //qDebug("done with matches"); +} + +QList QDocumentPrivate::marks(QDocumentLineHandle *h) const +{ + //return QList() << 1; //testcase + + return m_marks.contains(h) ? m_marks.value(h) : QList(); +} + +void QDocumentPrivate::addMark(QDocumentLineHandle *h, int mid) +{ + QList& l = m_marks[h]; + + l << mid; + + m_maxMarksPerLine = qMax(l.count(), m_maxMarksPerLine); + + emitMarkChanged(h, mid, true); +} + +void QDocumentPrivate::toggleMark(QDocumentLineHandle *h, int mid) +{ + if ( m_marks.value(h).contains(mid) ) + { + removeMark(h, mid); + } else { + addMark(h, mid); + } +} + +void QDocumentPrivate::removeMark(QDocumentLineHandle *h, int mid) +{ + QHash >::iterator it = m_marks.find(h); + + if ( it == m_marks.end() ) + return; + + int count = it->count(); + int n = it->removeAll(mid); + + if ( it->isEmpty() ) + m_marks.erase(it); + + if ( n && (count == m_maxMarksPerLine) ) + { + QHash >::const_iterator + rit = m_marks.constBegin(), + end = m_marks.constEnd(); + + m_maxMarksPerLine = 0; + + while ( rit != end ) + { + m_maxMarksPerLine = qMax(rit->count(), m_maxMarksPerLine); + ++rit; + } + } + + emitMarkChanged(h, mid, false); +} + +int QDocumentPrivate::visualLine(int textLine) const +{ + if ( textLine < 0 ) + return 0; + + int hiddenLines = 0, wrappedLines = 0; + QMap::const_iterator hit, wit, he, we; + hit = m_hidden.constBegin(); + wit = m_wrapped.constBegin(); + he = m_hidden.constEnd(); + we = m_wrapped.constEnd(); + + //qDebug("translating %i", visualLine); + + while ( hit != he || wit != we ) + { + if ( hit != he && (wit == we || hit.key() <= wit.key()) ) + { + int hl = hit.key(); + + if ( hl >= textLine ) + break; + + int max = 0; + + do + { + max = qMax(max, hit.key() - hl + *hit); + ++hit; + } while ( (hit != he) && (hit.key() <= hl + max) ); + + hiddenLines += max; + + if ( wit != we && wit.key() == hl ) + { + wrappedLines += *wit; + ++wit; + } + + while ( wit != we ) + { + if ( wit.key() > hl + max ) + break; + + ++wit; + } + + } else { + if ( wit.key() >= textLine ) + break; + + if ( m_lines.at(wit.key())->hasFlag(QDocumentLine::Hidden) ) + { + ++wit; + continue; + } + + wrappedLines += *wit; + ++wit; + } + } + + //qDebug("translating %i => %i", textLine, textLine - hiddenLines + wrappedLines); + + return textLine - hiddenLines + wrappedLines; +} + +int QDocumentPrivate::textLine(int visualLine, int *wrap) const +{ + if ( visualLine < 0 ) + return 0; + + int hiddenLines = 0, wrappedLines = 0, vis = 0, txt = 0, mess = 0; + QMap::const_iterator + h = m_hidden.constBegin(), + w = m_wrapped.constBegin(), + he = m_hidden.constEnd(), + we = m_wrapped.constEnd(); + + //qDebug("translating %i", visualLine); + + while ( vis < visualLine ) + { + if ( h != he ) + { + int hl = h.key(); + + if ( w == we || hl <= w.key() ) + { + if ( visualLine + mess <= hl ) + break; + + if ( w != we && w.key() == hl ) + { + //qDebug("trying to solve : h=(%i, %i), w=(%i, %i)", hl, *h, w.key(), *w); + const int off = (visualLine + mess) - hl; + if ( off <= *w ) + { + //qDebug("%i -> %i + %i", visualLine, hl, off); + if ( wrap ) + *wrap = off; + + return hl; + } + } + + int max = 0; + + do + { + max = qMax(max, h.key() - hl + *h); + ++h; + } while ( (h != he) && (h.key() <= hl + max) ); + + // very important : do not forget possible wrapping on folded block start + if ( w != we && w.key() == hl ) + { + wrappedLines += *w; + ++w; + } + + while ( w != we ) + { + if ( w.key() > txt + max ) + break; + + ++w; + } + + hiddenLines += max; + + } else { + txt = w.key(); + + if ( m_lines.at(txt)->hasFlag(QDocumentLine::Hidden) ) + { + ++w; + continue; + } + + if ( visualLine + mess < txt ) + break; + + wrappedLines += *w; + ++w; + } + } else if ( w != we ) { + txt = w.key(); + + if ( m_lines.at(txt)->hasFlag(QDocumentLine::Hidden) ) + { + ++w; + continue; + } + + if ( visualLine + mess < txt ) + break; + + wrappedLines += *w; + ++w; + } else { + break; + } + + mess = hiddenLines - wrappedLines; + vis = txt - mess; + } + + we = m_wrapped.constBegin(); + + if ( m_wrapped.count() && w != we ) + { + --w; + + int wl = w.key(); + bool hwrap = m_lines.at(wl)->hasFlag(QDocumentLine::Hidden); + + if ( !hwrap ) + { + int base = this->visualLine(wl); + + if ( visualLine >= base && visualLine <= base + *w ) + { + //qDebug("rebased : %i to %i", visualLine, base); + + if ( wrap ) + *wrap = visualLine - base; + + return wl; + } + } + } + + //qDebug("[visual:text] : %i : %i", visualLine, visualLine + mess); + int off = visualLine + mess - (m_lines.count() - 1); + + if ( off > 0 ) + { + if ( wrap ) + *wrap = m_lines.last()->m_frontiers.count(); + + return m_lines.count() - 1; + } + + return visualLine + mess; +} + +void QDocumentPrivate::hideEvent(int line, int count) +{ + m_hidden.insertMulti(line, count); + + setHeight(); + //emitFormatsChange(line, count); + emitFormatsChanged(); +} + +void QDocumentPrivate::showEvent(int line, int count) +{ + QMap::iterator it = m_hidden.find(line); + + while ( (it != m_hidden.end()) && (it.key() == line) ) + { + if ( *it == count ) + { +// qDebug("showing %i lines from %i", count, line); + it = m_hidden.erase(it); + } else + ++it; + } + + setHeight(); + //emitFormatsChange(line, count); + emitFormatsChanged(); +} + +void QDocumentPrivate::updateHidden(int line, int count) +{ + if ( m_hidden.isEmpty() || (line > (m_hidden.constEnd() - 1).key() ) ) + return; + + QMap prev = m_hidden; + m_hidden.clear(); + + QMap::const_iterator it = prev.constBegin(); + + //qDebug("shifting by %i from %i", count, line); + + while ( it != prev.constEnd() ) + { + if ( it.key() < line ) + { + m_hidden.insertMulti(it.key(), *it); + } else { + m_hidden.insertMulti(it.key() + count, *it); + } + + ++it; + } +} + +void QDocumentPrivate::updateWrapped(int line, int count) +{ + if ( m_wrapped.isEmpty() || (line > (m_wrapped.constEnd() - 1).key() ) ) + return; + + QMap prev = m_wrapped; + QMap::iterator it = prev.begin(); + + m_wrapped.clear(); + + //qDebug("shifting by %i from %i", count, line); + + while ( it != prev.end() ) + { + if ( it.key() < line ) + { + m_wrapped.insert(it.key(), *it); + } else { + //qDebug("moved wrap from line %i to line %i", it.key(), it.key() + count); + m_wrapped.insert(it.key() + count, *it); + } + + ++it; + } +} + +void QDocumentPrivate::emitFormatsChange(int line, int lines) +{ + emit m_doc->formatsChange(line, lines); +} + +void QDocumentPrivate::emitContentsChange(int line, int lines) +{ + //for ( int i = line; i < (line + lines); i++ ) + // m_lines.at(i)->cache(); + + int n = lines; + + if ( m_language ) + { + n = m_language->tokenize(m_doc, line, lines); + } + //qDebug("%i, %i : %i", line, lines, n); + + emit m_doc->contentsChange(line, lines); + emit m_doc->contentsChanged(); + + if ( n > lines ) + emitFormatsChange(line + lines, n - lines); +} + +void QDocumentPrivate::emitFormatsChanged() +{ + emit m_doc->formatsChanged(); +} + +void QDocumentPrivate::emitContentsChanged() +{ + //emit m_doc->contentsChanged(); +} + +void QDocumentPrivate::emitLineDeleted(QDocumentLineHandle *h) +{ + if ( !m_deleting ) + { + m_marks.remove(h); + + int idx = m_lines.indexOf(h); + + if ( idx != -1 ) + { + //qDebug("removing line %i", idx); + + m_lines.remove(idx); + + if ( m_largest.count() && (m_largest.at(0).first == h) ) + { + m_largest.remove(0); + setWidth(); + } + + m_hidden.remove(idx); + m_wrapped.remove(idx); + + setHeight(); + } + } + + emit m_doc->lineDeleted(h); +} + +void QDocumentPrivate::emitMarkChanged(QDocumentLineHandle *l, int m, bool on) +{ + emitFormatsChanged(); + emit m_doc->markChanged(l, m, on); +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocument.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocument.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QDOCUMENT_H_ +#define _QDOCUMENT_H_ + +#include "qce-config.h" + +/*! + \file qdocument.h + \brief Definition of the QDocument class + + \defgroup document Document related classes +*/ + +#include +#include +#include + +#include +#include +#include + +class QFont; +class QRect; +class QPrinter; +class QDateTime; +class QFormatScheme; +class QLanguageDefinition; + +struct QCE_EXPORT QDocumentSelection +{ + int start, end; + int startLine, endLine; +}; + +class QDocumentLine; +class QDocumentCursor; +class QDocumentPrivate; +class QDocumentCommand; +class QDocumentLineHandle; +class QDocumentCursorHandle; + +typedef QVector::iterator QDocumentIterator; +typedef QVector::const_iterator QDocumentConstIterator; + +Q_DECLARE_METATYPE(QDocumentIterator) +Q_DECLARE_METATYPE(QDocumentConstIterator) + +class QCE_EXPORT QDocument : public QObject +{ + friend class QMatcher; + friend class QDocumentPrivate; + friend class QDocumentCommand; + + Q_OBJECT + + public: + struct PaintContext + { + int width; + int height; + int xoffset; + int yoffset; + QPalette palette; + bool blinkingCursor; + bool fillCursorRect; + QList extra; + QList cursors; + QList selections; + }; + + enum LineEnding + { + Conservative, + Local, + Unix, + Windows, + Mac, // backward compat only : use OldMac instead (more self-explanatory) + OldMac = Mac + }; + + enum TextProcessing + { + RemoveTrailingWS = 1, + PreserveIndent = 2, + RestoreTrailingIndent = 4 + }; + + enum WhiteSpaceFlag + { + ShowNone = 0x00, + ShowTrailing = 0x01, + ShowLeading = 0x02, + ShowTabs = 0x04 + }; + + Q_DECLARE_FLAGS(WhiteSpaceMode, WhiteSpaceFlag) + + explicit QDocument(QObject *p = 0); + virtual ~QDocument(); + + QString text(int mode) const; + QString text(bool removeTrailing = false, bool preserveIndent = true) const; + void setText(const QString& s); + + void startChunkLoading(); + void stopChunkLoading(); + void addChunk(const QString& txt); + + LineEnding lineEnding() const; + LineEnding originalLineEnding() const; + void setLineEnding(LineEnding le); + + QDateTime lastModified() const; + void setLastModified(const QDateTime& d); + + bool canUndo() const; + bool canRedo() const; + + int width() const; + int height() const; + int widthConstraint() const; + + int lines() const; + int lineCount() const; + int visualLines() const; + int visualLineCount() const; + + int visualLineNumber(int textLineNumber) const; + int textLineNumber(int visualLineNumber) const; + + int y(int line) const; + int lineNumber(int ypos, int *wrap = 0) const; + int y(const QDocumentLine& l) const; + + QRect lineRect(int line) const; + QRect lineRect(const QDocumentLine& l) const; + + QDocumentCursor* editCursor() const; + void setEditCursor(QDocumentCursor *c); + + QLanguageDefinition* languageDefinition() const; + void setLanguageDefinition(QLanguageDefinition *l); + + int maxMarksPerLine() const; + int findNextMark(int id, int from = 0, int until = -1) const; + int findPreviousMark(int id, int from = -1, int until = 0) const; + + QDocumentLine lineAt(const QPoint& p) const; + void cursorForDocumentPosition(const QPoint& p, int& line, int& column) const; + QDocumentCursor cursorAt(const QPoint& p) const; + + QDocumentLine line(int line) const; + QDocumentLine line(QDocumentConstIterator iterator) const; + + QDocumentCursor cursor(int line, int column = 0) const; + + QDocumentLine findLine(int& position) const; + + bool isLineModified(const QDocumentLine& l) const; + bool hasLineEverBeenModified(const QDocumentLine& l) const; + + virtual void draw(QPainter *p, PaintContext& cxt); + + void execute(QDocumentCommand *cmd); + + inline QDocumentPrivate* impl() { return m_impl; } + + QDocumentConstIterator begin() const; + QDocumentConstIterator end() const; + + QDocumentConstIterator iterator(int ln) const; + QDocumentConstIterator iterator(const QDocumentLine& l) const; + + void beginMacro(); + void endMacro(); + + QFormatScheme* formatScheme() const; + void setFormatScheme(QFormatScheme *f); + + int getNextGroupId(); + void releaseGroupId(int groupId); + void clearMatches(int groupId); + void flushMatches(int groupId); + void addMatch(int groupId, int line, int pos, int len, int format); + + static QFont font(); + static void setFont(const QFont& f); + static const QFontMetrics& fontMetrics(); + + static LineEnding defaultLineEnding(); + static void setDefaultLineEnding(LineEnding le); + + static int tabStop(); + static void setTabStop(int n); + + static WhiteSpaceMode showSpaces(); + static void setShowSpaces(WhiteSpaceMode y); + + static QFormatScheme* defaultFormatScheme(); + static void setDefaultFormatScheme(QFormatScheme *f); + + static QFormatScheme* formatFactory(); + static void setFormatFactory(QFormatScheme *f); + + static int screenLength(const QChar *d, int l, int tabStop); + static QString screenable(const QChar *d, int l, int tabStop); + + inline void markViewDirty() { formatsChanged(); } + + bool isClean() const; + + public slots: + void clear(); + + void undo(); + void redo(); + + void setClean(); + + void highlight(); + + void print(QPrinter *p); + + void clearWidthConstraint(); + void setWidthConstraint(int width); + + signals: + void cleanChanged(bool m); + + void undoAvailable(bool y); + void redoAvailable(bool y); + + void formatsChanged(); + void contentsChanged(); + + void formatsChange (int line, int lines); + void contentsChange(int line, int lines); + + void widthChanged(int width); + void heightChanged(int height); + void sizeChanged(const QSize& s); + + void lineCountChanged(int n); + void visualLineCountChanged(int n); + + void lineDeleted(QDocumentLineHandle *h); + void markChanged(QDocumentLineHandle *l, int m, bool on); + + void lineEndingChanged(int lineEnding); + + private: + QString m_leftOver; + QDocumentPrivate *m_impl; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDocument::WhiteSpaceMode) + +#endif + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocument_p.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocument_p.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QDOCUMENT_P_H_ +#define _QDOCUMENT_P_H_ + +#include "qce-config.h" + +/*! + \file qdocument_p.h + \brief Definition of the private document API +*/ + +#include "qdocument.h" +#include "qdocumentline.h" +#include "qdocumentcursor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QDocument; +class QDocumentBuffer; +class QDocumentPrivate; +class QDocumentCommand; +class QDocumentCommandBlock; + +class QLanguageDefinition; + +Q_DECLARE_TYPEINFO(QDocumentSelection, Q_PRIMITIVE_TYPE); + +#include "qdocumentline_p.h" + +#include "qdocumentcursor_p.h" + +class QCE_EXPORT QDocumentPrivate +{ + friend class QEditConfig; + + friend class QDocument; + friend class QDocumentCommand; + friend class QDocumentLineHandle; + friend class QDocumentCursorHandle; + + public: + QDocumentPrivate(QDocument *d); + ~QDocumentPrivate(); + + void execute(QDocumentCommand *cmd); + + void draw(QPainter *p, QDocument::PaintContext& cxt); + + QDocumentLineHandle* lineForPosition(int& position) const; + int position(const QDocumentLineHandle *l) const; + + QDocumentLineHandle* at(int line) const; + int indexOf(const QDocumentLineHandle *l) const; + + QDocumentIterator index(const QDocumentLineHandle *l); + QDocumentConstIterator index(const QDocumentLineHandle *l) const; + + QDocumentLineHandle* next(const QDocumentLineHandle *l) const; + QDocumentLineHandle* previous(const QDocumentLineHandle *l) const; + + void adjustWidth(int l); + //int checkWidth(QDocumentLineHandle *l, int w); + //int checkWidth(QDocumentLineHandle *l, const QString& s); + + void setWidth(); + void setHeight(); + + static void setFont(const QFont& f); + + void beginChangeBlock(); + void endChangeBlock(); + + inline int maxMarksPerLine() const + { return m_maxMarksPerLine; } + + inline bool hasMarks() const + { return m_marks.count(); } + + QList marks(QDocumentLineHandle *h) const; + + void addMark(QDocumentLineHandle *h, int mid); + void toggleMark(QDocumentLineHandle *h, int mid); + void removeMark(QDocumentLineHandle *h, int mid); + + int findNextMark(int id, int from = 0, int until = -1); + int findPreviousMark(int id, int from = -1, int until = 0); + + int getNextGroupId(); + void releaseGroupId(int groupId); + void clearMatches(int gid); + void flushMatches(int gid); + void addMatch(int gid, int line, int pos, int len, int format); + + void emitFormatsChange (int line, int lines); + void emitContentsChange(int line, int lines); + + int visualLine(int textLine) const; + int textLine(int visualLine, int *wrap = 0) const; + void hideEvent(int line, int count); + void showEvent(int line, int count); + + void setWidth(int width); + + void emitFormatsChanged(); + void emitContentsChanged(); + + void emitLineDeleted(QDocumentLineHandle *h); + void emitMarkChanged(QDocumentLineHandle *l, int m, bool on); + + inline QDocumentIterator begin() { return m_lines.begin(); } + inline QDocumentIterator end() { return m_lines.end(); } + + inline QDocumentConstIterator constBegin() const { return m_lines.constBegin(); } + inline QDocumentConstIterator constEnd() const { return m_lines.constEnd(); } + + protected: + void updateHidden(int line, int count); + void updateWrapped(int line, int count); + + void insertLines(int after, const QList& l); + void removeLines(int after, int n); + + void emitWidthChanged(); + void emitHeightChanged(); + + void updateFormatCache(); + void setFormatScheme(QFormatScheme *f); + void tunePainter(QPainter *p, int fid); + + private: + QDocument *m_doc; + QUndoStack m_commands; + QDocumentCursor *m_editCursor; + + bool m_suspend, m_deleting; + QQueue > m_notifications; + + QMap m_hidden; + QMap m_wrapped; + QVector< QPair > m_largest; + + struct Match + { + int line; + QFormatRange range; + QDocumentLineHandle *h; + }; + + struct MatchList : QList + { + MatchList() : index(0) {} + + int index; + }; + + int m_lastGroupId; + QList m_freeGroupIds; + QHash m_matches; + + bool m_constrained; + int m_width, m_height; + + int m_tabStop; + static int m_defaultTabStop; + + static QFont *m_font; + static bool m_fixedPitch; + static QFontMetrics *m_fontMetrics; + static int m_leftMargin; + static QDocument::WhiteSpaceMode m_showSpaces; + static QDocument::LineEnding m_defaultLineEnding; + static int m_lineHeight; + static int m_lineSpacing; + static int m_spaceWidth; + static int m_ascent; + static int m_descent; + static int m_leading; + static int m_wrapMargin; + + QFormatScheme *m_formatScheme; + QLanguageDefinition *m_language; + static QFormatScheme *m_defaultFormatScheme; + + QVector m_fonts; + + static QList m_documents; + + int m_maxMarksPerLine; + QHash > m_marks; + QHash > m_status; + + int _nix, _dos, _mac; + QString m_lineEndingString; + QDocument::LineEnding m_lineEnding; + + QDateTime m_lastModified; + + QDocumentBuffer *m_buffer; + QVector m_lines; +}; + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentbuffer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentbuffer.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,373 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qdocumentbuffer.h" + +/* + Notes on design : + + The idea is to fragment the storage to workaround the issue of Qt + containers alocation model (too much memory usage when number of items + grow) and provide faster modification of the content. + + The number one goal is to keep lookup by index as fast possible to avoid + significant impact on document iteration/drawing speed + + for such a storage to be useful the block size may not be fixed but instead + must be kept around an "average" value. +*/ + +QDocumentBuffer::QDocumentBuffer() + : m_safetyRoom(10), m_optimalSize(1000), m_forkThresold(1500), m_mergeThresold(100) +{ + +} + +QDocumentBuffer::~QDocumentBuffer() +{ + qDeleteAll(m_blocks); +} + +// for loading +void QDocumentBuffer::appendLine(QDocumentLineHandle *l) +{ + Block *b = m_blocks.last(); + + if ( b->size() >= m_optimalSize ) + { + b = new Block(); + m_blocks << b; + } + + b->lines.append(l); +} + +int QDocumentBuffer::blockForLine(int index) const +{ + // TODO : binary search? + for ( int i = 0; i < m_blocks.count(); ++i ) + { + if ( index < m_blocks.at(i)->end ) + return i; + } + + // too high... + return -1; +} + +QDocumentLineHandle* QDocumentBuffer::at(int index) const +{ + int blockIndex = blockForLine(index); + + return blockIndex != -1 ? m_blocks.at(blockIndex)->at(index) : 0; +} + +void QDocumentBuffer::insertLine(int index, QDocumentLineHandle *l) +{ + int blockIndex = blockForLine(index); + + if ( blockIndex == -1 ) + { + qWarning("cannot insert line at pos %i", index); + return; + } + + Block *b = m_blocks.at(blockIndex); + + if ( (b->size() + 1) >= m_forkThresold ) + { + // split block + int bounds = b->start + m_optimalSize; + + Block *nb = new Block(bounds); + nb->insert(bounds, b->lines.constData() + m_optimalSize, b->size() - m_optimalSize); + nb->lines.append(l); + nb->end = bounds + nb->size(); + m_blocks.insert(blockIndex + 1, nb); + + b->lines.resize(m_optimalSize); + b->end = bounds; + + blockIndex += 2; + } else { + b->insert(index, l); + } + + // update block boundaries + while ( blockIndex < m_blocks.count() ) + { + b = m_blocks.at(blockIndex); + ++b->start; + ++b->end; + } +} + +void QDocumentBuffer::removeLine(int index) +{ + int blockIndex = blockForLine(index); + + if ( blockIndex == -1 ) + { + qWarning("cannot remove line at pos %i", index); + return; + } + + Block *b = m_blocks.at(blockIndex); + + b->remove(index); + --b->end; + + // if block too small, merge it with blocks around? + + if ( !b->size() ) + { + // remove empty block + m_blocks.remove(blockIndex); + delete b; + } else if ( b->size() < m_mergeThresold ) { + // "merge" block with its neighbors + + int n = b->size(); + n += qMin(1, n / m_safetyRoom); + + int roomPrev = blockIndex ? m_forkThresold - m_blocks.at(blockIndex - 1)->size() : 0; + int roomNext = blockIndex + 1 < m_blocks.count() ? m_forkThresold - m_blocks.at(blockIndex + 1)->size() : 0; + + bool maxPrev = false; + int maxRoom = 0, minRoom = 0; + Block *moreRoom = 0, *lessRoom = 0; + + if ( roomPrev > roomNext ) + { + maxPrev = true; + maxRoom = roomPrev; + moreRoom = m_blocks.at(blockIndex - 1); + + minRoom = roomNext; + + if ( roomNext ) + lessRoom = m_blocks.at(blockIndex + 1); + } else { + maxRoom = roomNext; + + if ( roomNext ) + moreRoom = m_blocks.at(blockIndex + 1); + + minRoom = roomPrev; + + if ( roomPrev ) + lessRoom = m_blocks.at(blockIndex - 1); + } + + if ( maxRoom > n ) + { + // put everything in one + moreRoom->lines << b->lines; + moreRoom->end += b->size(); + + m_blocks.remove(blockIndex); + delete b; + } else if ( (maxRoom + minRoom) > n ) { + // try to alloc evenly + int part = b->size() * maxRoom / (maxRoom + minRoom); + + if ( maxPrev ) + { + moreRoom->append(b->lines.constData(), part); + lessRoom->prepend(b->lines.constData() + part, b->size() - part); + } else { + moreRoom->prepend(b->lines.constData(), part); + lessRoom->append(b->lines.constData() + part, b->size() - part); + } + + moreRoom->end += part; + lessRoom->end += b->size() - part; + + m_blocks.remove(blockIndex); + delete b; + } else { + // cannot merge simply... let's forget about it for now as it is not vital + ++blockIndex; + } + } else { + ++blockIndex; + } + + // update block boundaries + while ( blockIndex < m_blocks.count() ) + { + b = m_blocks.at(blockIndex); + --b->start; + --b->end; + } +} + +void QDocumentBuffer::insertLines(int after, const QVector& l) +{ + int index = after + 1; + int blockIndex = blockForLine(index); + + if ( blockIndex == -1 ) + { + qWarning("cannot insert line at pos %i", index); + return; + } + + int n = l.count(); + Block *b = m_blocks.at(blockIndex); + + if ( (b->size() + 1) >= m_forkThresold ) + { + // split block + int bounds = b->start + m_optimalSize; + + Block *nb = new Block(bounds); + nb->insert(bounds, b->lines.constData() + m_optimalSize, b->size() - m_optimalSize); + nb->append(l.constData(), n); + nb->end = bounds + nb->size(); + m_blocks.insert(blockIndex + 1, nb); + + b->lines.resize(m_optimalSize); + b->end = bounds; + + blockIndex += 2; + } else { + b->insert(index, l.constData(), n); + } + + // update block boundaries + while ( blockIndex < m_blocks.count() ) + { + b = m_blocks.at(blockIndex); + b->start += n; + b->end += n; + } +} + +void QDocumentBuffer::removeLines(int after, int n) +{ + int index = after + 1; + int blockIndex = blockForLine(index); + + if ( blockIndex == -1 ) + { + qWarning("cannot remove line at pos %i", index); + return; + } + + // update block boundaries + int count = n; + Block *b = m_blocks.at(blockIndex); + + while ( count > 0 ) + { + int room = b->end - index; + int toRem = qMin(room, count); + + b->remove(index, toRem); + b->end -= toRem; + count -= toRem; + + if ( !b->size() ) + { + m_blocks.remove(blockIndex); + delete b; + } else { + ++blockIndex; + } + + if ( blockIndex >= m_blocks.count() ) + break; + + b = m_blocks.at(blockIndex); + b->start -= toRem; + } + + if ( index ) + { + qWarning("Troubles in line removal"); + } + + if ( b->size() < m_mergeThresold ) + { + // "merge" block with its neighbors + + int sz = b->size(); + sz += qMin(1, sz / m_safetyRoom); + + int roomPrev = blockIndex ? m_forkThresold - m_blocks.at(blockIndex - 1)->size() : 0; + int roomNext = blockIndex + 1 < m_blocks.count() ? m_forkThresold - m_blocks.at(blockIndex + 1)->size() : 0; + + bool maxPrev = false; + int maxRoom = 0, minRoom = 0; + Block *moreRoom = 0, *lessRoom = 0; + + if ( roomPrev > roomNext ) + { + maxPrev = true; + maxRoom = roomPrev; + moreRoom = m_blocks.at(blockIndex - 1); + + minRoom = roomNext; + + if ( roomNext ) + lessRoom = m_blocks.at(blockIndex + 1); + } else { + maxRoom = roomNext; + + if ( roomNext ) + moreRoom = m_blocks.at(blockIndex + 1); + + minRoom = roomPrev; + + if ( roomPrev ) + lessRoom = m_blocks.at(blockIndex - 1); + } + + if ( maxRoom > sz ) + { + // put everything in one + moreRoom->lines << b->lines; + moreRoom->end += b->size(); + + m_blocks.remove(blockIndex); + delete b; + } else if ( (maxRoom + minRoom) > sz ) { + // try to alloc evenly + int part = b->size() * maxRoom / (maxRoom + minRoom); + + moreRoom->append(b->lines.constData(), part); + moreRoom->end += part; + lessRoom->end += b->size() - part; + lessRoom->append(b->lines.constData() + part, b->size() - part); + + m_blocks.remove(blockIndex); + delete b; + } else { + // cannot merge simply... let's forget about it for now as it is not vital + ++blockIndex; + } + } else { + ++blockIndex; + } + + // update block boundaries + while ( blockIndex < m_blocks.count() ) + { + b = m_blocks.at(blockIndex); + b->start -= n; + b->end -= n; + } +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentbuffer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentbuffer.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QDOCUMENT_BUFFER_H_ +#define _QDOCUMENT_BUFFER_H_ + +#include "qce-config.h" + +#include + +#include "qdocumentline_p.h" + +class QDocumentLineHandle; + +class QCE_EXPORT QDocumentBuffer +{ + friend class QDocumentLineHandle; + + public: + class iterator + { + public: + iterator(const iterator& i); + + bool atEnd() const; + + int lineNumber() const; + QDocumentLineHandle* lineHandle() const; + + void move(int numLines); + + protected: + iterator(QDocumentBuffer *buffer, int block, int line); + + private: + int m_block; + int m_line; + QDocumentBuffer *m_buffer; + }; + + QDocumentBuffer(); + ~QDocumentBuffer(); + + QDocumentLineHandle* at(int index) const; + + void appendLine(QDocumentLineHandle *l); + + void insertLine(int index, QDocumentLineHandle *l); + void removeLine(int index); + + void insertLines(int after, const QVector& l); + void removeLines(int after, int n); + + private: + static void cleanHelper(QVector& l) + { + foreach ( QDocumentLineHandle *h, l ) + h->deref(); + } + + struct Block + { + inline Block() : start(-1), end(-1) {} + inline Block(int line) : start(line), end(line) {} + ~Block() { cleanHelper(lines); } + + inline void move(int numLines) { start += numLines; end += numLines; } + + inline int size() const { return lines.count(); } + inline QDocumentLineHandle* at(int index) const { return lines.at(index - start); } + + inline void append(QDocumentLineHandle *h) { lines.append(h); } + inline void prepend(QDocumentLineHandle *h) { lines.prepend(h); } + inline void insert(int index, QDocumentLineHandle *h) { lines.insert(index - start, h); } + inline void insert(int index, const QDocumentLineHandle* const* l, int n) + { + QDocumentLineHandle **d = const_cast(l); + + int i = index - start; + lines.insert(i, n, 0); + + while ( n ) + { + lines[i++] = *d; + ++d; + --n; + } + } + + inline void append(const QDocumentLineHandle* const* l, int n) + { + QDocumentLineHandle **d = const_cast(l); + + int i = lines.count(); + lines.insert(i, n, 0); + + while ( n ) + { + lines[i++] = *d; + ++d; + --n; + } + } + + inline void prepend(const QDocumentLineHandle* const* l, int n) + { + QDocumentLineHandle **d = const_cast(l); + + int i = 0; + lines.insert(i, n, 0); + + while ( n ) + { + lines[i++] = *d; + ++d; + --n; + } + } + + inline void remove(int index) { lines.remove(index - start); } + inline void remove(int index, int count) { lines.remove(index - start, qMin(count, end - index)); } + + int start, end; + QVector lines; + }; + + int blockForLine(int index) const; + + int m_safetyRoom; + int m_optimalSize; + int m_forkThresold; + int m_mergeThresold; + + QVector m_blocks; +}; + +#endif // !_QDOCUMENT_BUFFER_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentcommand.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentcommand.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,946 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qdocumentcommand.h" + +/*! + \file qdocumentcommand.cpp + \brief Implementation of the QDocumentCommand class and its basic heirs. +*/ + +#include "qdocument_p.h" + +/*! + \ingroup document + @{ +*/ + +/*! + \class QDocumentCommand + \brief The base class for document editing command +*/ + +QList QDocumentCommand::m_autoUpdated; + +/*! + \brief ctor +*/ +QDocumentCommand::QDocumentCommand(Command c, QDocument *d, QDocumentCommand *p) + : QUndoCommand(p), + m_state(false), m_first(true), m_doc(d), + m_redoOffset(0), m_undoOffset(0), + m_silent(false), m_keepAnchor(false), m_command(c), m_cursor(0) +{ + +} + +/*! + \brief dtor +*/ +QDocumentCommand::~QDocumentCommand() +{ + if ( m_cursor ) + { + // release the handle + m_cursor->deref(); + } +} + +/*! + \return command identifier +*/ +int QDocumentCommand::id() const +{ + return m_command; +} + +/*! + \brief Attempts to merge with another command + + Command merging is not implemented. +*/ +bool QDocumentCommand::mergeWith(const QUndoCommand *) +{ + return false; +} + +/*! + \brief Redo the command +*/ +void QDocumentCommand::redo() +{ + QUndoCommand::redo(); +} + +/*! + \brief Undo the command +*/ +void QDocumentCommand::undo() +{ + QUndoCommand::undo(); +} + +/*! + \return whether the command is silent + + Silent command do not update the editing cursor of the host document. +*/ +bool QDocumentCommand::isSilent() const +{ + return m_silent; +} + +/*! + \brief Set whether the command is silent +*/ +void QDocumentCommand::setSilent(bool y) +{ + m_silent = y; +} + +/*! + \return whether the command preserve selection of the target cursor + + When this property is true, cursor adjustement upon command execution + will preserve the anchor of target cursor and only alter its position + thus keeping a selection. + + \note This is disabled by default +*/ +bool QDocumentCommand::keepAnchor() const +{ + return m_keepAnchor; +} + +/*! + \brief Set whether the command preserve selection of the target cursor + + \note This is disabled by default +*/ +void QDocumentCommand::setKeepAnchor(bool y) +{ + m_keepAnchor = y; +} + +/*! + \brief Set the target cursor + + The position of the target cursor is update upon undo() and redo() +*/ +void QDocumentCommand::setTargetCursor(QDocumentCursorHandle *h) +{ + if ( m_cursor ) + { + // release the handle + m_cursor->deref(); + } + + m_cursor = h; + + if ( m_cursor ) + { + // make sure the handle does not get deleted while the command knows it + m_cursor->ref(); + } +} + +/*! + \brief ? +*/ +void QDocumentCommand::setRedoOffset(int off) +{ + m_redoOffset = off; +} + +/*! + \brief ? +*/ +void QDocumentCommand::setUndoOffset(int off) +{ + m_undoOffset = off; +} + +/*! + \brief Insert some text + \param line target line + \param pos target text position within line + \param s text to insert + + This helper method is provided so that subclasses may actually + modify the document contents without using private API. +*/ +void QDocumentCommand::insertText(int line, int pos, const QString& s) +{ + if ( !m_doc ) + return; + + QDocumentPrivate *pd = m_doc->impl(); + QDocumentLineHandle *h = pd->m_lines.at(line); + + if ( !h ) + return; + + h->textBuffer().insert(pos, s); + h->shiftOverlays(pos, s.length()); + + pd->adjustWidth(line); +} + +/*! + \brief Remove some text + \param line target line + \param pos target text position within line + \param length length of the text to remove + + This helper method is provided so that subclasses may actually + modify the document contents without using private API. +*/ +void QDocumentCommand::removeText(int line, int pos, int length) +{ + if ( !m_doc ) + return; + + QDocumentPrivate *pd = m_doc->impl(); + QDocumentLineHandle *h = pd->m_lines.at(line); + + if ( !h || !length ) + return; + + h->textBuffer().remove(pos, length); + h->shiftOverlays(pos, -length); + + pd->adjustWidth(line); +} + +/*! + \brief Insert some lines in the host document + \param after where to insert lines (line number) + \param l list of lines to insert + + This helper method is provided so that subclasses may actually + modify the document contents without using too much private API + (QDocumentLineHandle is part of the private API...) +*/ +void QDocumentCommand::insertLines(int after, const QList& l) +{ + if ( l.isEmpty() || !m_doc->impl()->at(after) ) + return; + + m_doc->impl()->insertLines(after, l); +} + +void QDocumentCommand::updateCursorsOnInsertion(int line, int column, int prefixLength, int numLines, int suffixLength) +{ + //qDebug("inserting %i lines at (%i, %i) with (%i : %i) bounds", numLines, line, column, prefixLength, suffixLength); + + foreach ( QDocumentCursorHandle *ch, m_autoUpdated ) + { + if ( ch == m_cursor || ch->document() != m_doc ) + continue; + + //printf("[[watch:0x%x(%i, %i)]]", ch, ch->m_begLine, ch->m_begOffset); + + // TODO : better selection handling + if ( ch->hasSelection() ) + { + int lbeg = line, cbeg = column, lend = line, cend = column; + + ch->intersectBoundaries(lbeg, cbeg, lend, cend); + + if ( lbeg == line && cbeg == column ) + { + //qDebug("expand (%i, %i : %i, %i)", ch->m_begLine, ch->m_begOffset, ch->m_endLine, ch->m_endOffset); + if ( (ch->m_begLine > ch->m_endLine) || (ch->m_begLine == ch->m_endLine && ch->m_begOffset > ch->m_endOffset) ) + { + if ( numLines ) + { + if ( ch->m_begLine == line ) + ch->m_begOffset -= column; + ch->m_begLine += numLines; + ch->m_begOffset += suffixLength; + } else if ( ch->m_begLine == line ) { + ch->m_begOffset += prefixLength; + } + } else { + if ( numLines ) + { + if ( ch->m_endLine == line ) + ch->m_endOffset -= column; + ch->m_endLine += numLines; + ch->m_endOffset += suffixLength; + } else if ( ch->m_endLine == line ) { + ch->m_endOffset += prefixLength; + } + } + //qDebug("into (%i, %i : %i, %i)", ch->m_begLine, ch->m_begOffset, ch->m_endLine, ch->m_endOffset); + continue; + } + } + + // move + if ( ch->m_begLine > line ) + { + ch->m_begLine += numLines; + } else if ( ch->m_begLine == line && ch->m_begOffset >= column ) { + if ( numLines ) + { + ch->m_begLine += numLines; + ch->m_begOffset -= column; + ch->m_begOffset += suffixLength; + } else { + ch->m_begOffset += prefixLength; + } + } + + if ( ch->m_endLine > line ) + { + ch->m_endLine += numLines; + } else if ( ch->m_endLine == line && ch->m_endOffset >= column ) { + if ( numLines ) + { + ch->m_endLine += numLines; + ch->m_endOffset -= column; + ch->m_endOffset += suffixLength; + } else { + ch->m_endOffset += prefixLength; + } + } + } +} + +void QDocumentCommand::updateCursorsOnDeletion(int line, int column, int prefixLength, int numLines, int suffixLength) +{ + //qDebug("removing %i lines at (%i, %i) with (%i : %i) bounds", numLines, line, column, prefixLength, suffixLength); + + foreach ( QDocumentCursorHandle *ch, m_autoUpdated ) + { + if ( ch == m_cursor || ch->document() != m_doc ) + continue; + + //printf("[[watch:0x%x(%i, %i)]]", ch, ch->m_begLine, ch->m_begOffset); + + // TODO : better selection handling + if ( ch->hasSelection() ) + { + int lbeg = line, cbeg = column, lend = line + numLines, cend = numLines ? suffixLength : column + prefixLength; + + ch->intersectBoundaries(lbeg, cbeg, lend, cend); + //qDebug("intersection (%i, %i : %i, %i)", lbeg, cbeg, lend, cend); + + if ( lbeg != -1 && cbeg != -1 && lend != -1 && cend != -1 ) + { + //qDebug("shrink (%i, %i : %i, %i)", ch->m_begLine, ch->m_begOffset, ch->m_endLine, ch->m_endOffset); + //qDebug("of intersection (%i, %i : %i, %i)", lbeg, cbeg, lend, cend); + ch->substractBoundaries(lbeg, cbeg, lend, cend); + //qDebug("into (%i, %i : %i, %i)", ch->m_begLine, ch->m_begOffset, ch->m_endLine, ch->m_endOffset); + continue; + } + } + + // move + if ( ch->m_begLine > line + numLines ) + { + ch->m_begLine -= numLines; + } else if ( ch->m_begLine == line + numLines && ch->m_begOffset >= suffixLength ) { + if ( numLines ) + { + ch->m_begLine -= numLines; + ch->m_begOffset -= suffixLength; + ch->m_begOffset += column; + } else { + ch->m_begOffset -= prefixLength; + } + } else if ( ch->m_begLine > line || (ch->m_begLine == line && ch->m_begOffset > column) ) { + // cursor will become invalid in an unrecoverable way... + + } + + if ( ch->m_endLine > line + numLines ) + { + ch->m_endLine -= numLines; + } else if ( ch->m_endLine == line + numLines && ch->m_endOffset >= suffixLength ) { + if ( numLines ) + { + ch->m_endLine -= numLines; + ch->m_endOffset -= suffixLength; + ch->m_endOffset += column; + } else { + ch->m_endOffset -= prefixLength; + } + } else if ( ch->m_endLine > line || (ch->m_endLine == line && ch->m_endOffset > column) ) { + // cursor will become invalid in an unrecoverable way... + // except that it should have intersected in the first place... + } + } +} + +/*! + \brief Remove some lines from the host document + \param after where to remove lines (line number) + \param n number of lines to remove + + This helper method is provided so that subclasses may actually + modify the document contents without using the private API. +*/ +void QDocumentCommand::removeLines(int after, int n) +{ + if ( n <= 0 || !m_doc->impl()->at(after) || !m_doc->impl()->at(after + n) ) + return; + + m_doc->impl()->removeLines(after, n); +} + +/*! + \brief Update the target cursor + \param l target line + \param offset target text position within target line +*/ +void QDocumentCommand::updateTarget(int l, int offset) +{ + //QDocumentLineHandle *h = m_doc->impl()->at(l); + + // update command sender if any + if ( m_cursor ) + { +// qDebug("moving cursor [0x%x:beg] from (%i, %i) to line (%i, %i) as updating", +// m_cursor, +// m_cursor->m_begLine, +// m_cursor->m_begOffset, +// l, +// offset +// ); +// + while ( l && (offset < 0) ) + { + --l; + offset += m_doc->line(l).length() + 1; + } + + while ( (l + 1) < m_doc->lines() && m_doc->line(l).length() < offset ) + { + offset -= m_doc->line(l).length() + 1; + ++l; + } + + if ( !m_keepAnchor ) + { + m_cursor->m_endLine = -1; + m_cursor->m_endOffset = -1; + } else if ( m_cursor->m_endLine == -1 ) { + m_cursor->m_endLine = m_cursor->m_begLine; + m_cursor->m_endOffset = m_cursor->m_begOffset; + } + + m_cursor->m_begLine = qMax(0, l); + m_cursor->m_begOffset = qMax(0, offset); + + m_cursor->refreshColumnMemory(); + } +} + +/*! + \return whether a given cursor is auto updated +*/ +bool QDocumentCommand::isAutoUpdated(const QDocumentCursorHandle *h) +{ + return m_autoUpdated.contains(const_cast(h)); +} + +/*! + \brief Enable auto update for a given cursor +*/ +void QDocumentCommand::enableAutoUpdate(QDocumentCursorHandle *h) +{ + //qDebug("up(0x%x)", h); + + if ( !m_autoUpdated.contains(h) ) + m_autoUpdated << h; +} + +/*! + \brief Disable auto update for a given cursor +*/ +void QDocumentCommand::disableAutoUpdate(QDocumentCursorHandle *h) +{ + //qDebug("no-up(0x%x)", h); + m_autoUpdated.removeAll(h); +} + +void QDocumentCommand::discardHandlesFromDocument(QDocument *d) +{ + int idx = 0; + + while ( idx < m_autoUpdated.count() ) + { + if ( m_autoUpdated.at(idx)->document() == d ) + m_autoUpdated.removeAt(idx); + else + ++idx; + } +} + +/*! + \brief Change the modification status of a line +*/ +void QDocumentCommand::markRedone(QDocumentLineHandle *h, bool firstTime) +{ + QHash >::iterator it = m_doc->impl()->m_status.find(h); + + if ( it != m_doc->impl()->m_status.end() ) + { + if ( firstTime && it->first < it->second ) + it->second = -1; + + ++it->first; + } else { + m_doc->impl()->m_status[h] = qMakePair(1, 0); + } +} + +/*! + \brief Change the modifiaction status of a line +*/ +void QDocumentCommand::markUndone(QDocumentLineHandle *h) +{ + QHash >::iterator it = m_doc->impl()->m_status.find(h); + + if ( it != m_doc->impl()->m_status.end() ) + { + --it->first; + } else { + qDebug("warning: status data and/or undo stack corrupted..."); + m_doc->impl()->m_status[h] = qMakePair(-1, 0); + } +} + +//////////////////////////// + +/*! + \class QDocumentInsertCommand + \brief A specialized command to insert text +*/ + +/*! + \brief ctor + \param l target line + \param offset target text position within target line + \param text text to insert (can contain line feeds, "\n", which will result in the creation of new lines) + \param doc host document + \param p parent command +*/ +QDocumentInsertCommand::QDocumentInsertCommand( int l, int offset, + const QString& text, + QDocument *doc, + QDocumentCommand *p) + : QDocumentCommand(Insert, doc, p) +{ + QStringList lines = text.split(QLatin1Char('\n'), QString::KeepEmptyParts); + + if ( !m_doc || text.isEmpty() ) + qFatal("Invalid insert command"); + + m_data.lineNumber = l; + m_data.startOffset = offset; + + m_data.begin = lines.takeAt(0); + m_data.endOffset = lines.count() ? lines.last().length() : -1; + + foreach ( const QString& s, lines ) + m_data.handles << new QDocumentLineHandle(s, m_doc); + + QDocumentLine bl = m_doc->line(l); + + if ( m_data.handles.count() && (bl.length() > offset) ) + { + m_data.end = bl.text().mid(offset); + m_data.handles.last()->textBuffer().append(m_data.end); + } + + /* + if ( (text == "\n") && m_data.handles.isEmpty() ) + qWarning("Go fix it by hand..."); + */ +} + +/*! + \brief dtor +*/ +QDocumentInsertCommand::~QDocumentInsertCommand() +{ + if ( m_state ) + return; + + //foreach ( QDocumentLineHandle *h, m_data.handles ) + // h->deref(); + +} + +bool QDocumentInsertCommand::mergeWith(const QUndoCommand *) +{ + return false; +} + +void QDocumentInsertCommand::redo() +{ + // state : handles used by doc + m_state = true; + + //QDocumentIterator it = m_doc->impl()->index(m_data.lineNumber); + + //qDebug("inserting %i lines after %i", m_data.handles.count(), m_data.lineNumber); + + QDocumentLineHandle *hl = m_doc->impl()->at(m_data.lineNumber); + + if ( m_data.handles.count() ) + { + removeText(m_data.lineNumber, m_data.startOffset, m_data.end.count()); + } + + insertText(m_data.lineNumber, m_data.startOffset, m_data.begin); + + insertLines(m_data.lineNumber, m_data.handles); + + if ( m_data.handles.count() ) + { + QDocumentLineHandle *h = m_data.handles.last(); + + //updateTarget(h, h->text().length() - m_data.end.length()); + updateTarget(m_data.lineNumber + m_data.handles.count(), h->text().length() - m_data.end.length() + m_redoOffset); + } else { + updateTarget(m_data.lineNumber, m_data.startOffset + m_data.begin.length() + m_redoOffset); + } + + updateCursorsOnInsertion(m_data.lineNumber, m_data.startOffset, m_data.begin.length(), m_data.handles.count(), m_data.endOffset); + + m_doc->impl()->emitContentsChange(m_data.lineNumber, m_data.handles.count() + 1); + + markRedone(hl, m_first); + + foreach ( QDocumentLineHandle *h, m_data.handles ) + markRedone(h, m_first); + + //m_doc->impl()->emitContentsChanged(); + m_first = false; +} + +void QDocumentInsertCommand::undo() +{ + // state : handles !used by doc + m_state = false; + + //QDocumentIterator it = m_doc->impl()->index(m_data.line); + + QDocumentLineHandle *hl = m_doc->impl()->at(m_data.lineNumber); + + removeLines(m_data.lineNumber, m_data.handles.count()); + removeText(m_data.lineNumber, m_data.startOffset, m_data.begin.count()); + + if ( m_data.handles.count() ) + { + insertText(m_data.lineNumber, m_data.startOffset, m_data.end); + } + + updateTarget(m_data.lineNumber, m_data.startOffset + m_undoOffset); + + updateCursorsOnDeletion(m_data.lineNumber, m_data.startOffset, m_data.begin.length(), m_data.handles.count(), m_data.endOffset); + + m_doc->impl()->emitContentsChange(m_data.lineNumber, m_data.handles.count() + 1); + + markUndone(hl); + + foreach ( QDocumentLineHandle *h, m_data.handles ) + markUndone(h); + + //m_doc->impl()->emitContentsChanged(); +} + +/////////////////////////// + +/*! + \class QDocumentEraseCommand + \brief A specialized command to erase text +*/ + +/*! + \brief ctor + \param bl begin line of the target area + \param bo begin text position of the target area + \param el end line of the target area + \param eo end text position of the target area + \param doc host document + \param p parent command +*/ +QDocumentEraseCommand::QDocumentEraseCommand( int bl, int bo, + int el, int eo, + QDocument *doc, + QDocumentCommand *p) + : QDocumentCommand(Erase, doc, p) +{ + QDocumentLineHandle *start = m_doc->impl()->at(bl), + *end = m_doc->impl()->at(el); + + QDocumentConstIterator it = m_doc->impl()->begin() + bl; //index(start); + + m_data.lineNumber = bl; + m_data.startOffset = bo; + + if ( start == end ) + { + m_data.begin = start->text().mid(bo, eo - bo); + + m_data.end = QString(); + m_data.endOffset = -1; + + } else { + m_data.begin = start->text().mid(bo); + + m_data.endOffset = eo; + m_data.end = end->text().mid(eo); + + do + { + m_data.handles << *(++it); + } while ( *it != end ); + } + + m_state = true; +} + +/*! + \brief dtor +*/ +QDocumentEraseCommand::~QDocumentEraseCommand() +{ + if ( m_state ) + return; + + //qDeleteAll(m_data.handles); +} + +bool QDocumentEraseCommand::mergeWith(const QUndoCommand *) +{ + return false; +} + +void QDocumentEraseCommand::redo() +{ + // state : handles !used by doc + m_state = false; + + //QDocumentIterator it = m_doc->impl()->index(m_data.line); + + QDocumentLineHandle *hl = m_doc->impl()->at(m_data.lineNumber); + + if ( m_data.handles.isEmpty() ) + { + removeText(m_data.lineNumber, m_data.startOffset, m_data.begin.count()); + + m_doc->impl()->emitContentsChange(m_data.lineNumber, 1); + + } else { + removeText(m_data.lineNumber, m_data.startOffset, m_data.begin.count()); + + if ( m_data.endOffset != -1 ) + insertText(m_data.lineNumber, m_data.startOffset, m_data.end); + + removeLines(m_data.lineNumber, m_data.handles.count()); + + m_doc->impl()->emitContentsChange(m_data.lineNumber, m_data.handles.count() + 1); + } + + updateTarget(m_data.lineNumber, m_data.startOffset + m_redoOffset); + + updateCursorsOnDeletion(m_data.lineNumber, m_data.startOffset, m_data.begin.length(), m_data.handles.count(), m_data.endOffset); + + markRedone(hl, m_first); + + foreach ( QDocumentLineHandle *h, m_data.handles ) + markRedone(h, m_first); + + //m_doc->impl()->emitContentsChanged(); + m_first = false; +} + +void QDocumentEraseCommand::undo() +{ + // state : handles used by doc + m_state = true; + + //QDocumentIterator it = m_doc->impl()->index(m_data.line); + + QDocumentLineHandle *hl = m_doc->impl()->at(m_data.lineNumber); + + if ( m_data.handles.count() ) + { + insertLines(m_data.lineNumber, m_data.handles); + + if ( m_data.endOffset != -1 ) + removeText(m_data.lineNumber, m_data.startOffset, m_data.end.count()); + + insertText(m_data.lineNumber, m_data.startOffset, m_data.begin); + + m_doc->impl()->emitContentsChange(m_data.lineNumber, m_data.handles.count() + 1); + } else { + + insertText(m_data.lineNumber, m_data.startOffset, m_data.begin); + + m_doc->impl()->emitContentsChange(m_data.lineNumber, 1); + } + + if ( m_data.handles.count() ) + { + QDocumentLineHandle *h = m_data.handles.last(); + + //updateTarget(h, h->text().length() - m_data.end.length()); + updateTarget(m_data.lineNumber + m_data.handles.count(), h->text().length() - m_data.end.length() + m_undoOffset); + } else { + updateTarget(m_data.lineNumber, m_data.startOffset + m_data.begin.length() + m_undoOffset); + } + + updateCursorsOnInsertion(m_data.lineNumber, m_data.startOffset, m_data.begin.length(), m_data.handles.count(), m_data.endOffset); + + markUndone(hl); + + foreach ( QDocumentLineHandle *h, m_data.handles ) + markUndone(h); + + //m_doc->impl()->emitContentsChanged(); +} + +/////////////////////////// +/* +QDocumentReplaceCommand::QDocumentReplaceCommand(const QDocumentLine& l, int, int, const QString&) + : QDocumentCommand(Replace, l.document()) +{ + +} + +QDocumentReplaceCommand::~QDocumentReplaceCommand() +{ + +} + +bool QDocumentReplaceCommand::mergeWith(const QUndoCommand *) +{ + return false; +} + +void QDocumentReplaceCommand::redo() +{ + +} + +void QDocumentReplaceCommand::undo() +{ + +} +*/ + +////////////////////// + + +/*! + \class QDocumentCommandBlock + \brief A meta command used for command grouping +*/ + +/*! + \brief ctor + \param d host document +*/ +QDocumentCommandBlock::QDocumentCommandBlock(QDocument *d) + : QDocumentCommand(Custom, d), m_weakLocked(false) +{ + +} + +/*! + \brief dtor +*/ +QDocumentCommandBlock::~QDocumentCommandBlock() +{ + +} + +void QDocumentCommandBlock::redo() +{ + if ( isWeakLocked() ) + { + setWeakLock(false); + return; + } + + //foreach ( QDocumentCommand *c, m_commands ) + // c->redo(); + + for ( int i = 0; i < m_commands.count(); ++i ) + m_commands.at(i)->redo(); + +} + +void QDocumentCommandBlock::undo() +{ + //foreach ( QDocumentCommand *c, m_commands ) + // c->undo(); + + for ( int i = m_commands.count() - 1; i >= 0; --i ) + m_commands.at(i)->undo(); + +} + +/*! + \brief Set whether the block is weakly locked +*/ +void QDocumentCommandBlock::setWeakLock(bool l) +{ + m_weakLocked = l; +} + +/*! + \return whether the block is weakly locked + + Weak locking of command block is an obscure internal feature + which prevents the first redo() call from actually redo'ing + the grouped commands +*/ +bool QDocumentCommandBlock::isWeakLocked() const +{ + return m_weakLocked; +} + +/*! + \brief Add a command to the group + + \warning Doing that after having pushed the command on the undo/redo stack + is likely to result in corruption of the undo/redo stack +*/ +void QDocumentCommandBlock::addCommand(QDocumentCommand *c) +{ + m_commands << c; +} + +/*! + \brief Remove a command from the block + + \warning Doing that after having pushed the command on the undo/redo stack + is likely to result in corruption of the undo/redo stack +*/ +void QDocumentCommandBlock::removeCommand(QDocumentCommand *c) +{ + m_commands.removeAll(c); +} + +/*! @} */ + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentcommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentcommand.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QDOCUMENT_COMMAND_H_ +#define _QDOCUMENT_COMMAND_H_ + +#include "qce-config.h" + +/*! + \file qdocumentcommand.h + \brief Definition of the QDocumentCommand class +*/ + +#include + +#include "qdocument.h" + +class QDocumentLine; +class QDocumentLineHandle; +class QDocumentCursorHandle; + +class QCE_EXPORT QDocumentCommand : public QUndoCommand +{ + public: + enum Command + { + None, + Insert, + Erase, + Replace, + Custom + }; + + struct TextCommandData + { + QString begin, end; + int lineNumber, startOffset, endOffset; + QList handles; + }; + + QDocumentCommand(Command c, QDocument *d, QDocumentCommand *p = 0); + virtual ~QDocumentCommand(); + + virtual int id() const; + + virtual bool mergeWith(const QUndoCommand *command); + + virtual void redo(); + virtual void undo(); + + bool isSilent() const; + void setSilent(bool y); + + bool keepAnchor() const; + void setKeepAnchor(bool y); + + void setTargetCursor(QDocumentCursorHandle *h); + + void setRedoOffset(int off); + void setUndoOffset(int off); + + static bool isAutoUpdated(const QDocumentCursorHandle *h); + static void enableAutoUpdate(QDocumentCursorHandle *h); + static void disableAutoUpdate(QDocumentCursorHandle *h); + static void discardHandlesFromDocument(QDocument *d); + + protected: + bool m_state, m_first; + QDocument *m_doc; + int m_redoOffset, m_undoOffset; + + void markRedone(QDocumentLineHandle *h, bool firstTime); + void markUndone(QDocumentLineHandle *h); + + void updateTarget(int l, int offset); + + void insertText(int line, int pos, const QString& s); + void removeText(int line, int pos, int length); + + void insertLines(int after, const QList& l); + void removeLines(int after, int n); + + void updateCursorsOnInsertion(int line, int column, int prefixLength, int numLines, int suffixLength); + void updateCursorsOnDeletion(int line, int column, int prefixLength, int numLines, int suffixLength); + + private: + bool m_silent; + bool m_keepAnchor; + Command m_command; + QDocumentCursorHandle *m_cursor; + + static QList m_autoUpdated; +}; + +Q_DECLARE_TYPEINFO(QDocumentCommand::TextCommandData, Q_MOVABLE_TYPE); + +class QCE_EXPORT QDocumentInsertCommand : public QDocumentCommand +{ + public: + QDocumentInsertCommand( int l, int offset, + const QString& text, + QDocument *doc, + QDocumentCommand *p = 0); + + virtual ~QDocumentInsertCommand(); + + virtual bool mergeWith(const QUndoCommand *command); + + virtual void redo(); + virtual void undo(); + + private: + TextCommandData m_data; +}; + +class QCE_EXPORT QDocumentEraseCommand : public QDocumentCommand +{ + public: + QDocumentEraseCommand( int bl, int bo, + int el, int eo, + QDocument *doc, + QDocumentCommand *p = 0); + + virtual ~QDocumentEraseCommand(); + + virtual bool mergeWith(const QUndoCommand *command); + + virtual void redo(); + virtual void undo(); + + private: + TextCommandData m_data; +}; + +class QCE_EXPORT QDocumentCommandBlock : public QDocumentCommand +{ + public: + QDocumentCommandBlock(QDocument *d); + virtual ~QDocumentCommandBlock(); + + virtual void redo(); + virtual void undo(); + + void setWeakLock(bool l); + bool isWeakLocked() const; + + virtual void addCommand(QDocumentCommand *c); + virtual void removeCommand(QDocumentCommand *c); + + private: + bool m_weakLocked; + QList m_commands; +}; + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentcursor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentcursor.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,852 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +/*! + \file qdocumentcursor.cpp + \brief Implementation of the QDocumentCursor class +*/ + +#include "qdocumentcursor.h" + +/*! + \ingroup document + @{ +*/ + +#include "qdocument_p.h" + +#include "qdocumentline.h" + +/*! + \class QDocumentCursor + + \brief A cursor to navigate within documents and edit them + + QDocumentCursor is a central class of the public API. + + It is the best (as in fastest and easiest) way to iterate over + the content of a document. + + It is also the only class that allows to perform editing operations. + + A cursor position is defined by a line number and a text position + within the line. + + Every cursor can have one or two cursor positions. In the later + case, they delimit a selection. The first position set (before + selecting) is referred to as the "anchor" and the other (if it + is different from the anchor) is the actual cursor position. + + When the cursor does not have a selection, querying informations about + the anchor has the same result as querying informations about the cursor + position. + + Informations you can get about both the anchor and the posiotion : +
    +
  • the line number : the logical line to which the cursor position points inside the document +
  • the column number : the logical text column to which the cursor position points to, inside the pointed line. +
  • the wrapped line offset : when a cursor resides on a wrapped line, this indicates in which part of + the wrapped line it does +
  • the document position : document (x, y) coordinates corresponding to the place the cursor is drawn +
+ + The visual line to which a given cursor resides can be obtained as follows : + + \code + int visual_line = cursor.document()->visualLine(cursor.lineNumber()) + cursor.wrappedLineOffset(); + \endcode + + \note The line and column numbers passed to/returned by a cursor method + always start at zero. + + \note Quick overview of the various coordinate systems : +
    +
  • document coordinates aka viewport coordinates : (x, y) coords, in pixels, origin at the top left corner of + the rectangle occupied by the very first line of the document +
  • text coordinates : (line, column) in logical units (number of lines, number of characters) +
  • visual text coordinates : (line, column) in logical units but with a different mapping +
+*/ + +QDocumentCursor::QDocumentCursor(QDocument *doc) + : m_handle(new QDocumentCursorHandle(doc)) +{ + m_handle->ref(); +} + +QDocumentCursor::QDocumentCursor(const QDocumentCursor& cursor) + : m_handle(0) +{ + if ( cursor.m_handle ) + { + m_handle = cursor.m_handle->clone(); + m_handle->ref(); + } +} + +QDocumentCursor::QDocumentCursor(QDocument *doc, int line, int column) + : m_handle(new QDocumentCursorHandle(doc, line)) +{ + m_handle->ref(); + + m_handle->setColumnNumber(column); +} + +/* +QDocumentCursor::QDocumentCursor(const QDocumentLine& line, int column) + : m_handle(new QDocumentCursorHandle(line.document(), line.lineNumber())) +{ + m_handle->ref(); + + m_handle->setColumnNumber(column); + //movePosition(qMin(column, line.length())); +} +*/ + +QDocumentCursor::QDocumentCursor(QDocumentCursorHandle *handle) + : m_handle(handle) +{ + if ( m_handle ) + m_handle->ref(); +} + +QDocumentCursor::~QDocumentCursor() +{ + if ( m_handle ) + m_handle->deref(); +} + +QDocumentCursor QDocumentCursor::clone() const +{ + return m_handle ? QDocumentCursor(m_handle->clone()) : QDocumentCursor(); +} + +QDocumentCursor& QDocumentCursor::operator = (const QDocumentCursor& c) +{ + #if 0 + if ( m_handle ) + m_handle->deref(); + + m_handle = c.m_handle ? c.m_handle->clone() : 0; + //m_handle = c.m_handle; + + if ( m_handle ) + m_handle->ref(); + #endif + + if ( c.m_handle ) + { + if ( m_handle ) + { + m_handle->copy(c.m_handle); + } else { + m_handle = c.m_handle->clone(); + m_handle->ref(); + } + } else if ( m_handle ) { + + //qWarning("Setting a cursor to null"); + + m_handle->deref(); + m_handle = 0; + } + + return *this; +} + +/*! + \brief comparision operator + + \note If any of the operand is an invalid cursor, false is returned +*/ +bool QDocumentCursor::operator == (const QDocumentCursor& c) const +{ + if ( !m_handle || !c.m_handle ) + return false; + + return m_handle->eq(c.m_handle); +} + +/*! + \brief comparision operator + + \note If any of the operand is an invalid cursor, true is returned (to preserve logical consistency with == ) +*/ +bool QDocumentCursor::operator != (const QDocumentCursor& c) const +{ + if ( !m_handle || !c.m_handle ) + return true; + + return !m_handle->eq(c.m_handle); +} + +/*! + \brief comparision operator + + \note If any of the operand is an invalid cursor, false is returned +*/ +bool QDocumentCursor::operator < (const QDocumentCursor& c) const +{ + if ( !m_handle || !c.m_handle ) + return false; + + return m_handle->lt(c.m_handle); +} + +/*! + \brief comparision operator + + \note If any of the operand is an invalid cursor, false is returned +*/ +bool QDocumentCursor::operator > (const QDocumentCursor& c) const +{ + if ( !m_handle || !c.m_handle ) + return false; + + return m_handle->gt(c.m_handle); +} + +/*! + \brief comparision operator + + \note If any of the operand is an invalid cursor, false is returned +*/ +bool QDocumentCursor::operator <= (const QDocumentCursor& c) const +{ + if ( !m_handle || !c.m_handle ) + return false; + + return m_handle->lt(c.m_handle) || m_handle->eq(c.m_handle); +} + +/*! + \brief comparision operator + + \note If any of the operand is an invalid cursor, false is returned +*/ +bool QDocumentCursor::operator >= (const QDocumentCursor& c) const +{ + if ( !m_handle || !c.m_handle ) + return false; + + return m_handle->gt(c.m_handle) || m_handle->eq(c.m_handle); +} + +/*! + \brief comparision operator +*/ +bool QDocumentCursor::isNull() const +{ + return !m_handle || !m_handle->document() || !line().isValid(); +} + +/*! + \brief comparision operator +*/ +bool QDocumentCursor::isValid() const +{ + return m_handle && m_handle->document() && line().isValid(); +} + +/*! + \return whether the cursor is at the end of the document +*/ +bool QDocumentCursor::atEnd() const +{ + return m_handle ? m_handle->atEnd() : false; +} + +/*! + \return whether the cursor is at the begining of the document +*/ +bool QDocumentCursor::atStart() const +{ + return m_handle ? m_handle->atStart() : false; +} + +/*! + \return whether the cursor is at the end of a block +*/ +bool QDocumentCursor::atBlockEnd() const +{ + return m_handle ? m_handle->atBlockEnd() : false; +} + +/*! + \return whether the cursor is at the start of a block +*/ +bool QDocumentCursor::atBlockStart() const +{ + return m_handle ? m_handle->atBlockStart() : false; +} + +/*! + \return whether the cursor is at the end of a line + + \note this may only differ from atBlockEnd() on wrapped lines +*/ +bool QDocumentCursor::atLineEnd() const +{ + return m_handle ? m_handle->atLineEnd() : false; +} + +/*! + \return whether the cursor is at the start of a line + + \note this may only differ from atBlockStart() on wrapped lines +*/ +bool QDocumentCursor::atLineStart() const +{ + return m_handle ? m_handle->atLineStart() : false; +} + +/*! + \return the document on which this cursor operates +*/ +QDocument* QDocumentCursor::document() const +{ + return m_handle ? m_handle->document() : 0; +} + +/*! + \return the text position (within the whole document) at which this cursor is + + \note available for compat with QTextCursor and ridiculously slow : avoid whenever possible +*/ +int QDocumentCursor::position() const +{ + return m_handle ? m_handle->position() : -1; +} + +/*! + \return the text column of the anchor +*/ +int QDocumentCursor::anchorColumnNumber() const +{ + return m_handle ? m_handle->anchorColumnNumber() : -1; +} + +/*! + \return the "visual" text column of the cursor + + \note this may only differ from columnNumber() when there are tabs on the line +*/ +int QDocumentCursor::visualColumnNumber() const +{ + return m_handle ? m_handle->visualColumnNumber() : -1; +} + +/*! + \return the text column of the cursor +*/ +int QDocumentCursor::columnNumber() const +{ + return m_handle ? m_handle->columnNumber() : -1; +} + +/*! + \brief Set the text column of the cursor + \param c text column to set + \param m move mode (determines whether text will be selected) +*/ +void QDocumentCursor::setColumnNumber(int c, MoveMode m) +{ + if ( m_handle ) + m_handle->setColumnNumber(c, m); +} + +/*! + \return The line number to which the cursor points +*/ +int QDocumentCursor::lineNumber() const +{ + return m_handle ? m_handle->lineNumber() : -1; +} + +/*! + \return The line number to which the cursor points +*/ +int QDocumentCursor::anchorLineNumber() const +{ + return m_handle ? m_handle->anchorLineNumber() : -1; +} + +/*! + \return The wrapped line on which the cursor resides + + Wrapped line are "sublines" of logical lines. +*/ +int QDocumentCursor::wrappedLineOffset() const +{ + return line().wrappedLineForCursor(columnNumber()); +} + +/*! + \return The line number on which the anchor resides +*/ +int QDocumentCursor::anchorWrappedLineOffset() const +{ + return anchorLine().wrappedLineForCursor(anchorColumnNumber()); +} + +/*! + \return the document position at which the cursor is + + Document position and viewport position are two terms used interchangeably. + The only difference is the former refers to model perception (QDocument) + whereas the later refers to view perception (QEditor) +*/ +QPoint QDocumentCursor::documentPosition() const +{ + return m_handle ? m_handle->documentPosition() : QPoint(); +} + +/*! + \return the document position of the anchor +*/ +QPoint QDocumentCursor::anchorDocumentPosition() const +{ + return m_handle ? m_handle->anchorDocumentPosition() : QPoint(); +} + +QPolygon QDocumentCursor::documentRegion() const +{ + return m_handle ? m_handle->documentRegion() : QPolygon(); +} + +/*! + \return The line object on which the cursor resides +*/ +QDocumentLine QDocumentCursor::line() const +{ + return m_handle ? m_handle->line() : QDocumentLine(); +} + +/*! + \return The line object on which the anchor resides +*/ +QDocumentLine QDocumentCursor::anchorLine() const +{ + return m_handle ? m_handle->anchorLine() : QDocumentLine(); +} + +/*! + \brief Shift cursor position (text column) by a number of columns (characters) +*/ +void QDocumentCursor::shift(int offset) +{ + if ( m_handle ) + m_handle->shift(offset); +} + +/*! + \brief Set the text position of the cursor (within the whole document) + + Remark made about position() applies. +*/ +void QDocumentCursor::setPosition(int pos, MoveMode m) +{ + if ( m_handle ) + m_handle->setPosition(pos, m); +} + +/*! + \brief Moves the cursor position + \param offset number of times the selected move will be done + \param op movement type + \param m movement mode (whether to select) + \return true on succes +*/ +bool QDocumentCursor::movePosition(int offset, MoveOperation op, MoveMode m) +{ + return m_handle ? m_handle->movePosition(offset, op, m) : false; +} + +/*! + \brief Jump to another cursor position + \param line target line number + \param colum target text column +*/ +void QDocumentCursor::moveTo(int line, int column) +{ + if ( m_handle ) + m_handle->moveTo(line, column); +} + +/*! + \brief Jump to the position of another cursor + \param c target cursor +*/ +void QDocumentCursor::moveTo(const QDocumentCursor &c) +{ + if ( m_handle ) + m_handle->moveTo(c); +} + +/*! + \brief Jump to another cursor position + \param l target line + \param column target text column + + \note Calls QDocumentLine::lineNumber() => SLOW : avoid whenever possible +*/ +void QDocumentCursor::moveTo(const QDocumentLine &l, int column) +{ + if ( m_handle ) + m_handle->moveTo(l.lineNumber(), column); +} + +/*! + \return the character at the position immediately after the cursor +*/ +QChar QDocumentCursor::nextChar() const +{ + return m_handle ? m_handle->nextChar() : QChar(); +} + +/*! + \return the character at the position immediately before the cursor +*/ +QChar QDocumentCursor::previousChar() const +{ + return m_handle ? m_handle->previousChar() : QChar(); +} + +/*! + \brief Delete the character at the position immediately after the cursor +*/ +void QDocumentCursor::deleteChar() +{ + if ( m_handle ) + m_handle->deleteChar(); +} + +/*! + \brief Delete the character at the position immediately before the cursor +*/ +void QDocumentCursor::deletePreviousChar() +{ + if ( m_handle ) + m_handle->deletePreviousChar(); +} + +/*! + \brief erase the whole line the cursor is on, newline included +*/ +void QDocumentCursor::eraseLine() +{ + if ( m_handle ) + m_handle->eraseLine(); +} + +/*! + \brief insert a new line at the cursor position +*/ +void QDocumentCursor::insertLine(bool keepAnchor) +{ + if ( m_handle ) + m_handle->insertText("\n", keepAnchor); +} + +/*! + \brief insert some text at the cursor position + + Selected text will be removed before insertion happens. + + \note Nothing happens if \a s is empty +*/ +void QDocumentCursor::insertText(const QString& s, bool keepAnchor) +{ + if ( m_handle ) + m_handle->insertText(s, keepAnchor); +} + +/*! + \return A cursor pointing at the position of the selection start. + + Selection start is the position of the selection that is nearest to document start. + + \note an invalid cursor is returned when the cursor does not have a selection +*/ +QDocumentCursor QDocumentCursor::selectionStart() const +{ + return m_handle ? m_handle->selectionStart() : QDocumentCursor(); +} + +/*! + \return A cursor pointing at the position of the selection end. + + Selection end is the position of the selection that is nearest to document end. + + \note an invalid cursor is returned when the cursor does not have a selection +*/ +QDocumentCursor QDocumentCursor::selectionEnd() const +{ + return m_handle ? m_handle->selectionEnd() : QDocumentCursor(); +} + +/*! + \return The selected text +*/ +QString QDocumentCursor::selectedText() const +{ + return m_handle ? m_handle->selectedText() : QString(); +} + +/*! + \brief Remove the selected text +*/ +void QDocumentCursor::removeSelectedText() +{ + if ( m_handle ) + m_handle->removeSelectedText(); +} + +/*! + \brief Replace the selected text + + This method differs from insertText() in two ways : +
    +
  • if \a text is empty the selection WILL be removed +
  • after the replacement happens this command will + cause the cursor to select the new text (note that this + information is NOT preserved by the undo/redo stack + however). +
+*/ +void QDocumentCursor::replaceSelectedText(const QString& text) +{ + if ( m_handle ) + m_handle->replaceSelectedText(text); +} + +/*! + \brief Begin an edit block + + Edit blocks are command groups. All the commands in an edit block + are executed in a row when the edit block is ended with endEditBlock(). + + Edit blocks are considered as a single command as far as the undo/redo + stack is concerned. + + Edit blocks can be nested but that isn't of much use +*/ +void QDocumentCursor::beginEditBlock() +{ + if ( m_handle ) + m_handle->beginEditBlock(); +} + +/*! + \brief End an edit block +*/ +void QDocumentCursor::endEditBlock() +{ + if ( m_handle ) + m_handle->endEditBlock(); +} + +/*! + \return Whether the cursor is silent +*/ +bool QDocumentCursor::isSilent() const +{ + return m_handle ? m_handle->isSilent() : true; +} + +/*! + \brief Set whether the cursor is silent +*/ +void QDocumentCursor::setSilent(bool y) +{ + if ( m_handle ) + m_handle->setSilent(y); + +} + +/*! + \return whether the cursor is auto updated + + An auto updated cursor will remain on the actual line it points + to when the document is modified. + + \code + QDocumentCursor c1(10, 0, document), c2(10, 0, document), c(5, 0, document); + + c1.setAutoUpdated(true); + + c.insertLine(); + + // at this point c2 still points to line 10 whereas c1 points to line 11 + \endcode +*/ +bool QDocumentCursor::isAutoUpdated() const +{ + return m_handle ? m_handle->isAutoUpdated() : true; +} + +/*! + \brief Set whether the cursor is aut updated +*/ +void QDocumentCursor::setAutoUpdated(bool y) +{ + if ( m_handle ) + m_handle->setAutoUpdated(y); + +} + +/*! + \brief Refresh the column memory of the cursor + + This set the current column memory to the current column position. + + \note It is not recommended to call that yourself. The various + movement method should do that perfectly fine. +*/ +void QDocumentCursor::refreshColumnMemory() +{ + if ( m_handle ) + m_handle->refreshColumnMemory(); + +} + +/*! + \return Whether the cursor has column memory + + The column memory is a feature that allow a cursor + to remember its biggest column number so that moving + back and forth (with movePosition()) on lines of + different width does not result in the column to change. + +*/ +bool QDocumentCursor::hasColumnMemory() const +{ + return m_handle ? m_handle->hasColumnMemory() : false; +} + +/*! + \brief Set whether the cursor use column memory +*/ +void QDocumentCursor::setColumnMemory(bool y) +{ + if ( m_handle ) + m_handle->setColumnMemory(y); + +} + +/*! + \return whether the cursor has a selection +*/ +bool QDocumentCursor::hasSelection() const +{ + return m_handle ? m_handle->hasSelection() : false; +} + +/*! + \brief clear the selection +*/ +void QDocumentCursor::clearSelection() +{ + if ( m_handle ) + m_handle->clearSelection(); + +} + +/*! + \brief Select something +*/ +void QDocumentCursor::select(SelectionType t) +{ + if ( m_handle ) + m_handle->select(t); + +} + +/*! + \brief Set the selection boundary + + Select text between the current cursor anchor and the one + of \a c. + + \note We mean ANCHOR. If the cursor already has a selection the + anchor will not change, but the position will be set to that of + \a c. +*/ +void QDocumentCursor::setSelectionBoundary(const QDocumentCursor& c) +{ + if ( m_handle ) + m_handle->setSelectionBoundary(c); + +} + +/*! + \return whether a given cursor is within the selection +*/ +bool QDocumentCursor::isWithinSelection(const QDocumentCursor& c) const +{ + return m_handle ? m_handle->isWithinSelection(c) : false; +} + +/*! + \return selection information + + \note The QDocumentSelection object is not updated if the selection + changes later on : use it right away and do not store it. +*/ +QDocumentSelection QDocumentCursor::selection() const +{ + QDocumentSelection s; + + if ( isNull() || !hasSelection() ) + { + qDebug("NULL selection"); + + s.startLine = -1; + s.endLine = -1; + + s.start = -1; + s.end = -1; + } else if ( m_handle->m_begLine == m_handle->m_endLine ) { + + s.startLine = m_handle->m_begLine; + s.endLine = m_handle->m_begLine; + + s.start = qMin(m_handle->m_begOffset, m_handle->m_endOffset); + s.end = qMax(m_handle->m_begOffset, m_handle->m_endOffset); + + } else if ( m_handle->m_begLine > m_handle->m_endLine ) { + + s.startLine = m_handle->m_endLine; + s.endLine = m_handle->m_begLine; + + s.start = m_handle->m_endOffset; + s.end = m_handle->m_begOffset; + + //qDebug("[(%i,%i);(%i,%i)]", s.startLine.lineNumber(), s.start, s.endLine.lineNumber(), s.end); + } else { + s.startLine = m_handle->m_begLine; + s.endLine = m_handle->m_endLine; + + s.start = m_handle->m_begOffset; + s.end = m_handle->m_endOffset; + + //qDebug("[(%i,%i);(%i,%i)]", s.startLine.lineNumber(), s.start, s.endLine.lineNumber(), s.end); + } + + return s; +} + +/*! @} */ + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentcursor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentcursor.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QDOCUMENT_CURSOR_H_ +#define _QDOCUMENT_CURSOR_H_ + +#include "qce-config.h" + +/*! + \file qdocumentcursor.h + \brief Definition of the QDocumentCursor class +*/ + +class QChar; +class QPoint; +class QString; +class QPolygon; + +class QDocument; +class QDocumentLine; +struct QDocumentSelection; +class QDocumentCursorHandle; + +class QCE_EXPORT QDocumentCursor +{ + public: + enum MoveFlag + { + MoveAnchor = 0, + KeepAnchor = 1, + ThroughWrap = 2 + }; + + Q_DECLARE_FLAGS(MoveMode, MoveFlag); + + enum MoveOperation + { + NoMove, + Up, + Down, + Left, + PreviousCharacter = Left, + Right, + NextCharacter = Right, + Start, + StartOfLine, + StartOfBlock = StartOfLine, + StartOfWord, + PreviousBlock, + PreviousLine = PreviousBlock, + PreviousWord, + WordLeft, + WordRight, + End, + EndOfLine, + EndOfBlock = EndOfLine, + EndOfWord, + NextWord, + NextBlock, + NextLine = NextBlock + }; + + enum SelectionType + { + WordUnderCursor, + LineUnderCursor + }; + + explicit QDocumentCursor(QDocument *doc); + QDocumentCursor(const QDocumentCursor& cursor); + QDocumentCursor(QDocument *doc, int line, int column = 0); + //QDocumentCursor(const QDocumentLine& line, int column = 0); + QDocumentCursor(QDocumentCursorHandle* handle = 0); + + ~QDocumentCursor(); + + QDocumentCursor clone() const; + + QDocumentCursor& operator = (const QDocumentCursor& c); + + bool operator == (const QDocumentCursor& c) const; + bool operator != (const QDocumentCursor& c) const; + + bool operator < (const QDocumentCursor& c) const; + bool operator > (const QDocumentCursor& c) const; + + bool operator <= (const QDocumentCursor& c) const; + bool operator >= (const QDocumentCursor& c) const; + + bool isNull() const; + bool isValid() const; + + bool atEnd() const; + bool atStart() const; + + bool atBlockEnd() const; + bool atBlockStart() const; + + bool atLineEnd() const; + bool atLineStart() const; + + bool hasSelection() const; + + bool isSilent() const; + void setSilent(bool y); + + bool isAutoUpdated() const; + void setAutoUpdated(bool y); + + int position() const; + + int lineNumber() const; + int columnNumber() const; + + int anchorLineNumber() const; + int anchorColumnNumber() const; + + int visualColumnNumber() const; + + void setColumnNumber(int c, MoveMode m = MoveAnchor); + + int wrappedLineOffset() const; + int anchorWrappedLineOffset() const; + + QPoint documentPosition() const; + QPoint anchorDocumentPosition() const; + + QPolygon documentRegion() const; + + QDocumentLine line() const; + QDocumentLine anchorLine() const; + + void shift(int offset); + void setPosition(int pos, MoveMode m = MoveAnchor); + bool movePosition(int offset, MoveOperation op = NextCharacter, MoveMode m = MoveAnchor); + + void moveTo(int line, int column); + void moveTo(const QDocumentCursor &c); + void moveTo(const QDocumentLine &l, int column); + + void eraseLine(); + void insertLine(bool keepAnchor = false); + void insertText(const QString& s, bool keepAnchor = false); + + QDocumentCursor selectionStart() const; + QDocumentCursor selectionEnd() const; + + QString selectedText() const; + + void clearSelection(); + void removeSelectedText(); + void replaceSelectedText(const QString& text); + + void select(SelectionType t); + void setSelectionBoundary(const QDocumentCursor& c); + + bool isWithinSelection(const QDocumentCursor& c) const; + + QChar nextChar() const; + QChar previousChar() const; + + void deleteChar(); + void deletePreviousChar(); + + void beginEditBlock(); + void endEditBlock(); + + void refreshColumnMemory(); + bool hasColumnMemory() const; + void setColumnMemory(bool y); + + QDocumentSelection selection() const; + + QDocument* document() const; + + inline QDocumentCursorHandle* handle() const + { return m_handle; } + + private: + QDocumentCursorHandle *m_handle; +}; + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentcursor_p.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentcursor_p.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QDOCUMENT_CURSOR_P_H_ +#define _QDOCUMENT_CURSOR_P_H_ + +#include "qce-config.h" + +/*! + \file qdocumentcursor_p.h + \brief Definition of QDocumentCursorHandle +*/ + +#include "qdocumentcursor.h" + +#include + +#if QT_VERSION < 0x040400 +#include +#else +#include +#endif + +class QPoint; +class QPolygon; + +class QDocument; +class QDocumentLine; +class QDocumentPrivate; +class QDocumentCommand; +class QDocumentCommandBlock; + +class QCE_EXPORT QDocumentCursorHandle +{ + friend class QDocumentCursor; + friend class QDocumentPrivate; + friend class QDocumentCommand; + + public: + enum Flags + { + Silent = 1, + ColumnMemory = 2, + MoveWithinWrapped = 4 + }; + + QDocument* document() const; + + bool atEnd() const; + bool atStart() const; + + bool atBlockEnd() const; + bool atBlockStart() const; + + bool atLineEnd() const; + bool atLineStart() const; + + bool hasSelection() const; + + bool isSilent() const; + void setSilent(bool y); + + bool isAutoUpdated() const; + void setAutoUpdated(bool y); + + QDocumentLine line() const; + QDocumentLine anchorLine() const; + + int lineNumber() const; + int columnNumber() const; + + int anchorLineNumber() const; + int anchorColumnNumber() const; + + int visualColumnNumber() const; + + void setColumnNumber(int c, int m = QDocumentCursor::MoveAnchor); + + QPoint documentPosition() const; + QPoint anchorDocumentPosition() const; + + QPolygon documentRegion() const; + + int position() const; + + void shift(int offset); + void setPosition(int pos, int m); + bool movePosition(int offset, int op, int m); + + void insertText(const QString& s, bool keepAnchor = false); + + QChar nextChar() const; + QChar previousChar() const; + + void eraseLine(); + void deleteChar(); + void deletePreviousChar(); + + QDocumentCursor selectionStart() const; + QDocumentCursor selectionEnd() const; + + bool eq(const QDocumentCursorHandle *h); + bool lt(const QDocumentCursorHandle *h); + bool gt(const QDocumentCursorHandle *h); + + QString selectedText() const; + + void clearSelection(); + void removeSelectedText(bool keepAnchor = false); + void replaceSelectedText(const QString& text); + + void select(QDocumentCursor::SelectionType t); + void setSelectionBoundary(const QDocumentCursor& c); + + bool isWithinSelection(const QDocumentCursor& c) const; + QDocumentCursor intersect(const QDocumentCursor& c) const; + + void beginBoundary(int& begline, int& begcol) const; + void endBoundary(int& endline, int& endcol) const; + void substractBoundaries(int lbeg, int cbeg, int lend, int cend); + void boundaries(int& begline, int& begcol, int& endline, int& endcol) const; + void intersectBoundaries(int& lbeg, int& cbeg, int& lend, int& cend) const; + void intersectBoundaries(QDocumentCursorHandle *h, int& lbeg, int& cbeg, int& lend, int& cend) const; + + void beginEditBlock(); + void endEditBlock(); + + void moveTo(int line, int column); + void moveTo(const QDocumentCursor &c); + + void copy(const QDocumentCursorHandle *c); + + void refreshColumnMemory(); + bool hasColumnMemory() const; + void setColumnMemory(bool y); + + virtual void execute(QDocumentCommand *c); + + inline void ref() { m_ref.ref(); } + inline void deref() { if ( m_ref ) m_ref.deref(); if ( !m_ref ) delete this; } + + inline bool hasFlag(int f) const { return m_flags & f; } + inline void setFlag(int f) { m_flags |= f; } + inline void clearFlag(int f) { m_flags &= ~f; } + + protected: + QDocumentCursorHandle(QDocument *d, int line = 0); + virtual ~QDocumentCursorHandle(); + + QDocumentCursorHandle* clone() const; + + private: + int m_flags; + QDocument *m_doc; +#if QT_VERSION < 0x040400 + QBasicAtomic m_ref; +#else + QAtomicInt m_ref; +#endif + int m_begOffset, m_endOffset, m_max, m_begLine, m_endLine; + QStack m_blocks; +}; + +Q_DECLARE_TYPEINFO(QDocumentCursorHandle*, Q_PRIMITIVE_TYPE); + +#endif // !_QDOCUMENT_CURSOR_P_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentline.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentline.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,602 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qdocumentline.h" + +/*! + \file qdocumentline.cpp + \brief Implementation of the QDocumentLine class. +*/ + +#include "qdocument_p.h" + +/*! + \ingroup document + @{ +*/ + +/*! + \class QDocumentLine + + \brief A reference to line objects + + In QCodeEdit, documents are stored as a list of lines. A QDocumentLine holds + a pointer to the data of one line and gives access to its content. + + It is not meant to be used to iterate over the document, though it is possible + for conveneience and compatibility reasons. Indeed, QDocumentLine does not now + where in the document it is located. It can obtain that information but this is + a O(n) operation. Navigation within the document is one of the task devoted to + QDocumentCursor which can move around in O(1) (or amortized O(1) in some rare + cases). + + Lines can be given formatting in various way : "regular" formatting used for + highlighting, overlays used mainly to display search matches and similar informations + and marks. +*/ + +QDocumentLine::QDocumentLine(QDocument *doc) + : m_handle(doc ? doc->impl()->at(0) : 0) +{ + //m_lines_backing_store << this; + + if ( m_handle ) + m_handle->ref(); + +} + +QDocumentLine::QDocumentLine(const QDocumentLine& line) + : m_handle(line.m_handle) +{ + //m_lines_backing_store << this; + if ( m_handle ) + m_handle->ref(); + +} + +QDocumentLine::QDocumentLine(QDocumentLineHandle* handle) + : m_handle(handle) +{ + //m_lines_backing_store << this; + if ( m_handle ) + m_handle->ref(); + +} + +QDocumentLine::~QDocumentLine() +{ + if ( m_handle ) + m_handle->deref(); + + //m_lines_backing_store.removeAll(this); +} + +/*! + \brief Comparision operator +*/ +bool QDocumentLine::operator == (const QDocumentLine& l) const +{ + return m_handle == l.m_handle; + //return lineNumber() == l.lineNumber(); +} + +/*! + \brief Comparision operator +*/ +bool QDocumentLine::operator != (const QDocumentLine& l) const +{ + return m_handle != l.m_handle; + //return lineNumber() != l.lineNumber(); +} + +/*! + \brief Comparision operator + + \note Line number based : avoid whenever possible +*/ +bool QDocumentLine::operator < (const QDocumentLine& l) const +{ + return lineNumber() < l.lineNumber(); +} + +/*! + \brief Comparision operator + + \note Line number based : avoid whenever possible +*/ +bool QDocumentLine::operator >= (const QDocumentLine& l) const +{ + return lineNumber() >= l.lineNumber(); +} + +/*! + \brief Comparision operator + + \note Line number based : avoid whenever possible +*/ +bool QDocumentLine::operator > (const QDocumentLine& l) const +{ + return lineNumber() > l.lineNumber(); +} + +/*! + \brief Comparision operator + + \note Line number based : avoid whenever possible +*/ +bool QDocumentLine::operator <= (const QDocumentLine& l) const +{ + return lineNumber() <= l.lineNumber(); +} + +/*! + \brief Iterate forward over the document +*/ +QDocumentLine& QDocumentLine::operator ++ () +{ + operator = (next()); + return *this; +} + +/*! + \brief Iterate backward over the document +*/ +QDocumentLine& QDocumentLine::operator -- () +{ + operator = (previous()); + return *this; +} + +/*! + \brief Iterate forward over the document +*/ +void QDocumentLine::operator ++ (int) +{ + operator = (next()); +} + +/*! + \brief Iterate backward over the document +*/ +void QDocumentLine::operator -- (int) +{ + operator = (previous()); +} + +/*! + \brief copy operator + + QDocumentLine objects are just wrappers around the "real" line data. + Copies of a QDocumentLine all points to the same underlying data and + modifying one affect them all (no implicit sharing). +*/ +QDocumentLine& QDocumentLine::operator = (const QDocumentLine& l) +{ + if ( m_handle ) + m_handle->deref(); + + m_handle = l.m_handle; + + if ( m_handle ) + m_handle->ref(); + + return *this; +} + +/*! + \return the document to which that line belongs +*/ +QDocument* QDocumentLine::document() const +{ + return m_handle ? m_handle->document() : 0; +} + +/*! + \return the line number of the line within the document + + Starts at 0, -1 for invalid lines. + + \note Avoid whenever possible : O(n) complexity, n being document size in lines + Prefer cursors over lines if you need to navigate within the document +*/ +int QDocumentLine::lineNumber() const +{ + return m_handle ? m_handle->line() : -1; +} + +/*! + \return the position of the line within the document + + \note This function is there for compatibility with QTextDocument & co + Avoid it whenever possible, it is ridiculously slow. +*/ +int QDocumentLine::position() const +{ + return m_handle ? m_handle->position() : -1; +} + +/*! + \brief Check whether a given flag is set to the line +*/ +bool QDocumentLine::hasFlag(State s) const +{ + return m_handle ? m_handle->hasFlag(s) : false; +} + +/*! + \brief Check whether any of the flags in a given combination is set to the line +*/ +bool QDocumentLine::hasAnyFlag(int s) const +{ + return m_handle ? m_handle->hasFlag(s) : false; +} + +/*! + \brief Set a flag of the line + + \warning Do not mess with flags unless you know what you are doing +*/ +void QDocumentLine::setFlag(State s, bool y) +{ + if ( m_handle ) + m_handle->setFlag(s, y); + +} + +/*! + \return whether the line object is null (i.e invalid) +*/ +bool QDocumentLine::isNull() const +{ + return m_handle ? !m_handle->document() : true; +} + +/*! + \return whether the line object is valid +*/ +bool QDocumentLine::isValid() const +{ + return m_handle ? m_handle->document() : false; +} + +/*! + \return the text of the line +*/ +QString QDocumentLine::text() const +{ + return m_handle ? m_handle->text() : QString(); +} + +/*! + \return The length of the line, in characters +*/ +int QDocumentLine::length() const +{ + return m_handle ? m_handle->text().length() : 0; +} + +/*! + \return the number of visual lines occupied by the line + + This is NOT set to zero when the line is hidden (e.g due to folding). +*/ +int QDocumentLine::lineSpan() const +{ + return m_handle && m_handle->document() ? m_handle->m_frontiers.count() + 1 : 0; +} + +/*! + \brief Returns the position of the first non-whitespace character + \return position of first non-whitespace char or -1 if there is none +*/ +int QDocumentLine::firstChar() const +{ + return nextNonSpaceChar(0); +} + +/*! + \brief Returns the position of the last non-whitespace character + \return position of last non-whitespace char or -1 if there is none +*/ +int QDocumentLine::lastChar() const +{ + return previousNonSpaceChar(length() - 1); +} + +int QDocumentLine::indent() const +{ + return m_handle ? m_handle->indent() : 0; +} + +/*! + Find the position of the next char that is not a space. + \param pos Column of the character which is examined first. + \return True if the specified or a following character is not a space + Otherwise false. +*/ +int QDocumentLine::nextNonSpaceChar(int pos) const +{ + return m_handle ? m_handle->nextNonSpaceChar(pos) : -1; +} + +/*! + \brief Find the position of the previous char that is not a space. + \param pos Column of the character which is examined first. + \return The position of the first non-whitespace character preceding pos, + or -1 if none is found. +*/ +int QDocumentLine::previousNonSpaceChar(int pos) const +{ + return m_handle ? m_handle->previousNonSpaceChar(pos) : -1; +} + +/*! + \return The previous line + + \note Avoid using this function whenever possible, especially + inside loops or time-consuming processing : it is SLOW for big + documents as determination of the line number is O(n), n being + the total number of lines in the document +*/ +QDocumentLine QDocumentLine::next() const +{ + return QDocumentLine(m_handle->next()); +} + +/*! + \return The next line + + \note Avoid using this function whenever possible, especially + inside loops or time-consuming processing : it is SLOW for big + documents as determination of the line number is O(n), n being + the total number of lines in the document +*/ +QDocumentLine QDocumentLine::previous() const +{ + return QDocumentLine(m_handle->previous()); +} + +/*! + \brief Converts a cursor position (column) to a document position (unconstrained viewport) + + \deprecated Use cursorToDocOffset() instead + + This function is kept for compatribility only. It dates back to the time before line wrapping + was implemented. Due to the limitation of its design (i.e signature) it works in a somewhat + awkard way : the x position returned is that the cursor would have in an unconstrained viewport, + even when the line is wrapped so it does not map to an actual viewport coordinate, unless of + course no wrapping is used. +*/ +int QDocumentLine::cursorToX(int cpos) const +{ + return m_handle ? m_handle->cursorToX(cpos) : -1; +} + +/*! + \brief Converts a document position (unconstrained viewport) to a cursor position (column) + + \deprecated Use cursorToDocOffset() instead + + \see cursorToX() for more informations about this function +*/ +int QDocumentLine::xToCursor(int xpos) const +{ + return m_handle ? m_handle->xToCursor(xpos) : -1; +} + +/*! + \return The wrapped line (i.e "subline") to which a given cursor position resides + \param cpos cursor position, as a text column +*/ +int QDocumentLine::wrappedLineForCursor(int cpos) const +{ + return m_handle ? m_handle->wrappedLineForCursor(cpos) : -1; +} + +/*! + \brief Converts a document offset (viewport) to a cursor position (character / text column) + + The (x, y) coordinates given by this function are relative to the absolute + position of the line, which can be obtained from the document. +*/ +int QDocumentLine::documentOffsetToCursor(int x, int y) const +{ + return m_handle ? m_handle->documentOffsetToCursor(x, y) : -1; +} + +/*! + \brief Converts a cursor position (character / text column) to a document offset (viewport) + + The (x, y) coordinates given by this function are relative to the absolute + position of the line, which can be obtained from the document. +*/ +void QDocumentLine::cursorToDocumentOffset(int cpos, int& x, int& y) const +{ + if ( m_handle ) + m_handle->cursorToDocumentOffset(cpos, x, y); +} + +/*! + \overload +*/ +QPoint QDocumentLine::cursorToDocumentOffset(int cpos) const +{ + return m_handle ? m_handle->cursorToDocumentOffset(cpos) : QPoint(); +} + +/*! + \brief Toggle a mark on the line +*/ +void QDocumentLine::addMark(int id) +{ + if ( !document() ) + return; + + document()->impl()->addMark(m_handle, id); +} + +/*! + \brief Toggle a mark on the line +*/ +void QDocumentLine::toggleMark(int id) +{ + if ( !document() ) + return; + + document()->impl()->toggleMark(m_handle, id); +} + +/*! + \brief Remove a mark from the line +*/ +void QDocumentLine::removeMark(int id) +{ + if ( !document() ) + return; + + document()->impl()->removeMark(m_handle, id); +} + +/*! + \return the list of marks set on this line +*/ +QList QDocumentLine::marks() const +{ + return document() ? document()->impl()->marks(m_handle) : QList(); +} + +/*! + \return Whether a given mark has been set on the line +*/ +bool QDocumentLine::hasMark(int id) const +{ + return marks().contains(id); +} + +/*! + \brief Set the formatting of the line + + \note this method is made available for syntax engine use. + If you want to apply extra formatting on a line use overlays + instead + + \see addOverlay() +*/ +void QDocumentLine::setFormats(const QVector& formats) +{ + if ( !m_handle ) + return; + + m_handle->setFormats(formats); +} + +/*! + \return whether the line has at least one overlay of a given format id +*/ +bool QDocumentLine::hasOverlay(int fid) const +{ + if ( !m_handle ) + return false; + + foreach ( const QFormatRange& r, m_handle->m_overlays ) + if ( r.format == fid ) + return true; + + return false; +} + +/*! + \brief Clear all overlays applied to the line +*/ +QList QDocumentLine::overlays() const +{ + if ( !m_handle ) + return QList(); + + return m_handle->m_overlays; +} + +/*! + \brief Clear all overlays applied to the line +*/ +void QDocumentLine::clearOverlays() +{ + if ( !m_handle ) + return; + + m_handle->clearOverlays(); +} + +/*! + \brief Add an overlay to the line + + Overlays are format range that get applied on top of regular formatting. + + They are typically used to display search matches, matching braces, ... +*/ +void QDocumentLine::addOverlay(const QFormatRange& over) +{ + if ( !m_handle ) + return; + + m_handle->addOverlay(over); +} + +/*! + \brief Remove an overlay from the line +*/ +void QDocumentLine::removeOverlay(const QFormatRange& over) +{ + if ( !m_handle ) + return; + + m_handle->removeOverlay(over); +} + +/*! + \return the list of parentheses present on the line + + \note This is language dependent. +*/ +const QVector& QDocumentLine::parentheses() const +{ + Q_CHECK_PTR(m_handle); + + return m_handle->m_parens; +} + +/*! + \brief Set the parentheses present on that line + + \note this should only be used by syntax engines +*/ +void QDocumentLine::setParentheses(const QVector& parentheses) +{ + if ( !m_handle ) + return; + + m_handle->m_parens = parentheses; +} + +/*! + Reserved to syntax engines. do not mess with this unless you know what you are doing. +*/ +QNFAMatchContext* QDocumentLine::matchContext() +{ + return m_handle ? &m_handle->m_context : 0; +} + +/*! @} */ + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentline.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentline.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QDOCUMENT_LINE_H_ +#define _QDOCUMENT_LINE_H_ + +#include "qce-config.h" + +/*! + \file qdocumentline.h + \brief Definition of the QDocumentLine class +*/ + +#include "qformat.h" + +class QPoint; +class QString; + +class QDocument; +class QDocumentLineHandle; + +struct QNFAMatchContext; + +struct QParenthesis +{ + enum Role + { + Open = 1, + Close = 2, + Indent = 4, + Fold = 8, + Match = 16 + }; + + inline QParenthesis() + : id(0), role(0), offset(0), length(0) + {} + + inline QParenthesis(int i, quint8 r, int pos, int len) + : id(i), role(r), offset(pos), length(len) + {} + + int id; + int role; + int offset; + int length; +}; + +Q_DECLARE_TYPEINFO(QParenthesis, Q_MOVABLE_TYPE); + +class QCE_EXPORT QDocumentLine +{ + friend class QDocumentLineHandle; + friend class QDocumentCursorHandle; + + public: + enum State + { + None = 0, + Hidden = 1, + CollapsedBlockStart = 2, + CollapsedBlockEnd = 4, + + LayoutDirty = 16, + FormatsApplied = 32 + }; + + Q_DECLARE_FLAGS(States, State); + + explicit QDocumentLine(QDocument *doc); + QDocumentLine(const QDocumentLine& line); + QDocumentLine(QDocumentLineHandle *h = 0); + + ~QDocumentLine(); + + bool isNull() const; + bool isValid() const; + + inline bool operator == (const QDocumentLineHandle* h) const + { + return m_handle == h; + } + + inline bool operator != (const QDocumentLineHandle* h) const + { + return m_handle != h; + } + + bool operator == (const QDocumentLine& l) const; + bool operator != (const QDocumentLine& l) const; + + bool operator < (const QDocumentLine& l) const; + bool operator >= (const QDocumentLine& l) const; + + bool operator > (const QDocumentLine& l) const; + bool operator <= (const QDocumentLine& l) const; + + QDocumentLine& operator ++ (); + QDocumentLine& operator -- (); + + void operator ++ (int); + void operator -- (int); + + QDocumentLine& operator = (const QDocumentLine& l); + + int lineNumber() const; + int position() const; + + QString text() const; + + int length() const; + int lineSpan() const; + + int firstChar() const; + int lastChar() const; + + int indent() const; + + int nextNonSpaceChar(int pos) const; + int previousNonSpaceChar(int pos) const; + + inline QString indentation() const + { int idx = firstChar(); return idx != -1 ? text().left(idx) : text(); } + + inline bool isHidden() const + { return hasFlag(Hidden); } + + bool hasFlag(State s) const; + bool hasAnyFlag(int s) const; + void setFlag(State s, bool y = true); + + QDocumentLine next() const; + QDocumentLine previous() const; + + QDocument* document() const; + + int xToCursor(int x) const; + int cursorToX(int cpos) const; + + int wrappedLineForCursor(int cpos) const; + + int documentOffsetToCursor(int x, int y) const; + void cursorToDocumentOffset(int cpos, int& x, int& y) const; + + QPoint cursorToDocumentOffset(int cpos) const; + + void addMark(int id); + void removeMark(int id); + void toggleMark(int id); + + QList marks() const; + bool hasMark(int id) const; + + bool hasOverlay(int fid) const; + QList overlays() const; + + void clearOverlays(); + void addOverlay(const QFormatRange& over); + void removeOverlay(const QFormatRange& over); + + void setFormats(const QVector& formats); + + const QVector& parentheses() const; + void setParentheses(const QVector& parentheses); + + inline QDocumentLineHandle* handle() const + { return m_handle; } + + QNFAMatchContext* matchContext(); + + private: + QDocumentLineHandle *m_handle; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDocumentLine::States) + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentline_p.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentline_p.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QDOCUMENT_LINE_P_H_ +#define _QDOCUMENT_LINE_P_H_ + +#include "qce-config.h" + +/*! + \file qdocumentline_p.h + \brief Definition of QDocumentLineHandle +*/ + +#include "qnfa.h" + +#include "qformat.h" + +#include "qdocumentline.h" + +#include +#include +#include +#include + +#include + +#if QT_VERSION < 0x040400 +#include +#else +#include +#endif + +typedef QVector QSmallArray; +typedef QVector QMediumArray; + +class QPoint; + +class QDocument; +class QDocumentLine; +class QDocumentBuffer; +class QDocumentPrivate; + +class QCE_EXPORT QDocumentLineHandle +{ + friend class QDocument; + friend class QDocumentLine; + friend class QDocumentBuffer; + friend class QDocumentPrivate; + + public: + QDocumentLineHandle(QDocument *d); + QDocumentLineHandle(const QString& s, QDocument *d); + + int count() const; + int length() const; + + int position() const; + + QString text() const; + + int line() const; + + int xToCursor(int x) const; + int cursorToX(int i) const; + + int wrappedLineForCursor(int cpos) const; + + int documentOffsetToCursor(int x, int y) const; + void cursorToDocumentOffset(int cpos, int& x, int& y) const; + + QPoint cursorToDocumentOffset(int cpos) const; + + int indent() const; + + int nextNonSpaceChar(uint pos) const; + int previousNonSpaceChar(int pos) const; + + bool hasFlag(int flag) const; + void setFlag(int flag, bool y = true) const; + + QDocument* document() const; + + QDocumentLineHandle* next() const; + QDocumentLineHandle* previous() const; + + void updateWrap() const; + + void setFormats(const QVector& formats); + + void clearOverlays(); + void addOverlay(const QFormatRange& over); + void removeOverlay(const QFormatRange& over); + + void shiftOverlays(int position, int offset); + + void draw( QPainter *p, + int xOffset, + int vWidth, + const QSmallArray& sel, + const QSmallArray& cursors, + const QPalette& pal, + bool fullSel) const; + + inline QString& textBuffer() { setFlag(QDocumentLine::LayoutDirty, true); return m_text; } + + inline void ref() { m_ref.ref(); } + inline void deref() { if ( m_ref ) m_ref.deref(); if ( !m_ref ) delete this; } + + protected: + ~QDocumentLineHandle(); + + private: + void layout() const; + void applyOverlays() const; + + QMediumArray compose() const; + QList decorations() const; + + QString m_text; + QDocument *m_doc; +#if QT_VERSION < 0x040400 + QBasicAtomic m_ref; +#else + QAtomicInt m_ref; +#endif + mutable int m_indent; + mutable quint16 m_state; + mutable QTextLayout *m_layout; + mutable QVector m_cache; + mutable QVector< QPair > m_frontiers; + + QNFAMatchContext m_context; + + QVector m_formats; + QVector m_parens; + QList m_overlays; +}; + +Q_DECLARE_TYPEINFO(QDocumentLineHandle*, Q_PRIMITIVE_TYPE); + +#endif // !_QDOCUMENT_LINE_P_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentsearch.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentsearch.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,663 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +/*! + \file qdocumentsearch.cpp + \brief Implementation of QDocumentSearch +*/ + +#include "qdocumentsearch.h" + +/*! + \ingroup document + @{ +*/ + +#include "qeditor.h" +#include "qdocument.h" +#include "qdocument_p.h" +#include "qdocumentline.h" +#include "qformatscheme.h" + +#include + +/*! + \class QDocumentSearch + \brief An helper class to perform search in document + + QDocumentSearch offer means to perform complex search in documents. +*/ + +QDocumentSearch::QDocumentSearch(QEditor *e, const QString& f, Options opt, const QString& r) + : m_group(-1), m_option(opt), m_string(f), m_replace(r), m_editor(e) +{ + +} + +QDocumentSearch::~QDocumentSearch() +{ + clearMatches(); +} + +QDocumentSearch::Options QDocumentSearch::options() const +{ + return m_option; +} + +/*! + \brief Position of the current match among the indexed matches +*/ +int QDocumentSearch::currentMatchIndex() const +{ + return m_highlight.count() ? m_index : -1; +} + +/*! + \brief Number of availables indexed matches + + Indexed matches are only available when the whole scope is searched, + i.e when either the HighlightAll option is set to true or when next() + is called with the all parameter set to true. +*/ +int QDocumentSearch::indexedMatchCount() const +{ + return m_highlight.count(); +} + +/*! + \return A cursor pointing to the n-th index match + \param idx index of the match to lookup + + The cursor returned, if valid, delimits the match through its selection. +*/ +QDocumentCursor QDocumentSearch::match(int idx) const +{ + return idx >= 0 && idx < m_highlight.count() ? m_highlight.at(idx) : QDocumentCursor(); +} + +/*! + \brief Clear matches + + This function should be called anytime you perform a search with the HighlightAll option, + once you're done iterating over the matches. +*/ +void QDocumentSearch::clearMatches() +{ + if ( !m_editor || !m_editor->document() ) + return; + + //qDebug("clearing matches"); + m_cursor = QDocumentCursor(); + + if ( m_group != -1 ) + { + m_editor->document()->clearMatches(m_group); + m_editor->document()->flushMatches(m_group); + m_group = -1; + } + + m_highlight.clear(); +} + +/*! + \return The search pattern +*/ +QString QDocumentSearch::searchText() const +{ + return m_string; +} + +/*! + \brief Set the search pattern +*/ +void QDocumentSearch::setSearchText(const QString& f) +{ + m_string = f; + + clearMatches(); +} + +/*! + \brief Test whether a given option is enabled +*/ +bool QDocumentSearch::hasOption(Option opt) const +{ + return m_option & opt; +} + +/*! + \brief Set a search option + \param opt option to set + \param on whether to enable the option +*/ +void QDocumentSearch::setOption(Option opt, bool on) +{ + if ( on ) + m_option |= opt; + else + m_option &= ~opt; + + if ( (opt & QDocumentSearch::HighlightAll) && m_highlight.count() ) + { + QDocument *d = m_editor->document(); + + if ( m_group != -1 && !on ) + { + d->clearMatches(m_group); + d->flushMatches(m_group); + m_group = -1; + } else if ( m_group == -1 && on ) { + m_group = d->getNextGroupId(); + + QFormatScheme *f = d->formatScheme(); + + if ( !f ) + f = QDocument::formatFactory(); + + if ( !f ) + { + qWarning("No format scheme set to the document and no global default one available.\n" + "-> highlighting of search matches disabled."); + return; + } + + int sid = f->id("search"); + + foreach ( const QDocumentCursor& c, m_highlight ) + { + //QFormatRange r(c.anchorColumnNumber(), c.columnNumber() - c.anchorColumnNumber(), sid); + + d->addMatch(m_group, + c.lineNumber(), + c.anchorColumnNumber(), + c.columnNumber() - c.anchorColumnNumber(), + sid); + } + + //qDebug("%i matches in group %i", indexedMatchCount(), m_group); + d->flushMatches(m_group); + } + } else if ( + (m_option & QDocumentSearch::HighlightAll) + && + ( + (opt & QDocumentSearch::RegExp) + || + (opt & QDocumentSearch::WholeWords) + || + (opt & QDocumentSearch::CaseSensitive) + ) + ) + { + // matches may have become invalid : update them + clearMatches(); + next(false); + } +} + +/*! + \return the replacement text +*/ +QString QDocumentSearch::replaceText() const +{ + return m_replace; +} + +/*! + \brief Set the replacement text +*/ +void QDocumentSearch::setReplaceText(const QString& r) +{ + m_replace = r; + + clearMatches(); +} + +/*! + \return The current cursor position + + This is useful to examine matches after performing a search. +*/ +QDocumentCursor QDocumentSearch::origin() const +{ + return m_origin; +} + +/*! + \brief Set the cursor + + If the related option is set, search will start from that cursor position + + This also changes the cursor() +*/ +void QDocumentSearch::setOrigin(const QDocumentCursor& c) +{ + m_cursor = QDocumentCursor(); + + if ( c == m_origin ) + return; + + m_origin = c; + + clearMatches(); +} + +/*! + \return The current cursor position + + This is useful to examine matches after performing a search. +*/ +QDocumentCursor QDocumentSearch::cursor() const +{ + return m_cursor; +} + +/*! + \brief Set the cursor + + If the related option is set, search will start from that cursor position +*/ +void QDocumentSearch::setCursor(const QDocumentCursor& c) +{ + m_cursor = c; +} + +/*! + \return The scope of the search + + An invalid cursor indicate that the scope is the whole document, otherwise + the scope is the selection of the returned cursor. +*/ +QDocumentCursor QDocumentSearch::scope() const +{ + return m_scope; +} + +/*! + \brief Set the search scope + + If the given cursor has no selection (a fortiori if it is invalid) then + the scope is the whole document. +*/ +void QDocumentSearch::setScope(const QDocumentCursor& c) +{ + if ( c == m_scope ) + return; + + if ( c.hasSelection() ) + m_scope = c; + else + m_scope = QDocumentCursor(); + + clearMatches(); +} + +/*! + \brief Test whether the end of the search scope has been reached +*/ +bool QDocumentSearch::end(bool backward) const +{ + bool absEnd = backward ? m_cursor.atStart() : m_cursor.atEnd(); + + if ( m_scope.isValid() && m_scope.hasSelection() ) + { + absEnd |= !m_scope.isWithinSelection(m_cursor); + /* + qDebug( + "(%i, %i, %i) %s in {(%i, %i), (%i, %i)}", + m_cursor.lineNumber(), + m_cursor.anchorColumnNumber(), + m_cursor.columnNumber(), + absEnd ? "is not" : "is", + m_scope.selectionStart().lineNumber(), + m_scope.selectionStart().columnNumber(), + m_scope.selectionEnd().lineNumber(), + m_scope.selectionEnd().columnNumber() + ); + */ + } + + return absEnd; +} + +/*! + \brief Perform a search + \param backward whether to go backward or forward + \param all if true, the whole document will be searched first, all matches recorded and available for further navigation + + \note Technically speaking the all parameter make search behave similarly to the HighlightAll option, except that the former + option does not alter the formatting of the document. +*/ +void QDocumentSearch::next(bool backward, bool all) +{ + if ( m_string.isEmpty() ) + return; + + if ( !hasOption(Replace) && (all || hasOption(HighlightAll)) && m_highlight.count() ) + { + if ( !backward ) + ++m_index; + + //m_index = m_index + (backward ? -1 : 1); + + if ( (m_index < 0 || m_index >= m_highlight.count()) ) + { + if ( hasOption(Silent) ) + { + m_cursor = QDocumentCursor(); + return; + } + + int ret = + QMessageBox::question( + m_editor, + tr("Failure"), + tr( + "End of matches reached.\n" + "Restart from the begining ?" + ), + QMessageBox::Yes + | QMessageBox::No, + QMessageBox::Yes + ); + + if ( ret == QMessageBox::Yes ) + { + m_index = backward ? m_highlight.count() : 0; + --m_index; + next(backward); + return; + } + } else { + m_cursor = m_highlight.at(m_index); + + if ( m_editor && !hasOption(Silent) ) + m_editor->setCursor(m_cursor); + } + + if ( backward ) + --m_index; + + return; + } + + if ( m_cursor.isNull() ) + { + m_cursor = m_origin; + } + + if ( m_cursor.isNull() ) + { + if ( m_scope.isValid() && m_scope.hasSelection() ) + { + if ( backward ) + m_cursor = m_scope.selectionEnd(); + else + m_cursor = m_scope.selectionStart(); + } else if ( m_editor ) { + + m_cursor = QDocumentCursor(m_editor->document()); + + if ( backward ) + m_cursor.movePosition(1, QDocumentCursor::End); + + } else { + QMessageBox::warning(0, 0, "Unable to perform search operation"); + } + } + + /* + qDebug( + "searching %s from line %i (column %i)", + backward ? "backward" : "forward", + m_cursor.lineNumber(), + m_cursor.columnNumber() + ); + */ + + m_index = 0; + QRegExp m_regexp; + Qt::CaseSensitivity cs = hasOption(CaseSensitive) + ? + Qt::CaseSensitive + : + Qt::CaseInsensitive; + + if ( hasOption(RegExp) ) + { + m_regexp = QRegExp(m_string, cs, QRegExp::RegExp); + } else if ( hasOption(WholeWords) ) { + m_regexp = QRegExp( + QString("\\b%1\\b").arg(QRegExp::escape(m_string)), + cs, + QRegExp::RegExp + ); + } else { + m_regexp = QRegExp(m_string, cs, QRegExp::FixedString); + } + + bool found = false; + QDocumentCursor::MoveOperation move; + QDocument *d = m_editor ? m_editor->document() : m_origin.document(); + QFormatScheme *f = d->formatScheme() ? d->formatScheme() : QDocument::formatFactory(); + int sid = f ? f->id("search") : 0; + + if ( !sid ) + qWarning("Highlighting of search matches disabled due to unavailability of a format scheme."); + + move = backward ? QDocumentCursor::PreviousBlock : QDocumentCursor::NextBlock; + + QDocumentSelection boundaries; + bool bounded = m_scope.isValid() && m_scope.hasSelection(); + + // condition only to avoid debug messages... + if ( bounded ) + boundaries = m_scope.selection(); + + while ( !end(backward) ) + { + if ( backward && !m_cursor.columnNumber() ) + { + m_cursor.movePosition(1, QDocumentCursor::PreviousCharacter); + continue; + } + + int ln = m_cursor.lineNumber(); + QDocumentLine l = m_cursor.line(); + + int coloffset = 0; + QString s = l.text(); + + if ( backward ) + { + if ( bounded && (boundaries.startLine == ln) ) + { + s = s.mid(boundaries.start); + coloffset = boundaries.start; + } + + s = s.left(m_cursor.columnNumber()); + } else { + if ( bounded && (boundaries.endLine == ln) ) + s = s.left(boundaries.end); + + } + + int column = backward + ? + m_regexp.lastIndexIn(s, m_cursor.columnNumber() - 1) + : + m_regexp.indexIn(s, m_cursor.columnNumber()) + ; + + /* + qDebug("searching %s in %s => %i", + qPrintable(m_regexp.pattern()), + qPrintable(s), + column); + */ + + if ( column != -1 && (backward || column >= m_cursor.columnNumber()) ) + { + column += coloffset; + + if ( backward ) + { + m_cursor.setColumnNumber(column + m_regexp.matchedLength()); + m_cursor.setColumnNumber(column, QDocumentCursor::KeepAnchor); + + /* + m_cursor.movePosition(m_regexp.matchedLength(), + QDocumentCursor::PreviousCharacter, + QDocumentCursor::KeepAnchor); + */ + } else { + m_cursor.setColumnNumber(column); + m_cursor.setColumnNumber(column + m_regexp.matchedLength(), QDocumentCursor::KeepAnchor); + + /* + m_cursor.movePosition(m_regexp.matchedLength(), + QDocumentCursor::NextCharacter, + QDocumentCursor::KeepAnchor); + */ + } + + if ( m_editor && !hasOption(Silent) && !hasOption(HighlightAll) ) + m_editor->setCursor(m_cursor); + + if ( hasOption(Replace) ) + { + bool rep = true; + + if ( hasOption(Prompt) ) + { + int ret = QMessageBox::question(m_editor, tr("Replacement prompt"), + tr("Shall it be replaced?"), + QMessageBox::Yes + | QMessageBox::No + | QMessageBox::Cancel, + QMessageBox::Yes); + + if ( ret == QMessageBox::Yes ) + { + rep = true; + } else if ( ret == QMessageBox::No ) { + rep = false; + } else if ( QMessageBox::Cancel ) { + //m_cursor.setColumnNumber(m_cursor.columnNumber()); + return; + } + } + + // + if ( rep ) + { + QString replacement = m_replace; + + for ( int i = m_regexp.numCaptures(); i >= 0; --i ) + replacement.replace(QString("\\") + QString::number(i), + m_regexp.cap(i)); + + m_cursor.beginEditBlock(); + m_cursor.removeSelectedText(); + m_cursor.insertText(replacement); + m_cursor.endEditBlock(); + + if ( backward ) + m_cursor.movePosition(replacement.length(), QDocumentCursor::PreviousCharacter); + } else { + //qDebug("no rep"); + } + } else if ( all || hasOption(HighlightAll) ) { + + if ( sid && hasOption(HighlightAll) ) + { + if ( m_group == -1 ) + m_group = d->getNextGroupId(); + + d->addMatch(m_group, + m_cursor.lineNumber(), + m_cursor.anchorColumnNumber(), + m_cursor.columnNumber() - m_cursor.anchorColumnNumber(), + sid); + //QFormatRange r( + // m_cursor.anchorColumnNumber(), + // m_cursor.columnNumber() - m_cursor.anchorColumnNumber(), + // m_editor->document()->formatScheme()->id("search") + // ); + + //qDebug("(%i, %i, %i)", r.offset, r.length, r.format); + //m_cursor.line().addOverlay(r); + } + + m_highlight << m_cursor; + m_highlight.last().setAutoUpdated(true); + } + + found = true; + + if ( !(all || hasOption(HighlightAll)) ) + break; + + } else { + m_cursor.movePosition(1, move); + } + } + + if ( !hasOption(Replace) && hasOption(HighlightAll) && m_highlight.count() ) + { + //qDebug("%i matches in group %i", indexedMatchCount(), m_group); + if ( indexedMatchCount() ) + { + m_editor->document()->flushMatches(m_group); + } else { + m_editor->document()->releaseGroupId(m_group); + m_group = -1; + } + + m_index = backward ? m_highlight.count() : 0; + --m_index; + return next(backward); + } + + if ( !found ) + { + m_cursor = QDocumentCursor(); + + if ( hasOption(Silent) ) + return; + + int ret = + QMessageBox::question( + m_editor, + tr("Failure"), + tr( + "End of scope reached with no match.\n" + "Restart from the begining ?" + ), + QMessageBox::Yes + | QMessageBox::No, + QMessageBox::Yes + ); + + if ( ret == QMessageBox::Yes ) + { + m_origin = QDocumentCursor(); + next(backward); + } + } +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/document/qdocumentsearch.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/document/qdocumentsearch.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QDOCUMENT_SEARCH_H_ +#define _QDOCUMENT_SEARCH_H_ + +#include "qce-config.h" + +/*! + \file qdocumentsearch.h + \brief Definition of the QDocumentSearch class +*/ + +#include +#include +#include +#include + +#include "qdocumentcursor.h" + +class QEditor; + +class QCE_EXPORT QDocumentSearch +{ + Q_DECLARE_TR_FUNCTIONS(QDocumentSearch) + + public: + enum Option + { + WholeWords = 1, + CaseSensitive = 2, + RegExp = 4, + Replace = 8, + Prompt = 16, + Silent = 32, + HighlightAll = 64 + }; + + Q_DECLARE_FLAGS(Options, Option); + + QDocumentSearch(QEditor *e, const QString& f, Options opt, const QString& r = QString()); + ~QDocumentSearch(); + + int currentMatchIndex() const; + int indexedMatchCount() const; + QDocumentCursor match(int idx) const; + + QString searchText() const; + void setSearchText(const QString& f); + + Options options() const; + bool hasOption(Option opt) const; + void setOption(Option opt, bool on); + + QString replaceText() const; + void setReplaceText(const QString& r); + + QDocumentCursor origin() const; + void setOrigin(const QDocumentCursor& c); + + QDocumentCursor cursor() const; + void setCursor(const QDocumentCursor& c); + + QDocumentCursor scope() const; + void setScope(const QDocumentCursor& c); + + void next(bool backward, bool all = false); + + private: + bool end(bool backward) const; + + void clearMatches(); + + int m_group; + int m_index; + Options m_option; + QString m_string; + QString m_replace; + QPointer m_editor; + QDocumentCursor m_cursor, m_scope, m_origin; + QList m_highlight; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDocumentSearch::Options) + +#endif // !_QDOCUMENT_SEARCH_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/lib.pri --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/lib.pri Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,103 @@ +DEFINES += _QCODE_EDIT_BUILD_ + +CONFIG *= qnfa console + +QT *= xml + +# Input + +HEADERS += qce-config.h \ + qeditor.h \ + qeditorfactory.h \ + qeditorinputbindinginterface.h \ + qeditorinputbinding.h \ + qeditsession.h \ + qcodeedit.h \ + qpanellayout.h \ + qformatfactory.h \ + qformatscheme.h \ + qlinemarksinfocenter.h \ + qreliablefilewatch.h \ + document/qdocument.h \ + document/qdocument_p.h \ + document/qdocumentcommand.h \ + document/qdocumentcursor.h \ + document/qdocumentline.h \ + document/qdocumentsearch.h \ + qcodecompletionengine.h \ + qlanguagedefinition.h \ + qlanguagefactory.h \ + widgets/qpanel.h \ + widgets/qlinenumberpanel.h \ + widgets/qlinemarkpanel.h \ + widgets/qlinechangepanel.h \ + widgets/qfoldpanel.h \ + widgets/qstatuspanel.h \ + widgets/qgotolinepanel.h \ + widgets/qsearchreplacepanel.h \ + widgets/qgotolinedialog.h \ + widgets/qeditconfig.h \ + widgets/qformatconfig.h \ + widgets/qsimplecolorpicker.h \ + widgets/qcalltip.h \ + snippets/qsnippet.h \ + snippets/qsnippet_p.h \ + snippets/qsnippetpatternloader.h \ + snippets/qsnippetmanager.h \ + snippets/qsnippetedit.h \ + snippets/qsnippetbinding.h + +SOURCES += qeditor.cpp \ + qeditorfactory.cpp \ + qeditorinputbinding.cpp \ + qeditsession.cpp \ + qcodeedit.cpp \ + qpanellayout.cpp \ + qformatscheme.cpp \ + qlinemarksinfocenter.cpp \ + qreliablefilewatch.cpp \ + document/qdocument.cpp \ + document/qdocumentcommand.cpp \ + document/qdocumentcursor.cpp \ + document/qdocumentline.cpp \ + document/qdocumentsearch.cpp \ + qcodecompletionengine.cpp \ + qlanguagedefinition.cpp \ + qlanguagefactory.cpp \ + widgets/qpanel.cpp \ + widgets/qlinenumberpanel.cpp \ + widgets/qlinemarkpanel.cpp \ + widgets/qlinechangepanel.cpp \ + widgets/qfoldpanel.cpp \ + widgets/qstatuspanel.cpp \ + widgets/qgotolinepanel.cpp \ + widgets/qsearchreplacepanel.cpp \ + widgets/qgotolinedialog.cpp \ + widgets/qeditconfig.cpp \ + widgets/qformatconfig.cpp \ + widgets/qsimplecolorpicker.cpp \ + widgets/qcalltip.cpp \ + snippets/qsnippet.cpp \ + snippets/qsnippetmanager.cpp \ + snippets/qsnippetedit.cpp \ + snippets/qsnippetbinding.cpp + +FORMS += widgets/searchreplace.ui \ + widgets/gotoline.ui \ + widgets/gotolinedialog.ui \ + widgets/editconfig.ui \ + widgets/formatconfig.ui \ + snippets/snippetedit.ui + +qnfa { + DEFINES += QNFA_BUILD + + HEADERS += qnfa/qnfa.h \ + qnfa/qnfadefinition.h \ + qnfa/light_vector.h + + SOURCES += qnfa/qnfa.cpp \ + qnfa/qnfadefinition.cpp \ + qnfa/xml2qnfa.cpp +} + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/lib.pro --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/lib.pro Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,22 @@ +###################################################################### +# Automatically generated by qmake (2.01a) ven. juin 8 21:29:34 2007 +###################################################################### + +TEMPLATE = lib +TARGET = qcodeedit +DESTDIR = .. +CONFIG += staticlib + +DEPENDPATH += . document language widgets qnfa +INCLUDEPATH += . document language widgets qnfa + +CONFIG += qnfa + +QT += xml + +UI_DIR = +MOC_DIR = moc-dir +OBJECTS_DIR = objects.dir + +# Input +include(lib.pri) diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qce-config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qce-config.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QCE_CONFIG_H_ +#define _QCE_CONFIG_H_ + +/*! + \file qce-config.h + \brief Utility file for shared library creation +*/ + +#include + +/*! + \macro QCE_EXPORT + \brief Macro needed for cross-platform shared libraries creation +*/ +#ifdef QCE_EXPORT + // QCE_EXPORT manually defined, trust the user +#else + #ifdef _QCODE_EDIT_BUILD_ + #ifdef _QCODE_EDIT_EMBED_ + #define QCE_EXPORT + #else + #define QCE_EXPORT Q_DECL_EXPORT + #endif + #else + #define QCE_EXPORT Q_DECL_IMPORT + #endif +#endif + +class QString; +class QStringList; + +namespace QCE +{ + QString fetchDataFile(const QString& file); + + QStringList dataPathes(); + void addDataPath(const QString& path); + + template + class Registar + { + public: + Registar() + { + Registerable::_register(); + } + }; +} + +#define QCE_AUTO_REGISTER(T) \ + static QCE::Registar _auto_##T##_registar; \ + + + +#endif // !_QCE_CONFIG_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qcodecompletionengine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qcodecompletionengine.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qcodecompletionengine.h" + +/*! + \file qcompletionengine.cpp + \brief Implementation of the QCodeCompletionEngine class. +*/ + +#include "qeditor.h" + +#include +#include +#include + +#ifdef _QCODE_MODEL_ +#include "qcodebuffer.h" +#endif + +/*! + +*/ +QCodeCompletionEngine::QCodeCompletionEngine(QObject *p) + : QObject(p), m_max(0) +{ + pForcedTrigger = new QAction(tr("&Trigger completion"), this); + + connect(pForcedTrigger , SIGNAL( triggered() ), + this , SLOT ( complete() ) ); + +} + +/*! + +*/ +QCodeCompletionEngine::~QCodeCompletionEngine() +{ + +} + +/*! + \return +*/ +QAction* QCodeCompletionEngine::triggerAction() const +{ + return pForcedTrigger; +} + +/*! + +*/ +void QCodeCompletionEngine::retranslate() +{ + pForcedTrigger->setText(tr("&Trigger completion")); +} + +/*! + +*/ +QStringList QCodeCompletionEngine::triggers() const +{ + return m_triggers; +} + +/*! + +*/ +void QCodeCompletionEngine::addTrigger(const QString& s) +{ + if ( m_triggers.contains(s) ) + return; + + if ( s.count() > m_max ) + m_max = s.count(); + + m_triggers << s; +} + +/*! + +*/ +void QCodeCompletionEngine::removeTrigger(const QString& s) +{ + m_triggers.removeAll(s); +} + +/*! + +*/ +void QCodeCompletionEngine::setCodeModel(QCodeModel *m) +{ + Q_UNUSED(m) +} + +/*! + +*/ +QEditor* QCodeCompletionEngine::editor() const +{ + return pEdit; +} + +/*! + \brief Attach the completion engine instance to a new editor object +*/ +void QCodeCompletionEngine::setEditor(QEditor *e) +{ + if ( pEdit ) + { + pEdit->removeAction(pForcedTrigger, "&Edit"); + //pEdit->removeEventFilter(this); + + disconnect( pEdit , SIGNAL( textEdited(QKeyEvent*) ), + this , SLOT ( textEdited(QKeyEvent*) ) ); + } + + pEdit = e; + + if ( pEdit ) + { + //pEdit->installEventFilter(this); + pEdit->addAction(pForcedTrigger, "&Edit"); + + connect(pEdit , SIGNAL( textEdited(QKeyEvent*) ), + this , SLOT ( textEdited(QKeyEvent*) ) ); + } +} + +/*! + \internal +*/ +void QCodeCompletionEngine::run() +{ + if ( m_cur.isNull() ) + return; + + //qDebug("complete!"); + + complete(m_cur, m_trig); + + m_cur = QDocumentCursor(); + m_trig.clear(); +} + +/*! + \brief Forced completion trigger +*/ +void QCodeCompletionEngine::complete() +{ + complete(editor()->cursor(), QString()); +} + +/*! + \brief Standard completion entry point for QEditor + \param e QKeyEvent that caused a modification of the text + + \note This slot is only called when editing happens without + any cursor mirrors +*/ +void QCodeCompletionEngine::textEdited(QKeyEvent *k) +{ + QString s, txt = s = k->text(); + QDocumentCursor cur = editor()->cursor(); + + int count = txt.count(); + + if ( txt.isEmpty() || m_triggers.isEmpty() ) + return; + + //qDebug("should trigger completion? (bis)"); + + if ( count > m_max ) + { + txt = txt.right(m_max); + + } else if ( count < m_max ) { + + QDocumentCursor c(cur); + c.movePosition(m_max, QDocumentCursor::Left, QDocumentCursor::KeepAnchor); + + //qDebug("prev text : %s", qPrintable(c.selectedText())); + + txt = c.selectedText(); + } + + //qDebug("text : %s", qPrintable(txt)); + + foreach ( QString trig, m_triggers ) + { + if ( txt.endsWith(trig) ) + { + cur = editor()->cursor(); + cur.movePosition(trig.count(), QDocumentCursor::PreviousCharacter); + + // notify completion trigger + emit completionTriggered(trig); + + //get rid of previous calltips/completions + editor()->setFocus(); + + // trigger completion + complete(cur, trig); + } + } +} + +/*! + \internal +*/ +bool QCodeCompletionEngine::eventFilter(QObject *o, QEvent *e) +{ + if ( !e || !o || (e->type() != QEvent::KeyPress) || (o != pEdit) ) + return false; + + //qDebug("should trigger completion?"); + + QDocumentCursor cur = editor()->cursor(); + QKeyEvent *k = static_cast(e); + + QString s, txt = s = k->text(); + + int count = txt.count(); + + if ( txt.isEmpty() || m_triggers.isEmpty() ) + return false; // QThread::eventFilter(o, e); + + //qDebug("should trigger completion? (bis)"); + + if ( count > m_max ) + { + txt = txt.right(m_max); + + } else if ( count < m_max ) { + + QDocumentCursor c(cur); + c.movePosition(m_max - count, QDocumentCursor::Left, QDocumentCursor::KeepAnchor); + + //qDebug("prev text : %s", qPrintable(c.selectedText())); + + txt.prepend(c.selectedText()); + } + + //qDebug("text : %s", qPrintable(txt)); + + foreach ( QString trig, m_triggers ) + { + if ( txt.endsWith(trig) ) + { + editor()->write(s); + + cur = editor()->cursor(); + cur.movePosition(trig.count(), QDocumentCursor::PreviousCharacter); + + // notify completion trigger + emit completionTriggered(trig); + + //get rid of previous calltips/completions + editor()->setFocus(); + + // trigger completion + complete(cur, trig); + + return true; + } + } + + return false; +} + +/*! + \brief Completion callback +*/ +void QCodeCompletionEngine::complete(const QDocumentCursor& c, const QString& trigger) +{ + #ifdef _QCODE_MODEL_ + // TODO : + // * use a more efficient design by avoiding deep copy of the data + // * only lex the requested part (stop at cursor or topmost frame required for proper class hierarchy) + + QDocumentCursor cc = c; + cc.movePosition(1, QDocumentCursor::Start, QDocumentCursor::KeepAnchor); + + //qDebug("%s", qPrintable(cc.selectedText())); + + QCodeBuffer buffer(cc.selectedText()); + //QCodeBuffer buffer(c.document()->text()); + complete(&buffer, trigger); + #else + Q_UNUSED(c) + Q_UNUSED(trigger) + qWarning("From complete(QDocumentCursor, QString)"); + qWarning("QCodeCompletionEngine is not self-sufficient : subclasses should " + "reimplement at least on of the complete() method..."); + #endif +} + +/*! + \overload + \brief Overloaded completion callback +*/ +void QCodeCompletionEngine::complete(QCodeStream *s, const QString& trigger) +{ + Q_UNUSED(s) + Q_UNUSED(trigger) + + qWarning("From complete(QCodeStream*, QString)"); + qWarning("QCodeCompletionEngine is not self-sufficient : subclasses should" + "reimplement at least on of the complete() method..."); +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qcodecompletionengine.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qcodecompletionengine.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QCOMPLETION_ENGINE_H_ +#define _QCOMPLETION_ENGINE_H_ + +#include "qce-config.h" + +/*! + \file qcompletionengine.h + \brief Definition of the QCodeCompletionEngine class. +*/ + +#include "qdocumentcursor.h" + +#include +#include +#include + +class QEditor; +class QAction; +class QKeyEvent; +class QCodeModel; +class QCodeStream; + +class QCE_EXPORT QCodeCompletionEngine : public QObject +{ + Q_OBJECT + + public: + QCodeCompletionEngine(QObject *p = 0); + virtual ~QCodeCompletionEngine(); + + virtual QCodeCompletionEngine* clone() = 0; + + virtual QString language() const = 0; + virtual QStringList extensions() const = 0; + + QAction* triggerAction() const; + + QEditor* editor() const; + void setEditor(QEditor *e); + + QStringList triggers() const; + + void addTrigger(const QString& s); + void removeTrigger(const QString& s); + + virtual void setCodeModel(QCodeModel *m); + + virtual void retranslate(); + + signals: + void popup(); + void cloned(QCodeCompletionEngine *e); + void completionTriggered(const QString& s); + + public slots: + void complete(); + void textEdited(QKeyEvent *e); + + protected: + virtual void run(); + virtual bool eventFilter(QObject *o, QEvent *e); + + virtual void complete(QCodeStream *s, const QString& trigger); + virtual void complete(const QDocumentCursor& c, const QString& trigger); + + private: + int m_max; + QString m_trig; + QDocumentCursor m_cur; + QAction *pForcedTrigger; + + QStringList m_triggers; + + QPointer pEdit; +}; + +#endif // _QCOMPLETION_ENGINE_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qcodeedit.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qcodeedit.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,489 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qcodeedit.h" + +/*! + \file qcodeedit.cpp + \brief Implementation of the QCodeEdit class +*/ + +#include "qpanel.h" +#include "qeditor.h" +#include "qpanellayout.h" + +#include +#include +#include +#include +#include +#include +#include + +/*! + \class QCodeEdit + \brief A thin layer over QEditor + + The QCodeEdit class provides simple means to associate panels with editors and manage them. +*/ + +/*! + \internal + \class QPanelWatcher + + A class designed to work around some limitations of the hide/show event system and allow + proper setting and conservation of default visibility for panels. +*/ +class QPanelWatcher : public QObject +{ + public: + QPanelWatcher(QCodeEdit *e) + : qce(e) + { + + } + + bool eventFilter(QObject *o, QEvent *e) + { + QPanel *p = qobject_cast(o); + QAction *a = qce->toggleViewAction(p); + + if ( a ) + { + bool sig = a->signalsBlocked(); + a->blockSignals(true); + + if ( a->isChecked() && e->type() == QEvent::Hide ) + a->setChecked(false); + else if ( !a->isChecked() && e->type() == QEvent::Show ) + a->setChecked(true); + + a->blockSignals(sig); + } + + return QObject::eventFilter(o, e); + } + + private: + QCodeEdit *qce; +}; + +QStringList __qce_data_path; + +/*! + \brief Centralized access point to data fetching +*/ +QString QCE::fetchDataFile(const QString& file) +{ + if ( QFileInfo(file).isAbsolute() ) + return file; + + foreach ( QString dp, __qce_data_path ) + { + QDir d(dp); + + if ( d.exists(file) ) + return d.absoluteFilePath(file); + } + + return file; +} + +/*! + \return The list of pathes used by QCE to fetch data +*/ +QStringList QCE::dataPathes() +{ + return __qce_data_path; +} + +/*! + \brief Add a path to the list of pathes used to fetch data +*/ +void QCE::addDataPath(const QString& path) +{ + if ( !__qce_data_path.contains(path) ) + __qce_data_path << path; +} + +QList QCodeEdit::m_instances; + +/*! + \brief ctor + + The created editor object comes with builtin actions. +*/ +QCodeEdit::QCodeEdit(QWidget *p) + : m_panelsMenu(0) +{ + m_editor = new QEditor(p); + m_watcher = new QPanelWatcher(this); + m_layout = new QPanelLayout(m_editor); + + m_instances << this; +} + +/*! + \brief ctor + \param actions whether the QEditor object should create builtin actions +*/ +QCodeEdit::QCodeEdit(bool actions, QWidget *p) + : m_panelsMenu(0) +{ + m_editor = new QEditor(actions, p); + m_watcher = new QPanelWatcher(this); + m_layout = new QPanelLayout(m_editor); + + m_instances << this; +} + +/*! + \brief ctor + \param layout structure of the panel layout + + The created editor object comes with builtin actions. +*/ +QCodeEdit::QCodeEdit(const QString& layout, QWidget *p) + : m_panelsMenu(0) +{ + m_editor = new QEditor(p); + m_watcher = new QPanelWatcher(this); + m_layout = new QPanelLayout(layout, m_editor); + + m_instances << this; +} + +/*! + \brief ctor + \param layout structure of the panel layout + \param actions whether the QEditor object should create builtin actions +*/ +QCodeEdit::QCodeEdit(const QString& layout, bool actions, QWidget *p) + : m_panelsMenu(0) +{ + m_editor = new QEditor(actions, p); + m_watcher = new QPanelWatcher(this); + m_layout = new QPanelLayout(layout, m_editor); + + m_instances << this; +} + +/*! + \brief ctor + \param e editor to manage + \param p panel layout to associate with the editor +*/ +QCodeEdit::QCodeEdit(QEditor *e, QPanelLayout *p) + : m_panelsMenu(0) +{ + m_editor = e; + m_watcher = new QPanelWatcher(this); + m_layout = p ? p : new QPanelLayout(m_editor); + + m_instances << this; +} + +/*! + \brief ctor + \param e editor to manage + \param l structure of the panel layout +*/ +QCodeEdit::QCodeEdit(QEditor *e, const QString& l) + : m_panelsMenu(0) +{ + m_editor = e; + m_watcher = new QPanelWatcher(this); + m_layout = new QPanelLayout(l, m_editor); + + m_instances << this; +} + +/*! + \brief dtor + + \warning Destroyes the editor and the panel layout it manages +*/ +QCodeEdit::~QCodeEdit() +{ + m_instances.removeAll(this); + + delete m_watcher; + delete m_editor; + delete m_layout; +} + +/*! + \return the managed editor +*/ +QEditor* QCodeEdit::editor() const +{ + return m_editor; +} + +/*! + \return the panel layout associated with the managed editor +*/ +QPanelLayout* QCodeEdit::panelLayout() const +{ + return m_layout; +} + +/*! + \brief Add a panel + \return Toggle view action for the added panel + \param panel panel to add + \param pos position of the panel in the layout + \param _add whether to add the show action of the panel to the menu of the editor +*/ +QAction* QCodeEdit::addPanel(QPanel *panel, Position pos, bool _add) +{ + panel->attach(m_editor); + + QAction *a = new QAction(panel->type(), m_editor); + a->setCheckable(true); + a->setChecked(panel->defaultVisibility()); + + QObject::connect(a , SIGNAL( toggled(bool) ), + panel , SLOT ( setVisible(bool) ) ); + + m_layout->addWidget(panel, QPanelLayout::Position(pos)); + m_layout->update(); + + m_actions << a; + + panel->installEventFilter(m_watcher); + + if ( _add ) + { + if ( !m_panelsMenu ) + { + m_panelsMenu = new QMenu(QEditor::tr("Panels"), m_editor); + m_panelsMenu->menuAction()->setObjectName("panels"); + m_editor->addAction(m_panelsMenu->menuAction(), QEditor::tr("&View"), QString()); + } + + m_panelsMenu->addAction(a); + } + + return a; +} + +/*! + \overload + \return Toggle view action for the added panel + \param name name of panel to add + \param pos position of the panel in the layout + \param _add whether to add the show action of the panel to the menu of the editor +*/ +QAction* QCodeEdit::addPanel(const QString& name, Position pos, bool _add) +{ + return addPanel(QPanel::panel(name, m_editor), pos, _add); +} + +/*! + \return whether the editor has a panel of the given \a type +*/ +bool QCodeEdit::hasPanel(const QString& type) const +{ + if ( !m_layout ) + return false; + + QList l = m_layout->panels(); + + foreach ( QPanel *p, l ) + if ( p->type() == type ) + return true; + + return false; +} + +/*! + \return a list of panels added to the editor + \param type Type of panel to look for (no filtering is performed if empty) +*/ +QList QCodeEdit::panels(const QString& type) const +{ + if ( !m_layout ) + return QList(); + + QList l = m_layout->panels(); + + if ( type.isEmpty() ) + return l; + + int i = 0; + + while ( i < l.count() ) + { + if ( l.at(i)->type() == type ) + { + ++i; + } else { + l.removeAt(i); + } + } + + return l; +} + +/*! + \return the toggle view action of a given panel +*/ +QAction* QCodeEdit::toggleViewAction(QPanel *p) const +{ + int idx = panels().indexOf(p); + return idx == -1 ? 0 : m_actions.at(idx); +} + +/*! + \brief Send a command to every panel of a given type + \param signature method name suitable for QMetaObject::invokeMethod() + \param args list of arguments suitable for QMetaObject::invokeMethod() + + Example use : + \code + sendPanelCommand("Status", "setVisible" Q_COMMAND << Q_ARG(bool, false)); + \endcode +*/ +void QCodeEdit::sendPanelCommand( const QString& type, + const char *signature, + const QList& args) +{ + QList lp = panels(); + + //qDebug("looking for panel of type %s", qPrintable(type)); + + foreach ( QPanel *p, lp ) + { + if ( p && (p->type() == type) ) + { + //qDebug("found."); + + // TODO : ask trolltech to provide an overloaded invokeMetaMethod() + // taking a QList<> instead of nine defaulted args... + + if ( args.isEmpty() ) + QMetaObject::invokeMethod(p, signature); + else if ( args.count() == 1 ) + QMetaObject::invokeMethod( p, signature, + args.at(0)); + else if ( args.count() == 2 ) + QMetaObject::invokeMethod( p, signature, + args.at(0), + args.at(1)); + else if ( args.count() == 3 ) + QMetaObject::invokeMethod( p, signature, + args.at(0), + args.at(1), + args.at(2)); + else if ( args.count() == 4 ) + QMetaObject::invokeMethod( p, signature, + args.at(0), + args.at(1), + args.at(2), + args.at(3)); + else if ( args.count() == 5 ) + QMetaObject::invokeMethod( p, signature, + args.at(0), + args.at(1), + args.at(2), + args.at(3), + args.at(4)); + else if ( args.count() == 6 ) + QMetaObject::invokeMethod( p, signature, + args.at(0), + args.at(1), + args.at(2), + args.at(3), + args.at(4), + args.at(5)); + else if ( args.count() == 7 ) + QMetaObject::invokeMethod( p, signature, + args.at(0), + args.at(1), + args.at(2), + args.at(3), + args.at(4), + args.at(5), + args.at(6)); + else if ( args.count() == 7 ) + QMetaObject::invokeMethod( p, signature, + args.at(0), + args.at(1), + args.at(2), + args.at(3), + args.at(4), + args.at(5), + args.at(6)); + else if ( args.count() == 8 ) + QMetaObject::invokeMethod( p, signature, + args.at(0), + args.at(1), + args.at(2), + args.at(3), + args.at(4), + args.at(5), + args.at(6), + args.at(7)); + else if ( args.count() == 9 ) + QMetaObject::invokeMethod( p, signature, + args.at(0), + args.at(1), + args.at(2), + args.at(3), + args.at(4), + args.at(5), + args.at(7), + args.at(8)); + else if ( args.count() == 10 ) + QMetaObject::invokeMethod( p, signature, + args.at(0), + args.at(1), + args.at(2), + args.at(3), + args.at(4), + args.at(5), + args.at(7), + args.at(8), + args.at(9)); + + } + } +} + +/*! + \return The QCodeEdit object managing a given editor or a null point if the given editor is unmanaged +*/ +QCodeEdit* QCodeEdit::manager(QEditor *e) +{ + foreach ( QCodeEdit *m, m_instances ) + if ( m->m_editor == e ) + return m; + + return 0; +} + +/*! + \brief The (first) managed editor editing a given file or a null pointer if none found +*/ +QEditor* QCodeEdit::managed(const QString& f) +{ + foreach ( QCodeEdit *m, m_instances ) + if ( m && m->m_editor && (m->m_editor->fileName() == f) ) + return m->m_editor; + + return 0; +} + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qcodeedit.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qcodeedit.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QCODE_EDIT_H_ +#define _QCODE_EDIT_H_ + +#include "qce-config.h" + +/*! + \file qcodeedit.h + \brief Definition of the QCodeEdit class +*/ + +#include +#include +#include + +class QMenu; +class QPanel; +class QEditor; +class QWidget; +class QString; +class QAction; +class QPanelLayout; +class QPanelWatcher; + +#define Q_COMMAND QList() + +class QCE_EXPORT QCodeEdit +{ + friend class QPanelWatcher; + + public: + enum Position + { + West, + North, + South, + East + }; + + QCodeEdit(QWidget *p = 0); + QCodeEdit(bool actions, QWidget *p = 0); + QCodeEdit(const QString& layout, QWidget *p = 0); + QCodeEdit(const QString& layout, bool actions, QWidget *p = 0); + virtual ~QCodeEdit(); + + QEditor* editor() const; + QPanelLayout* panelLayout() const; + + QAction* addPanel(QPanel *panel, Position pos, bool _add = false); + QAction* addPanel(const QString& name, Position pos, bool _add = false); + + bool hasPanel(const QString& type) const; + QList panels(const QString& type = QString()) const; + + QAction* toggleViewAction(QPanel *p) const; + + void sendPanelCommand( const QString& type, + const char *signature, + const QList& args = Q_COMMAND); + + static QCodeEdit* manager(QEditor *e); + static QEditor* managed(const QString& f); + + //protected: + QCodeEdit(QEditor *e, QPanelLayout *p); + QCodeEdit(QEditor *e, const QString& l); + + private: + QPanelWatcher *m_watcher; + QPointer m_editor; + QPointer m_layout; + + QMenu *m_panelsMenu; + QList m_actions; + + static QList m_instances; +}; + +#endif // _QCODE_EDIT_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qeditor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qeditor.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,4879 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qeditor.h" + +/*! + \file qeditor.cpp + \brief Implementation of the QEditor class +*/ + +#include "qeditorinputbindinginterface.h" + +#include "qdocument.h" +#include "qdocument_p.h" +#include "qdocumentline.h" +#include "qdocumentcursor.h" + +#include "qlanguagedefinition.h" +#include "qcodecompletionengine.h" + +#include "qcodeedit.h" +#include "qpanellayout.h" +#include "qgotolinedialog.h" +#include "qlinemarksinfocenter.h" + +#include "qreliablefilewatch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define Q_GL_EDITOR + +#ifdef _QMDI_ +#include "qmdiserver.h" +#endif + +#ifdef _EDYUK_ +#include "edyukapplication.h" +#include "qshortcutmanager.h" + +#define Q_SHORTCUT(a, s, c) EDYUK_SHORTCUT(a, tr(c), tr(s)) +#else +#define Q_SHORTCUT(a, s, c) a->setShortcut( QKeySequence( tr(s, c) ) ) +#endif + +#ifdef Q_GL_EDITOR +#include +#endif + +#define QCE_ACTION(name, action) { QAction *_a_ = m_actions.value(name); if ( _a_ ) _a_ action; } +#define QCE_TR_ACTION(name, label) { QAction *_a_ = m_actions.value(name); if ( _a_ ) _a_->setText(label); } +#define QCE_ENABLE_ACTION(name, yes) { QAction *_a_ = m_actions.value(name); if ( _a_ ) _a_->setEnabled(yes); } + +/*! + \ingroup editor + @{ + + \class QEditor + \brief A text editing widget + + QEditor is the central widget in QCE. It allows user to view and edit a + document. + + QEditor has an API similar to that of QTextEdit and it behaves in a very + similar way. + + Notable differences are : +
    +
  • QEditor can be given an InputBinding which can change the way it + handle user inputs which enables such things as implementing emacs-like + or Vi-like editing (almost) effortlessly.
  • +
  • QEditor has actions instead of hard coded shortcuts and expose them + so that, among other things, they can be easily added to menus/toolbars + and their shortcuts can be changed
  • +
  • QEditor brings the notion of cursor mirrors. Column selection and + column editing are just special use case of cursor mirrors.
  • +
  • QEditor brings the notion of placeholders, snippets-editing is just + as special use case of placeholders.
  • +
  • QEditor allows easy encodings management
  • +
+ + QEditor can gain features when it is managed by a QCodeEdit class which + is responsible for panels management. +*/ + + +/*! + \enum QEditor::CodecUpdatePolicy + \brief Specify the actions to take when changing the default codec + +*/ + + +/*! + \enum QEditor::EditFlag + \brief Flag holding information about the state of an editor + + Some of these are public and can be modified freely and some + others are only meant to be used internally though they can + still be read. + +*/ + +/*! + \struct QEditor::PlaceHolder + \brief A small structure holding placeholder data + + Placeholders are basically lists of cursors. When several palceholders coexist, it is + possible to navigate among them using the key assigned to that function by the current + input binding (CTRL+arrows by default). + + Each placeholder consist of a primary cursor and a list of mirrors (modeling the internals + of QEditor and allowing extended snippet replacements easily). + + Additionaly a placeholder can have an Affector which allows the text of the mirrors to be + modified in any imaginable way +*/ + +/*! + \class QEditor::PlaceHolder::Affector + \brief A small class allowing "placeholder scripting" + + The purpose of this class is to affect/process/reformat (whichever word you understand/like + most) the content of placeholder mirrors. + + The main placeholder instance (primary cursor) will always contain the text typed by the user, + only mirrors can be affected. + + To allow a large panel of processing to be done the affector is passed the following data : +
    +
  • A stringlist containing placeholder contents (primary cursor of all placeholders present in the editor) +
  • The index of the current placeholder among these +
  • The key event leading to a modification of the current placeholder +
  • The index of the current mirror +
  • A reference to the text of that placeholder mirror. This text has already been modified according + to the key event. The original text can be retrieved from the first argument (stringlist). This text + can be modified in any way you see fit or left as it is. +
+*/ + +//////////////////////////////////////////////////////////////////////// +// Bindings handling +//////////////////////////////////////////////////////////////////////// + +QList QEditor::m_editors; +QEditorInputBindingInterface* QEditor::m_defaultBinding = 0; +QHash QEditor::m_registeredBindings; + +/*! + \return A list of available input bindings +*/ +QStringList QEditor::registeredInputBindingIds() +{ + return m_registeredBindings.keys(); +} + +/*! + \return the name of the default input binding + + \note The "Default" name (or its translation, obtained via QEditor::tr()) + is used to indicate that no default input binding has been set. +*/ +QString QEditor::defaultInputBindingId() +{ + return m_defaultBinding ? m_defaultBinding->name() : tr("Default"); +} + +/*! + \brief Add an input binding to make it available for all editors +*/ +void QEditor::registerInputBinding(QEditorInputBindingInterface *b) +{ + m_registeredBindings[b->id()] = b; + + foreach ( QEditor *e, m_editors ) + e->updateBindingsMenu(); + +} + +/*! + \brief Remove an input binding from the pool of publicly available ones +*/ +void QEditor::unregisterInputBinding(QEditorInputBindingInterface *b) +{ + m_registeredBindings.remove(b->id()); + + foreach ( QEditor *e, m_editors ) + e->updateBindingsMenu(); + +} + +/*! + \brief Set the default input binding + + \note This does not change the current input binding of existing editors +*/ +void QEditor::setDefaultInputBinding(QEditorInputBindingInterface *b) +{ + m_defaultBinding = b; +} + +/*! + \brief Set the default input binding + + \note If no binding of the given name is available the default (null) + binding will be set back as default binding. + + \note This does not change the current input binding of existing editors +*/ +void QEditor::setDefaultInputBinding(const QString& b) +{ + m_defaultBinding = m_registeredBindings.value(b); +} + +//////////////////////////////////////////////////////////////////////// + +/*! + \return A pointer to the global "reliable" file monitor used by QEditor to avoid file conflicts + + The point of using a custom file watcher is to work around a bug (limitation) of QFileSystemWatcher + which sometimes emit multiple signals for a single file save. It also enables to use a single + object shared by all QEditor instances and reduce memory footprint. +*/ +QReliableFileWatch* QEditor::watcher() +{ + static QPointer _qce_shared; + + if ( !_qce_shared ) + _qce_shared = new QReliableFileWatch; + + return _qce_shared; +} + +//////////////////////////////////////////////////////////////////////// + +int QEditor::m_defaultFlags = QEditor::AutoIndent | QEditor::AdjustIndent; +QTextCodec* QEditor::m_defaultCodec = 0; + +/*! + \return The default flags set to every QEditor upon construction + \note the default flags are a configuration-oriented feature which only expose "user" flags +*/ +int QEditor::defaultFlags() +{ + return m_defaultFlags; +} + +/*! + \brief Set the default editor flags + + Setting editor flags result in them being applied to ALL existing editors + and editors to be created later on. + + These can of course be modified on a per-editor basis later on. +*/ +void QEditor::setDefaultFlags(int flags) +{ + m_defaultFlags = flags & Accessible; + + foreach ( QEditor *e, m_editors ) + { + bool ontoWrap = (m_defaultFlags & LineWrap) && !(e->m_state & LineWrap); + bool outOfWrap = !(m_defaultFlags & LineWrap) && (e->m_state & LineWrap); + + e->m_state &= Internal; + e->m_state |= m_defaultFlags; + + if ( ontoWrap ) + { + e->document()->setWidthConstraint(e->wrapWidth()); + } else if ( outOfWrap ) { + e->document()->clearWidthConstraint(); + } + + QAction *a = e->m_actions.value("wrap"); + + if ( a && (a->isChecked() != (bool)(e->m_state & LineWrap)) ) + a->setChecked(e->m_state & LineWrap); + + } +} + +/*! + \return The default text codec used to load and save document contents + + \note a null pointer indicates that local 8 bit encoding is used. +*/ +QTextCodec* QEditor::defaultCodec() +{ + return m_defaultCodec; +} + +/*! + \overload + \param mib codec identifier + \param update Update policy +*/ +void QEditor::setDefaultCodec(int mib, int update) +{ + setDefaultCodec(QTextCodec::codecForMib(mib), update); +} + +/*! + \overload + \param name name of the codec to use + \param update Update policy +*/ +void QEditor::setDefaultCodec(const char *name, int update) +{ + setDefaultCodec(QTextCodec::codecForName(name), update); +} + +/*! + \overload + \param name name of the codec to use + \param update Update policy +*/ +void QEditor::setDefaultCodec(const QByteArray& name, int update) +{ + setDefaultCodec(QTextCodec::codecForName(name), update); +} + +/*! + \brief Set the default text codec + \param c codec to use + \param update Update policy + + The update policy determines whether existing editors are + affected by the change of the default codec. +*/ +void QEditor::setDefaultCodec(QTextCodec *c, int update) +{ + foreach ( QEditor *e, m_editors ) + { + if ( e->codec() == m_defaultCodec ) + { + if ( update & UpdateOld ) + e->setCodec(c); + } else if ( e->codec() ) { + if ( update & UpdateCustom ) + e->setCodec(c); + } else { + if ( update & UpdateDefault ) + e->setCodec(c); + } + } + + //qDebug("new codec is : 0x%x (%s)", c, c ? c->name().constData() : "System"); + + m_defaultCodec = c; +} + +/*! + \brief ctor + + \note Creates builtin menus/actions +*/ +QEditor::QEditor(QWidget *p) + : QAbstractScrollArea(p), + pMenu(0), m_lineEndingsMenu(0), m_lineEndingsActions(0), + m_bindingsMenu(0), aDefaultBinding(0), m_bindingsActions(0), + m_doc(0), m_codec(m_defaultCodec), m_definition(0), m_curPlaceHolder(-1), m_state(defaultFlags()) +{ + m_editors << this; + + m_saveState = Undefined; + + init(); +} + +/*! + \brief ctor + \param actions Whether builtin actions and menus should be created +*/ +QEditor::QEditor(bool actions, QWidget *p) + : QAbstractScrollArea(p), + pMenu(0), m_lineEndingsMenu(0), m_lineEndingsActions(0), + m_bindingsMenu(0), aDefaultBinding(0), m_bindingsActions(0), + m_doc(0), m_codec(m_defaultCodec), m_definition(0), m_curPlaceHolder(-1), m_state(defaultFlags()) +{ + m_editors << this; + + m_saveState = Undefined; + + init(actions); +} + +/*! + \brief ctor + \param s file to load + + \note Creates builtin menus/actions +*/ +QEditor::QEditor(const QString& s, QWidget *p) + : QAbstractScrollArea(p), + pMenu(0), m_lineEndingsMenu(0), m_lineEndingsActions(0), + m_bindingsMenu(0), aDefaultBinding(0), m_bindingsActions(0), + m_doc(0), m_codec(m_defaultCodec), m_definition(0), m_curPlaceHolder(-1), m_state(defaultFlags()) +{ + m_editors << this; + + m_saveState = Undefined; + + init(); + + setText(s); +} + +/*! + \brief ctor + \param s file to load + \param actions Whether builtin actions and menus should be created + \note Creates builtin menus/action +*/ +QEditor::QEditor(const QString& s, bool actions, QWidget *p) + : QAbstractScrollArea(p), + pMenu(0), m_lineEndingsMenu(0), m_lineEndingsActions(0), + m_bindingsMenu(0), aDefaultBinding(0), m_bindingsActions(0), + m_doc(0), m_codec(m_defaultCodec), m_definition(0), m_curPlaceHolder(-1), m_state(defaultFlags()) +{ + m_editors << this; + + m_saveState = Undefined; + + init(actions); + + setText(s); +} + +/*! + \brief dtor +*/ +QEditor::~QEditor() +{ + m_editors.removeAll(this); + + if ( m_completionEngine ) + delete m_completionEngine; + + if ( m_doc ) + delete m_doc; + + if ( m_editors.isEmpty() ) + { + delete watcher(); + } else { + watcher()->removeWatch(this); + } +} + +/*! + \internal +*/ +void QEditor::init(bool actions) +{ + #ifdef Q_GL_EDITOR + setViewport(new QGLWidget); + #endif + + viewport()->setCursor(Qt::IBeamCursor); + viewport()->setBackgroundRole(QPalette::Base); + //viewport()->setAttribute(Qt::WA_OpaquePaintEvent, true); + viewport()->setAttribute(Qt::WA_KeyCompression, true); + viewport()->setAttribute(Qt::WA_InputMethodEnabled, true); + + verticalScrollBar()->setSingleStep(1); + horizontalScrollBar()->setSingleStep(20); + + setAcceptDrops(true); + //setDragEnabled(true); + setFrameShadow(QFrame::Plain); + setFocusPolicy(Qt::WheelFocus); + setAttribute(Qt::WA_KeyCompression, true); + setAttribute(Qt::WA_InputMethodEnabled, true); + + connect(this , + SIGNAL( markChanged(QString, QDocumentLineHandle*, int, bool) ), + QLineMarksInfoCenter::instance(), + SLOT ( markChanged(QString, QDocumentLineHandle*, int, bool) ) ); + + m_doc = new QDocument(this); + + connect(m_doc , SIGNAL( formatsChange (int, int) ), + this , SLOT ( repaintContent(int, int) ) ); + + connect(m_doc , SIGNAL( contentsChange(int, int) ), + this , SLOT ( updateContent (int, int) ) ); + + connect(m_doc , SIGNAL( formatsChanged() ), + viewport() , SLOT ( update() ) ); + + connect(m_doc , SIGNAL( widthChanged(int) ), + this , SLOT ( documentWidthChanged(int) ) ); + + connect(m_doc , SIGNAL( heightChanged(int) ), + this , SLOT ( documentHeightChanged(int) ) ); + + connect(m_doc , SIGNAL( cleanChanged(bool) ), + this , SLOT ( setContentClean(bool) ) ); + + connect(m_doc , SIGNAL( undoAvailable(bool) ), + this , SIGNAL( undoAvailable(bool) ) ); + + connect(m_doc , SIGNAL( redoAvailable(bool) ), + this , SIGNAL( redoAvailable(bool) ) ); + + connect(m_doc , SIGNAL( markChanged(QDocumentLineHandle*, int, bool) ), + this , SLOT ( markChanged(QDocumentLineHandle*, int, bool) ) ); + + connect(m_doc , SIGNAL( lineEndingChanged(int) ), + this , SLOT ( lineEndingChanged(int) ) ); + + m_cursor = QDocumentCursor(m_doc); + m_cursor.setAutoUpdated(true); + + if ( m_defaultBinding ) + { + m_bindings << m_defaultBinding; + } + + if ( actions ) + { + pMenu = new QMenu; + + QAction *a, *sep; + + a = new QAction(QIcon(":/undo.png"), tr("&Undo"), this); + a->setObjectName("undo"); + Q_SHORTCUT(a, "Ctrl+Z", "Edit"); + a->setEnabled(false); + connect(this , SIGNAL( undoAvailable(bool) ), + a , SLOT ( setEnabled(bool) ) ); + connect(a , SIGNAL( triggered() ), + this , SLOT ( undo() ) ); + + addAction(a, "&Edit", "Edit"); + + a = new QAction(QIcon(":/redo.png"), tr("&Redo"), this); + a->setObjectName("redo"); + Q_SHORTCUT(a, "Ctrl+Y", "Edit"); + a->setEnabled(false); + connect(this , SIGNAL( redoAvailable(bool) ), + a , SLOT ( setEnabled(bool) ) ); + connect(a , SIGNAL( triggered() ), + this , SLOT ( redo() ) ); + + addAction(a, "&Edit", "Edit"); + + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep, "&Edit", "Edit"); + + a = new QAction(QIcon(":/cut.png"), tr("Cu&t"), this); + a->setObjectName("cut"); + Q_SHORTCUT(a, "Ctrl+X", "Edit"); + a->setEnabled(false); + connect(this, SIGNAL( copyAvailable(bool) ), + a , SLOT ( setEnabled(bool) ) ); + connect(a , SIGNAL( triggered() ), + this, SLOT ( cut() ) ); + + addAction(a, "&Edit", "Edit"); + + a = new QAction(QIcon(":/copy.png"), tr("&Copy"), this); + a->setObjectName("copy"); + Q_SHORTCUT(a, "Ctrl+C", "Edit"); + a->setEnabled(false); + connect(this , SIGNAL( copyAvailable(bool) ), + a , SLOT ( setEnabled(bool) ) ); + connect(a , SIGNAL( triggered() ), + this , SLOT ( copy() ) ); + + addAction(a, "&Edit", "Edit"); + + a = new QAction(QIcon(":/paste.png"), tr("&Paste"), this); + a->setObjectName("paste"); + //aPaste->setEnabled(QApplication::clipboard()->text().count()); + Q_SHORTCUT(a, "Ctrl+V", "Edit"); + connect(QApplication::clipboard() , SIGNAL( dataChanged() ), + this , SLOT ( checkClipboard() ) ); + + connect(a , SIGNAL( triggered() ), + this, SLOT ( paste() ) ); + + addAction(a, "&Edit", "Edit"); + + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep, "&Edit", "Edit"); + + a = new QAction(QIcon(":/indent.png"), tr("&Indent"), this); + a->setObjectName("indent"); + Q_SHORTCUT(a, "Ctrl+I", "Edit"); + connect(a , SIGNAL( triggered() ), + this, SLOT ( indentSelection() ) ); + + addAction(a, "&Edit", "Edit"); + + a = new QAction(QIcon(":/unindent.png"), tr("&Unindent"), this); + a->setObjectName("unindent"); + Q_SHORTCUT(a, "Ctrl+Shift+I", "Edit"); + connect(a , SIGNAL( triggered() ), + this, SLOT ( unindentSelection() ) ); + + addAction(a, "&Edit", "Edit"); + + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep, "&Edit", ""); + + a = new QAction(QIcon(":/comment.png"), tr("Co&mment"), this); + a->setObjectName("comment"); + Q_SHORTCUT(a, "Ctrl+D", "Edit"); + connect(a , SIGNAL( triggered() ), + this, SLOT ( commentSelection() ) ); + + addAction(a, "&Edit", "Edit"); + + a = new QAction(QIcon(":/uncomment.png"), tr("Unc&omment"), this); + a->setObjectName("uncomment"); + Q_SHORTCUT(a, "Ctrl+Shift+D", "Edit"); + connect(a , SIGNAL( triggered() ), + this, SLOT ( uncommentSelection() ) ); + + addAction(a, "&Edit", "Edit"); + + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep, "&Edit", ""); + + a = new QAction(tr("&Select all"), this); + a->setObjectName("selectAll"); + Q_SHORTCUT(a, "Ctrl+A", "Edit"); + connect(a , SIGNAL( triggered() ), + this, SLOT ( selectAll() ) ); + + addAction(a, "&Edit", "Edit"); + + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep, QString()); + + a = new QAction(QIcon(":/find.png"), tr("&Find"), this); + a->setObjectName("find"); + Q_SHORTCUT(a, "Ctrl+F", "Search"); + connect(a , SIGNAL( triggered() ), + this, SLOT ( find() ) ); + + addAction(a, "&Search", "Search"); + + a = new QAction(QIcon(":/next.png"), tr("Fin&d next"), pMenu); + a->setObjectName("findNext"); + Q_SHORTCUT(a, "F3", "Search"); + connect(a , SIGNAL( triggered() ), + this, SLOT ( findNext() ) ); + + addAction(a, "&Search", "Search"); + + a = new QAction(QIcon(":/replace.png"), tr("&Replace"), this); + a->setObjectName("replace"); + Q_SHORTCUT(a, "Ctrl+R", "Search"); + connect(a , SIGNAL( triggered() ), + this, SLOT ( replace() ) ); + + addAction(a, "&Search", "Search"); + + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep, "&Search", "Search"); + + a = new QAction(QIcon(":/goto.png"), tr("&Goto line..."), this); + a->setObjectName("goto"); + Q_SHORTCUT(a, "Ctrl+G", "Search"); + connect(a , SIGNAL( triggered() ), + this, SLOT ( gotoLine() ) ); + + addAction(a, "&Search", "Search"); + + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep, "&Edit", ""); + + a = new QAction(tr("Dynamic line wrapping"), this); + a->setObjectName("wrap"); + a->setCheckable(true); + a->setChecked(flag(LineWrap)); + + addAction(a, "&Edit", ""); + + Q_SHORTCUT(a, "F10", "Edit"); + connect(a , SIGNAL( toggled(bool) ), + this, SLOT ( setLineWrapping(bool) ) ); + + + m_bindingsMenu = new QMenu(tr("Input binding"), this); + m_bindingsActions = new QActionGroup(m_bindingsMenu); + //m_bindingsActions->setExclusive(true); + + connect(m_bindingsActions , SIGNAL( triggered(QAction*) ), + this , SLOT ( bindingSelected(QAction*) ) ); + + aDefaultBinding = new QAction(tr("Default"), m_bindingsMenu); + aDefaultBinding->setCheckable(true); + aDefaultBinding->setData("default"); + + m_bindingsMenu->addAction(aDefaultBinding); + m_bindingsMenu->addSeparator(); + m_bindingsActions->addAction(aDefaultBinding); + m_registeredBindings["default"] = 0; + + updateBindingsMenu(); + + m_bindingsMenu->menuAction()->setObjectName("bindings"); + addAction(m_bindingsMenu->menuAction(), "&Edit", ""); + + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep, QString()); + + m_lineEndingsMenu = new QMenu(tr("Line endings"), this); + m_lineEndingsActions = new QActionGroup(m_lineEndingsMenu); + m_lineEndingsActions->setExclusive(true); + + connect(m_lineEndingsActions, SIGNAL( triggered(QAction*) ), + this , SLOT ( lineEndingSelected(QAction*) ) ); + + m_lineEndingsActions->addAction(tr("Conservative"))->setData("conservative"); + m_lineEndingsActions->addAction(tr("Local"))->setData("local"); + m_lineEndingsActions->addAction(tr("Unix/Linux"))->setData("unix"); + m_lineEndingsActions->addAction(tr("Dos/Windows"))->setData("dos"); + m_lineEndingsActions->addAction(tr("Old Mac"))->setData("mac"); + + QList lle = m_lineEndingsActions->actions(); + + foreach ( QAction *a, lle ) + { + a->setCheckable(true); + m_lineEndingsMenu->addAction(a); + } + + lle.at(0)->setChecked(true); + + m_lineEndingsMenu->menuAction()->setObjectName("lineEndings"); + addAction(m_lineEndingsMenu->menuAction(), "&Edit", ""); + + /* + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep, QString()); + */ + } +} + +/*! + \return wether the flag \a f is set +*/ +bool QEditor::flag(EditFlag f) const +{ + return m_state & f; +} + +/*! + \brief Sets the flag \a f +*/ +void QEditor::setFlag(EditFlag f, bool b) +{ + if ( b ) + { + m_state |= f; + + if ( f == LineWrap ) + { + if ( isVisible() ) + m_doc->setWidthConstraint(wrapWidth()); + + m_cursor.refreshColumnMemory(); + + QAction *a = m_actions.value("wrap"); + + if ( a && !a->isChecked() ) + a->setChecked(true); + } + } else { + m_state &= ~f; + + if ( f == LineWrap ) + { + if ( isVisible() ) + m_doc->clearWidthConstraint(); + + m_cursor.refreshColumnMemory(); + + QAction *a = m_actions.value("wrap"); + + if ( a && a->isChecked() ) + a->setChecked(false); + } + } + + // TODO : only update cpos if cursor used to be visible? + if ( f == LineWrap ) + ensureCursorVisible(); + +} + +/*! + \return whether it is possible to call undo() +*/ +bool QEditor::canUndo() const +{ + return m_doc ? m_doc->canUndo() : false; +} + +/*! + \return whether it is possible to call redo() +*/ +bool QEditor::canRedo() const +{ + return m_doc ? m_doc->canRedo() : false; +} + +/*! + \brief Set line wrapping + \param on line wrap on/off + + \note the function also enables "cursor movement within wrapped lines" + which can be disabled manually using setFlag(QEditor::CursorJumpPastWrap, false); +*/ +void QEditor::setLineWrapping(bool on) +{ + setFlag(LineWrap, on); + setFlag(CursorJumpPastWrap, on); +} + +/*! + \return The whole text being edited +*/ +QString QEditor::text() const +{ + return m_doc ? m_doc->text() : QString(); +} + +/*! + \return The text at a given line + \param line text line to extract, using C++ array conventions (start at zero) +*/ +QString QEditor::text(int line) const +{ + return m_doc ? m_doc->line(line).text() : QString(); +} + +/*! + \brief Set the text of the underlying document and update display +*/ +void QEditor::setText(const QString& s) +{ + clearPlaceHolders(); + + if ( m_doc ) + m_doc->setText(s); + + setCursor(QDocumentCursor(m_doc)); + + documentWidthChanged(m_doc->width()); + documentHeightChanged(m_doc->height()); + viewport()->update(); +} + +/*! + \brief Save the underlying document to a file + + \see fileName() +*/ +void QEditor::save() +{ + if ( !m_doc ) + return; + + QString oldFileName = fileName(); + + if ( fileName().isEmpty() ) + { + QString fn = QFileDialog::getSaveFileName(); + + if ( fn.isEmpty() ) + return; + + setFileName(fn); + } else if ( isInConflict() ) { + int ret = QMessageBox::warning(this, + tr("Conflict!"), + tr( + "%1\nhas been modified by another application.\n" + "Press \"Save\" to overwrite the file on disk\n" + "Press \"Reset\"to be reload the file from disk.\n" + "Press \"Discard\" to ignore this warning.\n" + ).arg(fileName()), + QMessageBox::Save + | + QMessageBox::Reset + | + QMessageBox::Discard + | + QMessageBox::Cancel + ); + if ( ret == QMessageBox::Save ) + { + m_saveState = Undefined; + } else if ( ret == QMessageBox::Reset ) { + load(fileName()); + m_saveState = Undefined; + return; + } else if ( ret == QMessageBox::Discard ) { + m_saveState = Undefined; + return; + } else { + return; + } + } + + m_saveState = Saving; + + if ( oldFileName.count() ) + { + watcher()->removeWatch(oldFileName, this); + } + + QFile f(fileName()); + + if ( !f.open(QFile::WriteOnly) ) + { + m_saveState = Undefined; + reconnectWatcher(); + + return; + } + + //QTextStream s(&f); + //s << text(); + QString txt = m_doc->text(flag(RemoveTrailing), flag(PreserveTrailingIndent)); + + if ( m_codec ) + f.write(m_codec->fromUnicode(txt)); + else + f.write(txt.toLocal8Bit()); + + m_doc->setClean(); + + emit saved(this, fileName()); + m_saveState = Saved; + + QTimer::singleShot(100, this, SLOT( reconnectWatcher() )); + + update(); +} + +/*! + \brief Save the content of the editor to a file + + \note This method renames the editor, stop monitoring the old + file and monitor the new one +*/ +void QEditor::save(const QString& fn) +{ + if ( fileName().count() ) + { + watcher()->removeWatch(fileName(), this); + } + + QFile f(fn); + + if ( !f.open(QFile::WriteOnly) ) + { + m_saveState = Undefined; + reconnectWatcher(); + + return; + } + + QString txt = m_doc->text(flag(RemoveTrailing), flag(PreserveTrailingIndent)); + + if ( m_codec ) + f.write(m_codec->fromUnicode(txt)); + else + f.write(txt.toLocal8Bit()); + + m_doc->setClean(); + + setFileName(fn); + emit saved(this, fn); + m_saveState = Saved; + + QTimer::singleShot(100, this, SLOT( reconnectWatcher() )); +} + +/*! + \internal +*/ +void QEditor::checkClipboard() +{ + // LOOKS LIKE THIS FUNCTION NEVER GETS CALLED DESPITE THE CONNECTION... + + //const QMimeData *d = QApplication::clipboard()->mimeData(); + + //qDebug("checking clipboard : %s", d); + + //QCE_ENABLE_ACTION("paste", d && d->hasText()) +} + +/*! + \internal +*/ +void QEditor::reconnectWatcher() +{ + watcher()->addWatch(fileName(), this); +} + +/*! + \internal +*/ +void QEditor::fileChanged(const QString& file) +{ + if ( (file != fileName()) || (m_saveState == Saving) ) + return; + + /* + if ( m_saveState == Saved ) + { + qApp->processEvents(); + + m_saveState = Undefined; + return; + } + */ + + if ( !isContentModified() ) + { + // silently reload file if the editor contains no modification? + // -> result in undo/redo history loss, still ask confirmation ? + bool autoReload = true; + + if ( canUndo() || canRedo() ) + { + int ret = QMessageBox::warning(this, + tr("File changed"), + tr( + "%1\nhas been modified by another application.\n\n" + "Undo/Redo stack would be discarded by the auto-reload.\n" + "Do you wish to keep up to date by reloading the file?" + ).arg(fileName()), + QMessageBox::Yes + | + QMessageBox::No + ); + + if ( ret == QMessageBox::No ) + autoReload = false; + } + + if ( autoReload ) + { + load(fileName()); + m_saveState = Undefined; + return; + } + } + + // TODO : check for actual modification (using a checksum?) + // TODO : conflict reversible (checksum again?) + + //qDebug("conflict!"); + m_saveState = Conflict; +} + +/*! + \return Whether a file conflict has been detected + + File conflicts happen when the loaded file is modified + on disk by another application if the text has been + modified in QCE +*/ +bool QEditor::isInConflict() const +{ + return m_saveState == Conflict; +} + +/*! + \brief Print the content of the editor +*/ +void QEditor::print() +{ + if ( !m_doc ) + return; + + QPrinter printer; + + // TODO : create a custom print dialog, page range sucks, lines range would be better + QPrintDialog dialog(&printer, this); + dialog.setEnabledOptions(QPrintDialog::PrintToFile | QPrintDialog::PrintPageRange); + + if ( dialog.exec() == QDialog::Accepted ) + { + m_doc->print(&printer); + } +} + +/*! + \brief Show the search/replace panel, if any +*/ +void QEditor::find() +{ + QCodeEdit *m = QCodeEdit::manager(this); + + if ( m ) + { + // makes sense hiding this one if present... + m->sendPanelCommand("Goto", "hide"); + + m->sendPanelCommand("Search", + "display", + Q_COMMAND + << Q_ARG(int, 1) + << Q_ARG(bool, false) + ); + + } else { + qDebug("Unmanaged QEditor"); + } +} + +/*! + \brief Ask the search/replace panel, if any, to move to next match +*/ +void QEditor::findNext() +{ + QCodeEdit *m = QCodeEdit::manager(this); + + if ( m ) + { + // makes sense hiding this one if present... + m->sendPanelCommand("Goto", "hide"); + + m->sendPanelCommand("Search", + "find", + Q_COMMAND + << Q_ARG(int, -1) + ); + + } else { + qDebug("Unmanaged QEditor"); + } +} + +/*! + \brief Show the search/replace panel, if any +*/ +void QEditor::replace() +{ + QCodeEdit *m = QCodeEdit::manager(this); + + if ( m ) + { + // makes sense hiding this one if present... + m->sendPanelCommand("Goto", "hide"); + + m->sendPanelCommand("Search", + "display", + Q_COMMAND + << Q_ARG(int, 1) + << Q_ARG(bool, true) + ); + + } else { + qDebug("Unmanaged QEditor"); + } +} + +/*! + \brief Show a panel or dialog to go to a specific line +*/ +void QEditor::gotoLine() +{ + QCodeEdit *m = QCodeEdit::manager(this); + + if ( m && m->hasPanel("Goto") ) + { + // makes sense hiding this one if present... + m->sendPanelCommand("Search", "hide"); + + m->sendPanelCommand("Goto", "show"); + } else { + QGotoLineDialog dlg(this); + + dlg.exec(this); + } +} + +/*! + \brief Run time translation entry point for compat with Edyuk +*/ +void QEditor::retranslate() +{ + QCE_TR_ACTION("undo", tr("&Undo")) + QCE_TR_ACTION("redo", tr("&Redo")) + + QCE_TR_ACTION("cut", tr("Cu&t")) + QCE_TR_ACTION("copy", tr("&Copy")) + QCE_TR_ACTION("paste", tr("&Paste")) + + QCE_TR_ACTION("indent", tr("&Indent")) + QCE_TR_ACTION("unindent", tr("&Unindent")) + QCE_TR_ACTION("comment", tr("Co&mment")) + QCE_TR_ACTION("uncomment", tr("Unc&omment")) + + QCE_TR_ACTION("selectAll", tr("&Select all")) + + QCE_TR_ACTION("find", tr("&Find")) + QCE_TR_ACTION("findNext", tr("Fin&d next")) + QCE_TR_ACTION("replace", tr("&Replace")) + + QCE_TR_ACTION("goto", tr("&Goto line...")) + + if ( m_completionEngine ) + m_completionEngine->retranslate(); + + if ( m_bindingsMenu ) + m_bindingsMenu->setTitle(tr("Input binding")); + + if ( aDefaultBinding ) + aDefaultBinding->setText(tr("Default")); + + #ifdef _QMDI_ + menus.setTranslation("&Edit", tr("&Edit")); + menus.setTranslation("&Search", tr("&Search")); + + toolbars.setTranslation("Edit", tr("Edit")); + toolbars.setTranslation("Search", tr("Search")); + #endif +} + +/*! + \return the action associated with a given name, if the QEditor has been created with actions on +*/ +QAction* QEditor::action(const QString& s) +{ + QHash::const_iterator it = m_actions.constFind(s); + + return it != m_actions.constEnd() ? *it : 0; +} + +/*! + \brief Add an action to the editor + \param a action to add + \param menu if not empty (and if QCE is built with qmdilib support) the action will be added to that menu + \param toolbar similar to \a menu but acts on toolbars + + \see removeAction() +*/ +void QEditor::addAction(QAction *a, const QString& menu, const QString& toolbar) +{ + if ( !a ) + return; + + QWidget::addAction(a); + + m_actions[a->objectName()] = a; + + if ( pMenu && menu.count() ) + { + pMenu->addAction(a); + + #ifdef _QMDI_ + menus[menu]->addAction(a); + #endif + } + + if ( toolbar.count() ) + { + #ifdef _QMDI_ + toolbars[toolbar]->addAction(a); + #endif + } +} + +/*! + \brief remove an action form the editor + \param a action to add + \param menu if not empty (and if QCE is built with qmdilib support) the action will be added to that menu + \param toolbar similar to \a menu but acts on toolbars + + \see addAction() +*/ +void QEditor::removeAction(QAction *a, const QString& menu, const QString& toolbar) +{ + if ( !a ) + return; + + QWidget::removeAction(a); + + //m_actions.remove(a->objectName()); + + if ( pMenu ) + pMenu->removeAction(a); + + #ifdef _QMDI_ + if ( menu.count() ) + { + menus[menu]->removeAction(a); + } + + if ( toolbar.count() ) + { + toolbars[toolbar]->removeAction(a); + } + #else + Q_UNUSED(menu) + Q_UNUSED(toolbar) + #endif +} + +/*! + \brief load a text file + \param file file to load + + If the file cannot be loaded, previous content is cleared. +*/ +void QEditor::load(const QString& file) +{ + QFile f(file); + + // gotta handle line endings ourselves if we want to detect current line ending style... + //if ( !f.open(QFile::Text | QFile::ReadOnly) ) + if ( !f.open(QFile::ReadOnly) ) + { + setText(QString()); + return; + } + + const int size = f.size(); + //const int size = m_lastFileState.size = f.size(); + + if ( size < 500000 ) + { + // instant load for files smaller than 500kb + QByteArray d = f.readAll(); + //m_lastFileState.checksum = qChecksum(d.constData(), d.size()); + if ( m_codec ) + setText(m_codec->toUnicode(d)); + else + setText(QString::fromLocal8Bit(d)); + } else { + // load by chunks of 100kb otherwise to avoid huge peaks of memory usage + // and driving mad the disk drivers + + int count = 0; + QByteArray ba; + + m_doc->startChunkLoading(); + //m_lastFileState.checksum = 0; + + do + { + ba = f.read(100000); + count += ba.count(); + + //m_lastFileState.checksum ^= qChecksum(ba.constData(), ba.size()); + + if ( m_codec ) + m_doc->addChunk(m_codec->toUnicode(ba)); + else + m_doc->addChunk(QString::fromLocal8Bit(ba)); + + } while ( (count < size) && ba.count() ); + + m_doc->stopChunkLoading(); + + setCursor(QDocumentCursor(m_doc)); + + documentWidthChanged(m_doc->width()); + documentHeightChanged(m_doc->height()); + } + + m_doc->setLastModified(QFileInfo(file).lastModified()); + + //qDebug("checksum = %i", m_lastFileState.checksum); + + if ( m_lineEndingsActions ) + { + // TODO : update Conservative to report original line endings + static const QRegExp rx(" \\[\\w+\\]"); + QAction *a = m_lineEndingsActions->actions().at(0); + + if ( a ) + { + QDocument::LineEnding le = m_doc->originalLineEnding(); + + QString det, txt = a->text(); + txt.remove(rx); + + if ( le == QDocument::Windows ) + det = tr("Windows"); + else if ( le == QDocument::OldMac ) + det = tr("Old Mac"); + else if ( le == QDocument::Unix ) + det = tr("Unix"); + + if ( det.count() ) + { + txt += " ["; + txt += det; + txt += ']'; + } + + a->setText(txt); + } + } + + setFileName(file); + + emit loaded(this, file); +} + +/*! + \return a pointer to the underlying QDocument object +*/ +QDocument* QEditor::document() const +{ + return m_doc; +} + +/*! + \internal +*/ +void QEditor::setDocument(QDocument *d) +{ + Q_UNUSED(d) + + qWarning("QEditor::setDocument() is not working yet..."); +} + +/*! + \return The text codec to use for load/save operations +*/ +QTextCodec* QEditor::codec() const +{ + return m_codec; +} + +/*! + \overload +*/ +void QEditor::setCodec(int mib) +{ + setCodec(QTextCodec::codecForMib(mib)); +} + +/*! + \overload +*/ +void QEditor::setCodec(const char *name) +{ + setCodec(QTextCodec::codecForName(name)); +} + +/*! + \overload +*/ +void QEditor::setCodec(const QByteArray& name) +{ + setCodec(QTextCodec::codecForName(name)); +} + +/*! + \brief Set the text codec to use for load/save operations +*/ +void QEditor::setCodec(QTextCodec *c) +{ + if ( c == m_codec ) + return; + + m_codec = c; + + // TODO : reload file? + if ( fileName().count() && QFile::exists(fileName()) ) + { + if ( !isContentModified() ) + { + load(fileName()); + } + } +} + +/*! + \brief Force a full re-highlighting of the document +*/ +void QEditor::highlight() +{ + m_doc->highlight(); + //updateContent(0, m_doc->lines()); +} + +/*! + \return the current InputBinding +*/ +QList QEditor::inputBindings() const +{ + return m_bindings; +} + +/*! + \brief Set the current input binding +*/ +void QEditor::addInputBinding(QEditorInputBindingInterface *b) +{ + if ( b ) + m_bindings << b; + + if ( !aDefaultBinding || !m_bindingsActions ) + return; + + QString id = b ? b->id() : QString(); + aDefaultBinding->setChecked(!b); + + if ( !b ) + return; + + QList actions = m_bindingsActions->actions(); + + foreach ( QAction *a, actions ) + { + if ( a->data().toString() != id ) + a->setChecked(true); + } +} + +/*! + \brief Set the current input binding +*/ +void QEditor::removeInputBinding(QEditorInputBindingInterface *b) +{ + int n = m_bindings.removeAll(b); + + if ( !aDefaultBinding || !m_bindingsActions || !n ) + return; + + QString id = b ? b->id() : QString(); + aDefaultBinding->setChecked(!b); + + if ( !b ) + return; + + QList actions = m_bindingsActions->actions(); + + foreach ( QAction *a, actions ) + { + if ( a->data().toString() != id ) + a->setChecked(false); + } +} + +/*! + \brief Set the current input binding +*/ +void QEditor::setInputBinding(QEditorInputBindingInterface *b) +{ + m_bindings.clear(); + + if ( b ) + m_bindings << b; + + if ( !aDefaultBinding || !m_bindingsActions ) + return; + + QString id = b ? b->id() : QString(); + aDefaultBinding->setChecked(!b); + + if ( !b ) + return; + + QList actions = m_bindingsActions->actions(); + + foreach ( QAction *a, actions ) + { + if ( a ) + a->setChecked(a->data().toString() != id); + } +} + +/*! + \internal +*/ +void QEditor::updateBindingsMenu() +{ + if ( !aDefaultBinding || !m_bindingsMenu || !m_bindingsActions ) + return; + + QStringList bindings = registeredInputBindingIds(); + QList actions = m_bindingsActions->actions(); + + aDefaultBinding->setChecked(m_bindings.contains(m_defaultBinding)); + + foreach ( QAction *a, actions ) + { + int idx = bindings.indexOf(a->data().toString()); + + if ( idx == -1 ) + { + m_bindingsMenu->removeAction(a); + m_bindingsActions->removeAction(a); + delete a; + } else { + bindings.removeAt(idx); + + foreach ( QEditorInputBindingInterface *b, m_bindings ) + if ( a->data().toString() == b->id() ) + a->setChecked(true); + + } + } + + bindings.removeAll("default"); + + foreach ( QString s, bindings ) + { + QEditorInputBindingInterface *b = m_registeredBindings.value(s); + + if ( !b ) + continue; + + QAction *a = new QAction(b->name(), m_bindingsMenu); + a->setData(b->id()); + a->setCheckable(true); + + m_bindingsActions->addAction(a); + m_bindingsMenu->addAction(a); + } +} + +/*! + \internal +*/ +void QEditor::bindingSelected(QAction *a) +{ + //a = m_bindingsActions->checkedAction(); + + if ( !a ) + return; + + QEditorInputBindingInterface *b = m_registeredBindings.value(a->data().toString()); + + if ( a->isChecked() ) + addInputBinding(b); + else + removeInputBinding(b); + + //qDebug("setting binding to %s [0x%x]", qPrintable(a->data().toString()), m_binding); + + updateMicroFocus(); +} + +/*! + \internal +*/ +void QEditor::lineEndingSelected(QAction *a) +{ + a = m_lineEndingsActions->checkedAction(); + + if ( !a ) + return; + + QString le = a->data().toString(); + + if ( le == "conservative" ) + m_doc->setLineEnding(QDocument::Conservative); + else if ( le == "local" ) + m_doc->setLineEnding(QDocument::Local); + else if ( le == "unix" ) + m_doc->setLineEnding(QDocument::Unix); + else if ( le == "dos" ) + m_doc->setLineEnding(QDocument::Windows); + else if ( le == "mac" ) + m_doc->setLineEnding(QDocument::OldMac); + + + updateMicroFocus(); +} + +/*! + \internal +*/ +void QEditor::lineEndingChanged(int lineEnding) +{ + if ( !m_lineEndingsActions ) + return; + + QAction *a = m_lineEndingsActions->checkedAction(), + *n = m_lineEndingsActions->actions().at(lineEnding); + + if ( a != n ) + n->setChecked(true); + +} + +/*! + \return the current cursor +*/ +QDocumentCursor QEditor::cursor() const +{ + QDocumentCursor copy = m_cursor; + copy.setAutoUpdated(false); + return copy; +} + +/*! + \brief Set the document cursor +*/ +void QEditor::setCursor(const QDocumentCursor& c) +{ + repaintCursor(); + + m_cursor = c.isValid() ? c : QDocumentCursor(m_doc); + m_cursor.setAutoUpdated(true); + clearCursorMirrors(); + + if ( m_curPlaceHolder != -1 ) + { + const PlaceHolder& ph = m_placeHolders[m_curPlaceHolder]; + + if ( !ph.cursor.isWithinSelection(m_cursor) ) + { + m_curPlaceHolder = -1; + viewport()->update(); + } + } + + emitCursorPositionChanged(); + + setFlag(CursorOn, true); + repaintCursor(); + ensureCursorVisible(); + selectionChange(); + + updateMicroFocus(); +} + +/*! + \brief Set the cursor + \param line document line to move the cursor to (start at zero) + \param index column index of the new cursor (start at zero) +*/ +void QEditor::setCursorPosition(int line, int index) +{ + setCursor(QDocumentCursor(m_doc, line, index)); +} + +/*! + \brief Write the current cursor position to to integers +*/ +void QEditor::getCursorPosition(int &line, int &index) +{ + line = m_cursor.lineNumber(); + index = m_cursor.columnNumber(); +} + +/*! + \return the number of cursor mirrors currently used +*/ +int QEditor::cursorMirrorCount() const +{ + return m_mirrors.count(); +} + +/*! + \return the cursor mirror at index \a i + + Index has no extra meaning : you cannot deduce anything about + the cursor mirror it corresponds to from it. For instance, the + cursor mirror at index 0 is neither the first mirror added nor + the one at smallest document position (well : it *might* be but + that would be a coincidence...) +*/ +QDocumentCursor QEditor::cursorMirror(int i) const +{ + return i >= 0 && i < m_mirrors.count() ? m_mirrors.at(i) : QDocumentCursor(); +} + +/*! + \brief Clear all placeholders +*/ +void QEditor::clearPlaceHolders() +{ + bool updateView = m_placeHolders.count() && m_curPlaceHolder != -1; + + m_curPlaceHolder = -1; + + for ( int i = 0; i < m_placeHolders.count(); ++i ) + { + PlaceHolder& ph = m_placeHolders[i]; + + ph.cursor.setAutoUpdated(false); + + for ( int j = 0; j < ph.mirrors.count(); ++j ) + { + ph.mirrors[j].setAutoUpdated(false); + } + + ph.mirrors.clear(); + } + + m_placeHolders.clear(); + + if ( updateView ) + viewport()->update(); + +} + +/*! + \brief Add a placeholder + \param p placeholder data + \param autoUpdate whether to force auto updating of all cursors used by the placeholder + + Auto update is on by default and it is recommended not to disable it unless you know what you are doing. +*/ +void QEditor::addPlaceHolder(const PlaceHolder& p, bool autoUpdate) +{ + m_placeHolders << p; + + PlaceHolder& ph = m_placeHolders.last(); + + ph.cursor.setAutoUpdated(autoUpdate); + ph.cursor.movePosition(ph.length, QDocumentCursor::NextCharacter, QDocumentCursor::KeepAnchor); + + for ( int i = 0; i < ph.mirrors.count(); ++i ) + { + ph.mirrors[i].setAutoUpdated(autoUpdate); + ph.mirrors[i].movePosition(ph.length, QDocumentCursor::NextCharacter, QDocumentCursor::KeepAnchor); + } +} + +/*! + \brief Remove a placeholder + \param i placeholder index + + \note if the current placeholder is removed there will be NO automatic switching to a remaining one. +*/ +void QEditor::removePlaceHolder(int id) +{ + if ( id == m_curPlaceHolder) + clearCursorMirrors(); + + PlaceHolder& ph = m_placeHolders[id]; + + for ( int i = 0; i < ph.mirrors.count(); ++i ) + ph.mirrors[i].setAutoUpdated(false); + + ph.mirrors.clear(); + ph.cursor.setAutoUpdated(false); + m_placeHolders.removeAt(id); + + if ( id < m_curPlaceHolder ) + --m_curPlaceHolder; + +} + +/*! + \return the number of placeholders currently set +*/ +int QEditor::placeHolderCount() const +{ + return m_placeHolders.count(); +} + +/*! + \return the id of the current placeholder +*/ +int QEditor::currentPlaceHolder() const +{ + return m_curPlaceHolder; +} + +/*! + \brief Set the current placeholder to use + + This function change the cursor and the cursor mirrors. +*/ +void QEditor::setPlaceHolder(int i) +{ + if ( i < 0 || i >= m_placeHolders.count() ) + return; + + clearCursorMirrors(); + + const PlaceHolder& ph = m_placeHolders.at(i); + QDocumentCursor cc = ph.cursor; + + setCursor(cc); + + /* + ditch cursor mirrors in favor of QDocumentCursor::replaceSelectedText() + + * workaround mirror limitations re movement (no way to ensure proper + synchronization when moving up/down) + + * make it relatively easy to implement affectors + */ + + m_curPlaceHolder = i; + + viewport()->update(); +} + +/*! + \brief Move to next placeholder + + \see setPlaceHolder +*/ +void QEditor::nextPlaceHolder() +{ + if ( m_placeHolders.isEmpty() ) + return; + + ++m_curPlaceHolder; + + if ( m_curPlaceHolder >= m_placeHolders.count() ) + m_curPlaceHolder = 0; + + setPlaceHolder(m_curPlaceHolder); +} + +/*! + \brief Move to previous placeholder + + \see setPlaceHolder +*/ +void QEditor::previousPlaceHolder() +{ + if ( m_placeHolders.isEmpty() ) + return; + + if ( m_curPlaceHolder <= 0 ) + m_curPlaceHolder = m_placeHolders.count(); + + --m_curPlaceHolder; + + setPlaceHolder(m_curPlaceHolder); +} + +/*! + \return the code completion engine set to this editor, if any +*/ +QCodeCompletionEngine* QEditor::completionEngine() const +{ + return m_completionEngine; +} + +/*! + \brief Set a code completion engine to the editor + + \warning Most completion engines can only be attached + to a single editor due to issues in the widget used to + dispaly matches so you got to clone them and, as a consequence + QEditor will take ownership of the completion engines + and delete them. +*/ +void QEditor::setCompletionEngine(QCodeCompletionEngine *e) +{ + if ( m_completionEngine ) + { + m_completionEngine->setEditor(0); + m_completionEngine->deleteLater(); + } + + m_completionEngine = e; + + if ( m_completionEngine ) + { + m_completionEngine->setEditor(this); + } +} + +/*! + \return the language definition set to this editor, if any +*/ +QLanguageDefinition* QEditor::languageDefinition() const +{ + return m_definition; +} + +/*! + \brief Set a language definition to the editor +*/ +void QEditor::setLanguageDefinition(QLanguageDefinition *d) +{ + m_definition = d; + + if ( m_doc ) + m_doc->setLanguageDefinition(d); + + if ( m_definition ) + { + bool cuc = d->singleLineComment().count(); + + QCE_ENABLE_ACTION("comment", cuc) + QCE_ENABLE_ACTION("uncomment", cuc) + } else { + QCE_ENABLE_ACTION("comment", false) + QCE_ENABLE_ACTION("uncomment", false) + } +} + +/*! + \return the line at a given viewport position +*/ +QDocumentLine QEditor::lineAtPosition(const QPoint& p) const +{ + return m_doc ? m_doc->lineAt(p) : QDocumentLine(); +} + +/*! + \return The cursor object nearest to the given viewport position +*/ +QDocumentCursor QEditor::cursorForPosition(const QPoint& p) const +{ + //qDebug("cursor for : (%i, %i)", p.x(), p.y()); + + return m_doc ? m_doc->cursorAt(p) : QDocumentCursor(); +} + +/*! + \brief Set the cursor to that nearest to a given viewport position +*/ +void QEditor::setCursorPosition(const QPoint& p) +{ + //qDebug("cursor for : (%i, %i)", p.x(), p.y()); + + QDocumentCursor c = cursorForPosition(p); + + if ( c.isValid() ) + { + setCursor(c); + } +} + +/*! + \brief Emitted whenever the position of the cursor changes +*/ +void QEditor::emitCursorPositionChanged() +{ + emit cursorPositionChanged(); + emit copyAvailable(m_cursor.hasSelection()); + + if ( m_definition ) + m_definition->match(m_cursor); + + if ( m_doc->impl()->hasMarks() ) + QLineMarksInfoCenter::instance()->cursorMoved(this); + +} + +/*! + \brief Undo the last editing operation, if any on the stack +*/ +void QEditor::undo() +{ + if ( m_doc ) + { + if ( m_definition ) + m_definition->clearMatches(m_doc); + + m_doc->undo(); + + selectionChange(); + ensureCursorVisible(); + setFlag(CursorOn, true); + emitCursorPositionChanged(); + repaintCursor(); + } +} + +/*! + \brief Redo the last undone editing operation, if any on the stack +*/ +void QEditor::redo() +{ + if ( m_doc ) + { + if ( m_definition ) + m_definition->clearMatches(m_doc); + + m_doc->redo(); + + selectionChange(); + ensureCursorVisible(); + setFlag(CursorOn, true); + emitCursorPositionChanged(); + repaintCursor(); + } +} + +/*! + \brief Cut the selected text, if any +*/ +void QEditor::cut() +{ + copy(); + + bool macro = m_mirrors.count(); + + if ( macro ) + m_doc->beginMacro(); + + m_cursor.removeSelectedText(); + + for ( int i = 0; i < m_mirrors.count(); ++i ) + m_mirrors[i].removeSelectedText(); + + if ( macro ) + m_doc->endMacro(); + + clearCursorMirrors(); + + ensureCursorVisible(); + setFlag(CursorOn, true); + emitCursorPositionChanged(); + repaintCursor(); +} + +/*! + \brief Copy the selected text, if any + + \note Column selection may be created but the can only be copied properly in a QCE editor +*/ +void QEditor::copy() +{ + if ( !m_cursor.hasSelection() ) + return; + + QMimeData *d = createMimeDataFromSelection(); + QApplication::clipboard()->setMimeData(d); + + //qDebug("%s", qPrintable(m_cursor.selectedText())); + //QApplication::clipboard()->setText(m_cursor.selectedText()); +} + +/*! + \brief Paste text from the clipboard to the current cursor position + + \note May paste column selections from other QCE editors +*/ +void QEditor::paste() +{ + const QMimeData *d = QApplication::clipboard()->mimeData(); + + if ( d ) + insertFromMimeData(d); +} + +static bool unindent(const QDocumentCursor& cur) +{ + QDocumentLine beg(cur.line()); + int r = 0, n = 0, t = QDocument::tabStop(); + QString txt = beg.text().left(beg.firstChar()); + + while ( txt.count() && (n < t) ) + { + if ( txt.at(txt.length() - 1) == '\t' ) + n += t - (n % t); + else + ++n; + + ++r; + txt.chop(1); + } + + if ( !r ) + return false; + + QDocumentCursor c(cur); + c.setSilent(true); + c.movePosition(1, QDocumentCursor::StartOfBlock, QDocumentCursor::MoveAnchor); + c.movePosition(r, QDocumentCursor::Right, QDocumentCursor::KeepAnchor); + c.removeSelectedText(); + + return true; +} + +static void insert(const QDocumentCursor& cur, const QString& txt) +{ + QDocumentCursor c(cur); + c.setSilent(true); + c.setColumnNumber(0); + c.insertText(txt); +} + +static void removeFromStart(const QDocumentCursor& cur, const QString& txt) +{ + QDocumentLine l = cur.line(); + int pos = l.firstChar(); + + if ( l.text().mid(pos, txt.length()) != txt ) + return; + + QDocumentCursor c(cur.document(), cur.lineNumber(), pos); + c.setSilent(true); + c.movePosition(txt.length(), + QDocumentCursor::NextCharacter, + QDocumentCursor::KeepAnchor); + c.removeSelectedText(); +} + +/*! + \brief Indent the selection +*/ +void QEditor::indentSelection() +{ + // TODO : respect tab stops in case of screwed up indent (correct it?) + + QString txt = flag(ReplaceTabs) ? QString(m_doc->tabStop(), ' ') : QString("\t"); + + if ( m_mirrors.count() ) + { + m_doc->beginMacro(); + + if ( !protectedCursor(m_cursor) ) + insert(m_cursor, txt); + + foreach ( const QDocumentCursor& m, m_mirrors ) + if ( !protectedCursor(m) ) + insert(m, txt); + + m_doc->endMacro(); + + } else if ( !m_cursor.hasSelection() ) { + if ( !protectedCursor(m_cursor) ) + insert(m_cursor, txt); + } else if ( !protectedCursor(m_cursor) ) { + QDocumentSelection s = m_cursor.selection(); + QDocumentCursor c(m_doc, s.startLine); + c.setSilent(true); + c.beginEditBlock(); + + while ( c.isValid() && (c.lineNumber() <= s.endLine) ) + { + c.insertText(txt); + c.movePosition(1, QDocumentCursor::NextLine); + + if ( c.atEnd() ) + break; + } + + c.endEditBlock(); + } +} + +/*! + \brief Unindent the selection +*/ +void QEditor::unindentSelection() +{ + if ( !m_cursor.line().firstChar() ) + return; + + if ( m_mirrors.count() ) + { + m_doc->beginMacro(); + + if ( !protectedCursor(m_cursor) ) + unindent(m_cursor); + + foreach ( const QDocumentCursor& m, m_mirrors ) + unindent(m); + + m_doc->endMacro(); + + } else if ( !m_cursor.hasSelection() ) { + unindent(m_cursor); + } else if ( !protectedCursor(m_cursor) ) { + QDocumentSelection s = m_cursor.selection(); + + m_doc->beginMacro(); + + for ( int i = s.startLine; i <= s.endLine; ++i ) + { + unindent(QDocumentCursor(m_doc, i)); + } + + m_doc->endMacro(); + } +} + +/*! + \brief Comment the selection (or the current line) using single line comments supported by the language +*/ +void QEditor::commentSelection() +{ + if ( !m_definition ) + return; + + QString txt = m_definition->singleLineComment(); + + if ( txt.isEmpty() ) + return; + + if ( m_mirrors.count() ) + { + m_doc->beginMacro(); + + if ( !protectedCursor(m_cursor) ) + insert(m_cursor, txt); + + foreach ( const QDocumentCursor& m, m_mirrors ) + if ( !protectedCursor(m) ) + insert(m, txt); + + m_doc->endMacro(); + + } else if ( !m_cursor.hasSelection() ) { + if ( !protectedCursor(m_cursor) ) + insert(m_cursor, txt); + } else if ( !protectedCursor(m_cursor) ) { + QDocumentSelection s = m_cursor.selection(); + QDocumentCursor c(m_doc, s.startLine); + c.setSilent(true); + c.beginEditBlock(); + + while ( c.isValid() && (c.lineNumber() <= s.endLine) ) + { + c.insertText(txt); + c.movePosition(1, QDocumentCursor::NextLine); + + if ( c.atEnd() ) + break; + } + + c.endEditBlock(); + } +} + +/*! + \brief Uncomment the selection (or current line), provided it has been commented with single line comments +*/ +void QEditor::uncommentSelection() +{ + if ( !m_definition ) + return; + + QString txt = m_definition->singleLineComment(); + + if ( txt.isEmpty() ) + return; + + if ( m_mirrors.count() ) + { + m_doc->beginMacro(); + + if ( !protectedCursor(m_cursor) ) + removeFromStart(m_cursor, txt); + + foreach ( const QDocumentCursor& m, m_mirrors ) + if ( !protectedCursor(m) ) + removeFromStart(m, txt); + + m_doc->endMacro(); + + } else if ( !m_cursor.hasSelection() ) { + if ( !protectedCursor(m_cursor) ) + removeFromStart(m_cursor, txt); + } else if ( !protectedCursor(m_cursor) ) { + QDocumentSelection s = m_cursor.selection(); + + m_doc->beginMacro(); + + for ( int i = s.startLine; i <= s.endLine; ++i ) + { + removeFromStart(QDocumentCursor(m_doc, i), txt); + } + + m_doc->endMacro(); + } +} + +/*! + \brief Select the whole text +*/ +void QEditor::selectAll() +{ + clearCursorMirrors(); + + m_cursor.movePosition(1, QDocumentCursor::Start); + m_cursor.movePosition(1, QDocumentCursor::End, QDocumentCursor::KeepAnchor); + + emitCursorPositionChanged(); + selectionChange(true); + + viewport()->update(); +} + +/*! + \internal +*/ +bool QEditor::event(QEvent *e) +{ + bool r = QAbstractScrollArea::event(e); + + if ( (e->type() == QEvent::Resize || e->type() == QEvent::Show) && m_doc ) + verticalScrollBar()->setMaximum(qMax(0, 1 + (m_doc->height() - viewport()->height()) / m_doc->fontMetrics().lineSpacing())); + + if ( e->type() == QEvent::Resize && flag(LineWrap) ) + { + //qDebug("resize adjust (1) : wrapping to %i", viewport()->width()); + m_doc->setWidthConstraint(wrapWidth()); + ensureCursorVisible(); + } + + return r; +} + +/*! + \internal +*/ +void QEditor::paintEvent(QPaintEvent *e) +{ + if ( !m_doc ) + return; + + QPainter p(viewport()); + const int yOffset = verticalOffset(); + const int xOffset = horizontalOffset(); + + #ifdef Q_GL_EDITOR + //QRect r(e->rect()); + QRect r(0, 0, viewport()->width(), viewport()->height()); + #else + QRect r(e->rect()); + #endif + + //qDebug() << r; + + //p.setClipping(false); + p.translate(-xOffset, -yOffset); + + QDocument::PaintContext ctx; + ctx.xoffset = xOffset; + ctx.yoffset = r.y() + yOffset; + ctx.width = viewport()->width(); + ctx.height = qMin(r.height(), viewport()->height()); + ctx.palette = palette(); + ctx.cursors << m_cursor.handle(); + ctx.fillCursorRect = true; + ctx.blinkingCursor = flag(CursorOn); + + if ( m_cursor.hasSelection() ) + { + //qDebug("atempting to draw selected text"); + QDocumentSelection s = m_cursor.selection(); + + ctx.selections << s; + } + + // cursor mirrors :D + foreach ( const QDocumentCursor& m, m_mirrors ) + { + if ( ctx.blinkingCursor ) + ctx.extra << m.handle(); + + if ( m.hasSelection() ) + { + QDocumentSelection s = m.selection(); + + ctx.selections << s; + } + } + + if ( m_dragAndDrop.isValid() ) + { + ctx.extra << m_dragAndDrop.handle(); + } + + p.save(); + m_doc->draw(&p, ctx); + p.restore(); + + if ( m_curPlaceHolder >= 0 && m_curPlaceHolder < m_placeHolders.count() ) + { + const PlaceHolder& ph = m_placeHolders.at(m_curPlaceHolder); + + p.setPen(Qt::red); + p.drawConvexPolygon(ph.cursor.documentRegion()); + + p.setPen(Qt::yellow); + foreach ( const QDocumentCursor& m, ph.mirrors ) + { + if ( m.isValid() ) + p.drawConvexPolygon(m.documentRegion()); + } + } + + if ( viewport()->height() > m_doc->height() ) + { + p.fillRect( 0, + m_doc->height(), + viewport()->width(), + viewport()->height() - m_doc->height(), + palette().base() + ); + } +} + +/*! + \internal +*/ +void QEditor::timerEvent(QTimerEvent *e) +{ + int id = e->timerId(); + + if ( id == m_blink.timerId() ) + { + bool on = !flag(CursorOn); + + if ( m_cursor.hasSelection() ) + on &= style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, + 0, + this) != 0; + + setFlag(CursorOn, on); + + repaintCursor(); + + } else if ( id == m_drag.timerId() ) { + m_drag.stop(); + //startDrag(); + } else if ( id == m_click.timerId() ) { + m_click.stop(); + } +} + +static int max(const QList& l) +{ + int ln = 0; + + foreach ( const QDocumentCursor& c, l ) + if ( c.lineNumber() > ln ) + ln = c.lineNumber(); + + return ln; +} + +static int min(const QList& l) +{ + // beware the sign bit... + int ln = 0x7fffffff; + + foreach ( const QDocumentCursor& c, l ) + if ( (c.lineNumber() < ln) || (ln < 0) ) + ln = c.lineNumber(); + + return ln; +} + +bool QEditor::protectedCursor(const QDocumentCursor& c) const +{ + if ( c.hasSelection() ) + { + int line = qMin(c.lineNumber(), c.anchorLineNumber()), end = qMax(c.lineNumber(), c.anchorLineNumber()); + + while ( line <= end ) + { + if ( m_doc->line(line).hasAnyFlag(QDocumentLine::Hidden | QDocumentLine::CollapsedBlockStart | QDocumentLine::CollapsedBlockEnd) ) + return true; + + ++line; + } + + } else { + return m_doc->line(c.lineNumber()).hasAnyFlag(QDocumentLine::Hidden | QDocumentLine::CollapsedBlockStart | QDocumentLine::CollapsedBlockEnd); + } + + return false; +} + +/*! + \internal +*/ +void QEditor::keyPressEvent(QKeyEvent *e) +{ + foreach ( QEditorInputBindingInterface *b, m_bindings ) + if ( b->keyPressEvent(e, this) ) + return; + + forever + { + bool leave = false; + + // try mirrors bindings first + if ( (e->modifiers() & Qt::AltModifier) && (e->modifiers() & Qt::ControlModifier) ) + { + int ln = - 1; + QDocumentLine l; + + if ( e->key() == Qt::Key_Up ) + { + ln = m_cursor.lineNumber(); + + if ( m_mirrors.count() ) + ln = qMin(ln, min(m_mirrors)); + + //qDebug("first %i", ln); + + l = m_doc->line(--ln); + } else if ( e->key() == Qt::Key_Down ) { + ln = m_cursor.lineNumber(); + + if ( m_mirrors.count() ) + ln = qMax(ln, max(m_mirrors)); + + l = m_doc->line(++ln); + } + + if ( l.isValid() ) + { + addCursorMirror(QDocumentCursor(m_doc, ln, m_cursor.anchorColumnNumber())); + repaintCursor(); + emitCursorPositionChanged(); + + break; + } + } + + selectionChange(); + + // placeholders handling + bool bHandled = false; + + if ( m_placeHolders.count() && e->modifiers() == Qt::ControlModifier ) + { + if ( e->key() == Qt::Key_Up || e->key() == Qt::Key_Left ) + { + bHandled = true; + previousPlaceHolder(); + } else if ( e->key() == Qt::Key_Down || e->key() == Qt::Key_Right ) { + bHandled = true; + nextPlaceHolder(); + } + } + + // default shortcuts + if ( e->matches(QKeySequence::Undo) ) + { + undo(); + break; + } else if ( e->matches(QKeySequence::Redo) ) { + redo(); + break; + } else if ( e->matches(QKeySequence::Copy) ) { + copy(); + break; + } else if ( e->matches(QKeySequence::Paste) ) { + paste(); + break; + } else if ( e->matches(QKeySequence::Cut) ) { + cut(); + break; + } else if ( e->matches(QKeySequence::Print) ) { + print(); + break; + } else if ( e->matches(QKeySequence::SelectAll) ) { + selectAll(); + break; + } else if ( e->matches(QKeySequence::Find) ) { + find(); + break; + } else if ( e->matches(QKeySequence::FindNext) ) { + findNext(); + break; + } else if ( e->matches(QKeySequence::Replace) ) { + replace(); + break; + } + + // regular moves + if ( !bHandled ) + { + if ( moveKeyEvent(m_cursor, e, &leave) ) + { + e->accept(); + + //setFlag(CursorOn, true); + //ensureCursorVisible(); + + if ( !leave ) + for ( int i = 0; !leave && (i < m_mirrors.count()); ++i ) + moveKeyEvent(m_mirrors[i], e, &leave); + + if ( leave && m_mirrors.count() ) + { + for ( int i = 0; i < m_mirrors.count(); ++i ) + { + m_mirrors[i].setAutoUpdated(false); + } + + clearCursorMirrors(); + viewport()->update(); + } else { + repaintCursor(); + selectionChange(); + } + + bHandled = true; + } + } + + bool bOk = true; + if ( !bHandled ) + { + int offset = 0; + bool pke = isProcessingKeyEvent(e, &offset); + bool prot = protectedCursor(m_cursor); + + foreach ( const QDocumentCursor& c, m_mirrors ) + prot |= protectedCursor(c); + + if ( !pke || prot ) + { + bHandled = false; + } else { + + // clear matches to avoid offsetting and subsequent remanence of matches + if ( m_definition ) + m_definition->clearMatches(m_doc); + + bool hasPH = m_placeHolders.count() && m_curPlaceHolder != -1; + bool macroing = hasPH || m_mirrors.count(); + + if ( macroing ) + m_doc->beginMacro(); + + QStringList prevText; + + if ( hasPH ) + { + for ( int k = 0; k < m_placeHolders.count(); ++k ) + prevText << m_placeHolders.at(k).cursor.selectedText(); + + } + + bHandled = processCursor(m_cursor, e, bOk); + + if ( hasPH ) + { + PlaceHolder& ph = m_placeHolders[m_curPlaceHolder]; + + QString baseText = ph.cursor.selectedText(); + + for ( int phm = 0; phm < ph.mirrors.count(); ++phm ) + { + QString s = baseText; + + if ( ph.affector ) + ph.affector->affect(prevText, m_curPlaceHolder, e, phm, s); + + ph.mirrors[phm].replaceSelectedText(s); + } + } + + if ( m_mirrors.isEmpty() ) + { + // this signal is NOT emitted when cursor mirrors are used ON PURPOSE + // as it is the "standard" entry point for code completion, which cannot + // work properly with cursor mirrors (art least not always and not simply) + if ( bHandled ) + emit textEdited(e); + + } else { + + for ( int i = 0; bOk && (i < m_mirrors.count()); ++i ) + processCursor(m_mirrors[i], e, bOk); + + } + + if ( macroing ) + m_doc->endMacro(); + + } + } + + if ( !bHandled ) + { + QAbstractScrollArea::keyPressEvent(e); + + break; + } + + e->accept(); + emitCursorPositionChanged(); + setFlag(CursorOn, true); + ensureCursorVisible(); + repaintCursor(); + selectionChange(); + break; + } + + foreach ( QEditorInputBindingInterface *b, m_bindings ) + b->postKeyPressEvent(e, this); + +} + +/*! + \internal +*/ +void QEditor::inputMethodEvent(QInputMethodEvent* e) +{ + foreach ( QEditorInputBindingInterface *b, m_bindings ) + if ( b->inputMethodEvent(e, this) ) + return; + + /* + if ( m_doc->readOnly() ) + { + e->ignore(); + return; + } + */ + + m_cursor.beginEditBlock(); + + if ( e->commitString().count() ) + m_cursor.insertText(e->commitString()); + + m_cursor.endEditBlock(); + + foreach ( QEditorInputBindingInterface *b, m_bindings ) + b->postInputMethodEvent(e, this); + +} + +/*! + \internal +*/ +void QEditor::mouseMoveEvent(QMouseEvent *e) +{ + foreach ( QEditorInputBindingInterface *b, m_bindings ) + if ( b->mouseMoveEvent(e, this) ) + return; + + forever + { + if ( !(e->buttons() & Qt::LeftButton) ) + break; + + if ( !( flag(MousePressed) || m_doubleClick.hasSelection() ) ) + break; + + if ( flag(MaybeDrag) ) + { + m_drag.stop(); + + if ( (e->globalPos() - m_dragPoint).manhattanLength() > + QApplication::startDragDistance() + ) + startDrag(); + + //emit clearAutoCloseStack(); + break; + } + + repaintCursor(); + selectionChange(); + + const QPoint mousePos = mapToContents(e->pos()); + + if ( m_scroll.isActive() ) + { + if ( viewport()->rect().contains(e->pos()) ) + m_scroll.stop(); + } else { + if ( !viewport()->rect().contains(e->pos()) ) + m_scroll.start(100, this); + } + + QDocumentCursor newCursor = cursorForPosition(mousePos); + + if ( newCursor.isNull() ) + break; + + if ( e->modifiers() & Qt::ControlModifier ) + { + + // get column number for column selection + int org = m_cursor.anchorColumnNumber(); + int dst = newCursor.columnNumber(); + // TODO : adapt to line wrapping... + + clearCursorMirrors(); + //m_cursor.clearSelection(); + int min = qMin(m_cursor.lineNumber(), newCursor.lineNumber()); + int max = qMax(m_cursor.lineNumber(), newCursor.lineNumber()); + + if ( min != max ) + { + for ( int l = min; l <= max; ++l ) + { + if ( l != m_cursor.lineNumber() ) + addCursorMirror(QDocumentCursor(m_doc, l, org)); + + } + + if ( e->modifiers() & Qt::ShiftModifier ) + { + m_cursor.setColumnNumber(dst, QDocumentCursor::KeepAnchor); + + for ( int i = 0; i < m_mirrors.count(); ++i ) + m_mirrors[i].setColumnNumber(dst, QDocumentCursor::KeepAnchor); + } + } else { + m_cursor.setSelectionBoundary(newCursor); + } + } else { + m_cursor.setSelectionBoundary(newCursor); + //setFlag(FoldedCursor, isCollapsed()); + } + + selectionChange(true); + ensureCursorVisible(); + //emit clearAutoCloseStack(); + emitCursorPositionChanged(); + + repaintCursor(); + break; + } + + foreach ( QEditorInputBindingInterface *b, m_bindings ) + b->postMouseMoveEvent(e, this); + +} + +/*! + \internal +*/ +void QEditor::mousePressEvent(QMouseEvent *e) +{ + foreach ( QEditorInputBindingInterface *b, m_bindings ) + if ( b->mousePressEvent(e, this) ) + return; + + forever + { + if ( !(e->buttons() & Qt::LeftButton) ) + break; + + QPoint p = mapToContents(e->pos()); + + setFlag(MousePressed, true); + setFlag(MaybeDrag, false); + + repaintCursor(); + selectionChange(); + + if ( m_click.isActive() && + (( e->globalPos() - m_clickPoint).manhattanLength() < + QApplication::startDragDistance() )) + { + #if defined(Q_WS_MAC) + m_cursor.select(QDocumentCursor::LineUnderCursor); + m_doubleClick = m_cursor; + #else + m_cursor.movePosition(1, QDocumentCursor::StartOfBlock); + m_cursor.movePosition(1, QDocumentCursor::EndOfBlock, QDocumentCursor::KeepAnchor); + #endif + + m_click.stop(); + } else { + QDocumentCursor cursor = cursorForPosition(p); + + if ( cursor.isNull() ) + break; + + if ( e->modifiers() == Qt::ShiftModifier ) + { + clearCursorMirrors(); + m_cursor.setSelectionBoundary(cursor); + } else if ( e->modifiers() & Qt::ControlModifier && ((e->modifiers() & Qt::ShiftModifier) || (e->modifiers() & Qt::AltModifier)) ) { + //m_mirrors << cursor; + if ( e->modifiers() & Qt::ShiftModifier ) + { + // get column number for column selection + int org = m_cursor.anchorColumnNumber(); + int dst = cursor.columnNumber(); + // TODO : fix and adapt to line wrapping... + + clearCursorMirrors(); + //m_cursor.clearSelection(); + int min = qMin(m_cursor.lineNumber(), cursor.lineNumber()); + int max = qMax(m_cursor.lineNumber(), cursor.lineNumber()); + + if ( min != max ) + { + for ( int l = min; l <= max; ++l ) + { + if ( l != m_cursor.lineNumber() ) + addCursorMirror(QDocumentCursor(m_doc, l, org)); + + } + + if ( e->modifiers() & Qt::ShiftModifier ) + { + m_cursor.setColumnNumber(dst, QDocumentCursor::KeepAnchor); + + for ( int i = 0; i < m_mirrors.count(); ++i ) + m_mirrors[i].setColumnNumber(dst, QDocumentCursor::KeepAnchor); + } + } else { + m_cursor.setSelectionBoundary(cursor); + } + } else if ( (e->modifiers() & Qt::AltModifier) ) { + addCursorMirror(cursor); + } + } else { + + const QDocumentCursor& cur = m_cursor; + + if ( m_cursor.hasSelection() ) + { + bool inSel = cur.isWithinSelection(cursor); + + if ( !inSel ) + { + foreach ( const QDocumentCursor& m, m_mirrors ) + { + inSel = m.isWithinSelection(cursor); + + if ( inSel ) + break; + } + } + + if ( inSel ) + { + setFlag(MaybeDrag, true); + + m_dragPoint = e->globalPos(); + m_drag.start(QApplication::startDragTime(), this); + + break; + } + } + + m_doubleClick = QDocumentCursor(); + setCursor(cursor); + break; + } + } + + ensureCursorVisible(); + //emit clearAutoCloseStack(); + emitCursorPositionChanged(); + repaintCursor(); + selectionChange(); + break; + } + + foreach ( QEditorInputBindingInterface *b, m_bindings ) + b->postMousePressEvent(e, this); + +} + +/*! + \internal +*/ +void QEditor::mouseReleaseEvent(QMouseEvent *e) +{ + foreach ( QEditorInputBindingInterface *b, m_bindings ) + if ( b->mouseReleaseEvent(e, this) ) + return; + + m_scroll.stop(); + + repaintCursor(); + selectionChange(); + + if ( flag(MaybeDrag) ) + { + setFlag(MousePressed, false); + setCursorPosition(mapToContents(e->pos())); + + m_cursor.clearSelection(); + } + + if ( flag(MousePressed) ) + { + setFlag(MousePressed, false); + + setClipboardSelection(); + } else if ( e->button() == Qt::MidButton + && QApplication::clipboard()->supportsSelection()) { + setCursorPosition(mapToContents(e->pos())); + //setCursorPosition(viewport()->mapFromGlobal(e->globalPos())); + + const QMimeData *md = QApplication::clipboard() + ->mimeData(QClipboard::Selection); + + if ( md ) + insertFromMimeData(md); + } + + repaintCursor(); + + if ( m_drag.isActive() ) + m_drag.stop(); + + selectionChange(); + + foreach ( QEditorInputBindingInterface *b, m_bindings ) + b->postMouseReleaseEvent(e, this); + +} + +/*! + \internal +*/ +void QEditor::mouseDoubleClickEvent(QMouseEvent *e) +{ + foreach ( QEditorInputBindingInterface *b, m_bindings ) + if ( b->mouseDoubleClickEvent(e, this) ) + return; + + forever + { + if ( e->button() != Qt::LeftButton ) + { + e->ignore(); + break; + } + + setFlag(MaybeDrag, false); + + repaintCursor(); + selectionChange(); + clearCursorMirrors(); + setCursorPosition(mapToContents(e->pos())); + + //setFlag(FoldedCursor, isCollapsed()); + + if ( m_cursor.isValid() ) + { + m_cursor.select(QDocumentCursor::WordUnderCursor); + + setClipboardSelection(); + //emit clearAutoCloseStack(); + emitCursorPositionChanged(); + + repaintCursor(); + selectionChange(); + } else { + //qDebug("invalid cursor"); + } + + m_doubleClick = m_cursor; + + m_clickPoint = e->globalPos(); + m_click.start(qApp->doubleClickInterval(), this); + break; + } + + foreach ( QEditorInputBindingInterface *b, m_bindings ) + b->postMouseDoubleClickEvent(e, this); + +} + +/*! + \internal +*/ +void QEditor::dragEnterEvent(QDragEnterEvent *e) +{ + if ( + e + && + e->mimeData() + && + ( + e->mimeData()->hasFormat("text/plain") + || + e->mimeData()->hasFormat("text/html") + ) + && + !e->mimeData()->hasFormat("text/uri-list") + ) + e->acceptProposedAction(); + else + return; + + m_dragAndDrop = QDocumentCursor(); +} + +/*! + \internal +*/ +void QEditor::dragLeaveEvent(QDragLeaveEvent *) +{ + const QRect crect = cursorRect(m_dragAndDrop); + m_dragAndDrop = QDocumentCursor(); + + if ( crect.isValid() ) + viewport()->update(crect); + +} + +/*! + \internal +*/ +void QEditor::dragMoveEvent(QDragMoveEvent *e) +{ + if ( + e + && + e->mimeData() + && + ( + e->mimeData()->hasFormat("text/plain") + || + e->mimeData()->hasFormat("text/html") + ) + && + !e->mimeData()->hasFormat("text/uri-list") + ) + e->acceptProposedAction(); + else + return; + + QDocumentCursor c = cursorForPosition(mapToContents(e->pos())); + + if ( c.isValid() ) + { + QRect crect = cursorRect(m_dragAndDrop); + + if ( crect.isValid() ) + viewport()->update(crect); + + m_dragAndDrop = c; + + crect = cursorRect(m_dragAndDrop); + viewport()->update(crect); + } + + //e->acceptProposedAction(); +} + +/*! + \internal +*/ +void QEditor::dropEvent(QDropEvent *e) +{ + m_dragAndDrop = QDocumentCursor(); + + QDocumentCursor c(cursorForPosition(mapToContents(e->pos()))); + + if ( (e->source() == this) && (m_cursor.isWithinSelection(c)) ) + return; + + if ( + e + && + e->mimeData() + && + ( + e->mimeData()->hasFormat("text/plain") + || + e->mimeData()->hasFormat("text/html") + ) + && + !e->mimeData()->hasFormat("text/uri-list") + && + !flag(FoldedCursor) + ) + { + e->acceptProposedAction(); + } else { + return; + } + + //repaintSelection(); + + m_doc->beginMacro(); + //m_cursor.beginEditBlock(); + + if ( + (e->dropAction() == Qt::MoveAction) + && + ( + (e->source() == this) + || + (e->source() == viewport()) + ) + ) + { + m_cursor.removeSelectedText(); + + for ( int i = 0; i < m_mirrors.count(); ++i ) + m_mirrors[i].removeSelectedText(); + + } else { + //qDebug("action : %i", e->dropAction()); + m_cursor.clearSelection(); + } + + clearCursorMirrors(); + m_cursor.moveTo(cursorForPosition(mapToContents(e->pos()))); + insertFromMimeData(e->mimeData()); + //m_cursor.endEditBlock(); + + m_doc->endMacro(); + + selectionChange(); +} + +/*! + \internal +*/ +void QEditor::changeEvent(QEvent *e) +{ + QAbstractScrollArea::changeEvent(e); + + if ( + e->type() == QEvent::ApplicationFontChange + || + e->type() == QEvent::FontChange + ) + { + if ( !m_doc ) + return; + + m_doc->setFont(font()); + //setTabStop(iTab); + + } else if ( e->type() == QEvent::ActivationChange ) { + if ( !isActiveWindow() ) + m_scroll.stop(); + } +} + +/*! + \internal +*/ +void QEditor::showEvent(QShowEvent *e) +{ + QCodeEdit *ce = QCodeEdit::manager(this); + + if ( ce ) + ce->panelLayout()->update(); + + QAbstractScrollArea::showEvent(e); + + if ( flag(LineWrap) ) + { + m_doc->setWidthConstraint(wrapWidth()); + } + + if ( flag(EnsureVisible) ) + ensureCursorVisible(); + +} + +/*! + \internal + \brief Zoom in/out upon ctrl+wheel +*/ +void QEditor::wheelEvent(QWheelEvent *e) +{ + if ( e->modifiers() & Qt::ControlModifier ) + { + const int delta = e->delta(); + + if ( delta > 0 ) + zoom(-1); + else if ( delta < 0 ) + zoom(1); + + //viewport()->update(); + + return; + } + + QAbstractScrollArea::wheelEvent(e); + updateMicroFocus(); + //viewport()->update(); +} + +/*! + \internal +*/ +void QEditor::resizeEvent(QResizeEvent *) +{ + const QSize viewportSize = viewport()->size(); + + if ( flag(LineWrap) ) + { + //qDebug("resize t (2) : wrapping to %i", viewport()->width()); + + m_doc->setWidthConstraint(wrapWidth()); + } else { + horizontalScrollBar()->setMaximum(qMax(0, m_doc->width() - viewportSize.width())); + horizontalScrollBar()->setPageStep(viewportSize.width()); + } + + const int ls = m_doc->fontMetrics().lineSpacing(); + verticalScrollBar()->setMaximum(qMax(0, 1 + (m_doc->height() - viewportSize.height()) / ls)); + verticalScrollBar()->setPageStep(viewportSize.height() / ls); + + //qDebug("page step : %i", viewportSize.height() / ls); + + //if ( isCursorVisible() && flag(LineWrap) ) + // ensureCursorVisible(); +} + +/*! + \internal +*/ +void QEditor::focusInEvent(QFocusEvent *e) +{ + setFlag(CursorOn, true); + m_blink.start(QApplication::cursorFlashTime() / 2, this); + //ensureCursorVisible(); + + QAbstractScrollArea::focusInEvent(e); +} + +/*! + \internal +*/ +void QEditor::focusOutEvent(QFocusEvent *e) +{ + setFlag(CursorOn, false); + m_blink.stop(); + + QAbstractScrollArea::focusOutEvent(e); +} + +/*! + \brief Context menu event + + All the (managed) actions added to the editor are showed in it by default. +*/ +void QEditor::contextMenuEvent(QContextMenuEvent *e) +{ + foreach ( QEditorInputBindingInterface *b, m_bindings ) + if ( b->contextMenuEvent(e, this) ) + return; + + if ( !pMenu ) + { + e->ignore(); + return; + } + + selectionChange(); + + e->accept(); + + pMenu->exec(e->globalPos()); +} + +/*! + \brief Close event + + When build with qmdilib support (e.g in Edyuk) this check for + modifications and a dialog pops up to offer various options + (like saving, discarding or canceling) +*/ +void QEditor::closeEvent(QCloseEvent *e) +{ + #ifdef _QMDI_ + bool bOK = true; + + if ( isContentModified() ) + bOK = server()->maybeSave(this); + + if ( bOK ) + { + e->accept(); + notifyDeletion(); + } else { + e->ignore(); + } + #else + QAbstractScrollArea::closeEvent(e); + #endif +} + +#ifndef _QMDI_ +/*! + \return Whether the document has been modified. +*/ +bool QEditor::isContentModified() const +{ + return m_doc ? !m_doc->isClean() : false; +} +#endif + +/*! + \brief Notify that the content is clean (modifications undone or document saved) + + \note Don't mess with this. The document knows better. +*/ +void QEditor::setContentClean(bool y) +{ + setContentModified(!y); +} + +/*! + \brief Notify that the content has been modified + + \note Don't mess with this. The document knows better. +*/ +void QEditor::setContentModified(bool y) +{ + #ifdef _QMDI_ + qmdiClient::setContentModified(y); + #endif + + setWindowModified(y); + emit contentModified(y); +} + +/*! + \brief Changes the file name + + This method does not affect files on disk (no save/load/move occurs) +*/ +void QEditor::setFileName(const QString& f) +{ + QString prev = fileName(); + + if ( f == prev ) + return; + + /* + QStringList l = m_watcher->files(); + + if ( l.count() ) + m_watcher->removePaths(l); + */ + + watcher()->removeWatch(QString(), this); + + #ifdef _QMDI_ + qmdiClient::setFileName(f); + #else + m_fileName = f; + m_name = QFileInfo(f).fileName(); + #endif + + //if ( fileName().count() ) + // m_watcher->addPath(fileName()); + + if ( fileName().count() ) + watcher()->addWatch(fileName(), this); + + setTitle(name().count() ? name() : "untitled"); +} + +/*! + \brief Set the title of the widget + + Take care of adding a "[*]" prefix so that document changes are visible + on title bars. +*/ +void QEditor::setTitle(const QString& title) +{ + QString s(title); + + if ( !s.contains("[*]") ) + s.prepend("[*]"); + + setWindowTitle(s); + emit titleChanged(title); +} + +#ifndef _QMDI_ +/*! + \return The name of the file being edited (without its path) +*/ +QString QEditor::name() const +{ + return m_name; +} + +/*! + \return The full filename of the file being edited +*/ +QString QEditor::fileName() const +{ + return m_fileName; +} +#endif + +/*! + \brief Prevent tab key press to be considered as widget navigation +*/ +bool QEditor::focusNextPrevChild(bool) +{ + // make sure we catch tabs :) + + return false; +} + +/*! + \brief Start a drag and drop operation using the current selection +*/ +void QEditor::startDrag() +{ + setFlag(MousePressed, false); + QMimeData *data = createMimeDataFromSelection(); + + QDrag *drag = new QDrag(this); + drag->setMimeData(data); + + Qt::DropActions actions = Qt::CopyAction | Qt::MoveAction; + Qt::DropAction action = drag->start(actions); + + if ( (action == Qt::MoveAction) && (drag->target() != this) ) + { + m_cursor.removeSelectedText(); + + for ( int i = 0; i < m_mirrors.count(); ++i ) + m_mirrors[i].removeSelectedText(); + } +} + +/*! + \brief Handle cursor movements upon key event +*/ +bool QEditor::moveKeyEvent(QDocumentCursor& cursor, QKeyEvent *e, bool *leave) +{ + QDocumentCursor::MoveMode mode = e->modifiers() & Qt::ShiftModifier + ? QDocumentCursor::KeepAnchor + : QDocumentCursor::MoveAnchor; + + if ( flag(LineWrap) && flag(CursorJumpPastWrap) ) + mode |= QDocumentCursor::ThroughWrap; + + QDocumentCursor::MoveOperation op = QDocumentCursor::NoMove; +#ifdef Q_WS_MAC + // There can be only one modifier (+ shift), but we also need to make sure + // that we have a "move key" pressed before we reject it. + bool twoModifiers + = ((e->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) + == (Qt::ControlModifier | Qt::AltModifier)) + || ((e->modifiers() & (Qt::ControlModifier | Qt::MetaModifier)) + == (Qt::ControlModifier | Qt::MetaModifier)) + || ((e->modifiers() & (Qt::AltModifier | Qt::MetaModifier)) + == (Qt::AltModifier | Qt::MetaModifier)); +#else + if (e->modifiers() & (Qt::AltModifier | + Qt::MetaModifier | Qt::KeypadModifier) ) + { + e->ignore(); + if ( leave ) *leave = false; + return false; + } +#endif + + switch ( e->key() ) + { +#ifndef Q_WS_MAC // Use the default Windows bindings. + case Qt::Key_Up: + op = QDocumentCursor::Up; + break; + case Qt::Key_Down: + op = QDocumentCursor::Down; + /* + if (mode == QDocumentCursor::KeepAnchor) { + QTextBlock block = cursor.block(); + QTextLine line = currentTextLine(cursor); + if (!block.next().isValid() + && line.isValid() + && line.lineNumber() == block.layout()->lineCount() - 1) + op = QDocumentCursor::End; + } + */ + break; + case Qt::Key_Left: + op = e->modifiers() & Qt::ControlModifier + ? QDocumentCursor::WordLeft + : QDocumentCursor::Left; + break; + case Qt::Key_Right: + op = e->modifiers() & Qt::ControlModifier + ? QDocumentCursor::WordRight + : QDocumentCursor::Right; + break; + case Qt::Key_Home: + op = e->modifiers() & Qt::ControlModifier + ? QDocumentCursor::Start + : QDocumentCursor::StartOfLine; + break; + case Qt::Key_End: + op = e->modifiers() & Qt::ControlModifier + ? QDocumentCursor::End + : QDocumentCursor::EndOfLine; + break; +#else +/* + Except for pageup and pagedown, Mac OS X has very different behavior, we + don't do it all, but here's the breakdown: + + Shift still works as an anchor, but only one of the other keys can be dow + Ctrl (Command), Alt (Option), or Meta (Control). + + Command/Control + Left/Right -- Move to left or right of the line + + Up/Down -- Move to top bottom of the file. + (Control doesn't move the cursor) + + Option + Left/Right -- Move one word Left/right. + + Up/Down -- Begin/End of Paragraph. + + Home/End Top/Bottom of file. (usually don't move the cursor, but will select) +*/ + case Qt::Key_Up: + if (twoModifiers) { + QApplication::beep(); + if ( leave ) *leave = false; + return true; + } else { + if (e->modifiers() & (Qt::ControlModifier | Qt::MetaModifier)) + op = QDocumentCursor::Start; + else if (e->modifiers() & Qt::AltModifier) + op = QDocumentCursor::StartOfBlock; + else + op = QDocumentCursor::Up; + } + break; + case Qt::Key_Down: + if (twoModifiers) { + QApplication::beep(); + if ( leave ) *leave = false; + return true; + } else { + if (e->modifiers() & (Qt::ControlModifier | Qt::MetaModifier)) + { + op = QDocumentCursor::End; + } else if (e->modifiers() & Qt::AltModifier) { + op = QDocumentCursor::EndOfBlock; + } else { + op = QDocumentCursor::Down; + /* + if (mode == QDocumentCursor::KeepAnchor) { + QTextBlock block = cursor.block(); + QTextLine line = currentTextLine(cursor); + if (!block.next().isValid() + && line.isValid() + && line.lineNumber() == + block.layout()->lineCount() - 1) + op = QDocumentCursor::End; + } + */ + } + } + break; + case Qt::Key_Left: + if (twoModifiers) { + QApplication::beep(); + if ( leave ) *leave = false; + return true; + } else { + if (e->modifiers() & (Qt::ControlModifier | Qt::MetaModifier)) + op = QDocumentCursor::StartOfLine; + else if (e->modifiers() & Qt::AltModifier) + op = QDocumentCursor::WordLeft; + else + op = QDocumentCursor::Left; + } + break; + case Qt::Key_Right: + if ( twoModifiers ) + { + QApplication::beep(); + if ( leave ) *leave = false; + return true; + } else { + if (e->modifiers() & (Qt::ControlModifier | Qt::MetaModifier)) + op = QDocumentCursor::EndOfLine; + else if (e->modifiers() & Qt::AltModifier) + op = QDocumentCursor::WordRight; + else + op = QDocumentCursor::Right; + } + break; + case Qt::Key_Home: + if (e->modifiers() & (Qt::ControlModifier | + Qt::MetaModifier | Qt::AltModifier) ) + { + QApplication::beep(); + if ( leave ) *leave = false; + return true; + } else { + op = QDocumentCursor::Start; + } + break; + case Qt::Key_End: + if (e->modifiers() & (Qt::ControlModifier | + Qt::MetaModifier | Qt::AltModifier)) + { + QApplication::beep(); + if ( leave ) *leave = false; + return true; + } else { + op = QDocumentCursor::End; + } + break; +#endif + case Qt::Key_PageDown: + if ( leave ) *leave = true; + pageDown(mode); + return true; + + case Qt::Key_PageUp: + if ( leave ) *leave = true; + pageUp(mode); + return true; + + case Qt::Key_Insert : + if ( !e->modifiers() ) + { + if ( leave ) *leave = false; + setFlag(Overwrite, !flag(Overwrite)); + + // hack to make sure status panel gets updated... + // TODO : emit signals on flag change? + emitCursorPositionChanged(); + return false; + } + + default: + return false; + } + + int prev = cursor.lineNumber(); + + //const bool moved = + cursor.movePosition(1, op, mode); + + if ( prev != cursor.lineNumber() ) + { + if ( m_curPlaceHolder >= 0 && m_curPlaceHolder < m_placeHolders.count() ) + { + // allow mirror movement out of line while in placeholder + PlaceHolder& ph = m_placeHolders[m_curPlaceHolder]; + if ( ph.cursor.isWithinSelection(cursor) ) + return true; + for ( int i = 0; i < ph.mirrors.count(); ++i ) + if ( ph.mirrors.at(i).isWithinSelection(cursor) ) + return true; + } + //moved = true; + if ( leave ) *leave = true; + m_curPlaceHolder = -1; + } + + return true; +} + +/*! + \brief Go up by one page + + \note This method clears all cursor mirrors and suspend placeholder edition. +*/ +void QEditor::pageUp(QDocumentCursor::MoveMode moveMode) +{ + clearCursorMirrors(); + m_curPlaceHolder = -1; + + if ( m_cursor.atStart() ) + return; + + int n = viewport()->height() / QDocument::fontMetrics().lineSpacing(); + + repaintCursor(); + m_cursor.movePosition(n, QDocumentCursor::Up, moveMode); + + ensureCursorVisible(); + emitCursorPositionChanged(); + //updateMicroFocus(); +} + +/*! + \brief Go down by one page + + \note This method clears all cursor mirrors. +*/ +void QEditor::pageDown(QDocumentCursor::MoveMode moveMode) +{ + clearCursorMirrors(); + m_curPlaceHolder = -1; + + if ( m_cursor.atEnd() ) + return; + + int n = viewport()->height() / QDocument::fontMetrics().lineSpacing(); + + repaintCursor(); + m_cursor.movePosition(n, QDocumentCursor::Down, moveMode); + + ensureCursorVisible(); + emitCursorPositionChanged(); +} + +/*! + \brief Determine whether a given key event is an editing operation +*/ +bool QEditor::isProcessingKeyEvent(QKeyEvent *e, int *offset) +{ + if ( flag(FoldedCursor) ) + return false; + + switch ( e->key() ) + { + case Qt::Key_Backspace : + //--*offset; + break; + + case Qt::Key_Delete : + //--*offset; + break; + + case Qt::Key_Enter : + case Qt::Key_Return : + if ( offset ) + ++*offset; + break; + + default : + { + QString text = e->text(); + + if ( text.isEmpty() || !(text.at(0).isPrint() || (text.at(0) == '\t')) ) + return false; + + //if ( offset ) + // *offset += text.length(); + + break; + } + } + + return true; +} + +/*! + \internal + \brief Process a key event for a given cursor + + This method only take care of editing operations, not movements. +*/ +bool QEditor::processCursor(QDocumentCursor& c, QKeyEvent *e, bool& b) +{ + if ( !b ) + return false; + + bool hasSelection = c.hasSelection(); + + switch ( e->key() ) + { + case Qt::Key_Backspace : + if ( flag(FoldedCursor) ) + return false; + + if ( hasSelection ) + c.removeSelectedText(); + else + c.deletePreviousChar(); + + break; + + case Qt::Key_Delete : + if ( flag(FoldedCursor) ) + return false; + + if ( hasSelection ) + + c.removeSelectedText(); + else + c.deleteChar(); + + //emit clearAutoCloseStack(); + break; + + case Qt::Key_Enter : + case Qt::Key_Return : + { + if ( flag(FoldedCursor) ) + return false; + + c.beginEditBlock(); + + if ( hasSelection ) + c.removeSelectedText(); + else if ( flag(Overwrite) ) + c.deleteChar(); + + QString indent; + + if ( flag(AutoIndent) && (m_curPlaceHolder == -1) ) + { + if ( m_definition ) + { + indent = m_definition->indent(c); + } else { + // default : keep leading ws from previous line... + QDocumentLine l = c.line(); + const int idx = qMin(l.firstChar(), c.columnNumber()); + + indent = l.text(); + + if ( idx != -1 ) + indent.resize(idx); + + } + } + + if ( indent.count() ) + { + indent.prepend("\n"); + c.insertText(indent); + } else { + c.insertLine(); + } + + c.endEditBlock(); + + break; + } + + default : + { + QString text = e->text(); + + if ( text.isEmpty() || !(text.at(0).isPrint() || (text.at(0) == '\t')) ) + { + b = false; + return false; + } + + if ( flag(ReplaceTabs) ) + { + text.replace("\t", QString(m_doc->tabStop(), ' ')); + } + + c.beginEditBlock(); + insertText(c, text); + c.endEditBlock(); + + break; + } + } + + selectionChange(); + + return true; +} + +void QEditor::preInsert(QDocumentCursor& c, const QString& s) +{ + if ( + flag(AutoIndent) + && + (m_curPlaceHolder == -1) + && + c.columnNumber() + && + m_definition + && + m_definition->unindent(c, s) + ) + { + int firstNS = 0; + QString txt = c.line().text(); + + while ( (firstNS < txt.length()) && txt.at(firstNS).isSpace() ) + ++firstNS; + + if ( !firstNS ) + return; + + const int off = c.columnNumber() - firstNS; + + if ( off > 0 ) + c.movePosition(off, QDocumentCursor::PreviousCharacter); + + /* + It might be possible to improve that part to have a more natural/smarter unindenting + by trying to guess the scheme used by the user... + */ + + if ( txt.at(firstNS - 1) == '\t' ) + { + c.movePosition(1, QDocumentCursor::Left, QDocumentCursor::KeepAnchor); + } else { + const int ts = m_doc->tabStop(); + + do + { + --firstNS; + c.movePosition(1, QDocumentCursor::Left, QDocumentCursor::KeepAnchor); + } while ( QDocument::screenLength(txt.constData(), firstNS, ts) % ts ); + } + + c.removeSelectedText(); + + if ( off > 0 ) + c.movePosition(off, QDocumentCursor::NextCharacter); + + } +} + +/*! + \brief Insert some text at a given cursor position + + This function is provided to keep indenting/outdenting working when editing +*/ +void QEditor::insertText(QDocumentCursor& c, const QString& text) +{ + if ( protectedCursor(c) ) + return; + + bool hasSelection = c.hasSelection(); + + if ( hasSelection ) + c.removeSelectedText(); + + if ( !hasSelection && flag(Overwrite) ) + c.deleteChar(); + + QStringList lines = text.split('\n', QString::KeepEmptyParts); + + if ( (lines.count() == 1) || !flag(AdjustIndent) ) + { + preInsert(c, lines.first()); + + if ( flag(ReplaceTabs) ) + { + // TODO : replace tabs by spaces properly + } + + c.insertText(text); + } else { + + preInsert(c, lines.first()); + c.insertText(lines.takeFirst()); + + QString indent; + // FIXME ? work on strings to make sure command grouping does not interfere with cursor state... + + indent = c.line().text().left(qMax(0, qMin(c.line().firstChar(), c.columnNumber()))); + + foreach ( QString l, lines ) + { + int n = 0; + + while ( n < l.count() && l.at(n).isSpace() ) + ++n; + + l.remove(0, n); + + if ( m_definition ) + { + // FIXME ? work on strings to make sure command grouping does not interfere with cursor state... + indent = m_definition->indent(c); + + if ( flag(ReplaceTabs) ) + indent.replace("\t", QString(m_doc->tabStop(), ' ')); + } + + c.insertLine(); + c.insertText(indent); + + preInsert(c, l); + + c.insertText(l); + } + } +} + +/*! + \brief Write some text at the current cursor position + + This function is provided to make editing operations easier + from the outside and to keep them compatible with cursor + mirrors. +*/ +void QEditor::write(const QString& s) +{ + m_doc->beginMacro(); + + insertText(m_cursor, s); + + for ( int i = 0; i < m_mirrors.count(); ++i ) + insertText(m_mirrors[i], s); + + m_doc->endMacro(); + + emitCursorPositionChanged(); + setFlag(CursorOn, true); + ensureCursorVisible(); + repaintCursor(); + selectionChange(); +} + +/*! + \brief Zoom + \param n relative zoom factor + + Zooming is achieved by changing the point size of the font as follow : + + fontPointSize += \a n +*/ +void QEditor::zoom(int n) +{ + if ( !m_doc ) + return; + + QFont f = m_doc->font(); + f.setPointSize(qMax(1, f.pointSize() + n)); + m_doc->setFont(f); +} + +/*! + \brief Obtain the value of panel margins + \param l left margin + \param t top margin + \param r right margin + \param b bottom margin +*/ +void QEditor::getPanelMargins(int *l, int *t, int *r, int *b) const +{ + m_margins.getCoords(l, t, r, b); +} + +/*! + \brief Change the viewport margins to make room for panels + \param l left margin + \param t top margin + \param r right margin + \param b bottom margin +*/ +void QEditor::setPanelMargins(int l, int t, int r, int b) +{ + m_margins.setCoords(l, t, r, b); + + setViewportMargins(l, t, r, b); + + if ( flag(LineWrap) ) + { + //qDebug("panel adjust : wrapping to %i", viewport()->width()); + m_doc->setWidthConstraint(wrapWidth()); + } +} + +/*! + \deprecated + \brief Does not do anything anymore... +*/ +void QEditor::selectionChange(bool force) +{ + Q_UNUSED(force) + // TODO : repaint only selection rect + /* + if ( false )//force ) + { + //qDebug("repainting selection... [%i]", force); + viewport()->update(); + } else if ( m_cursor.hasSelection() ) { + viewport()->update(selectionRect()); + } + + m_selection = m_cursor.hasSelection(); + */ +} + +/*! + \brief Request repaint (using QWidget::update()) for the region occupied by the cursor +*/ +void QEditor::repaintCursor() +{ + if ( m_mirrors.count() ) + viewport()->update(); + + QRect r = cursorRect(); + + if ( m_crect != r ) + { + viewport()->update(m_crect.translated(horizontalOffset(), 0)); + m_crect = r; + viewport()->update(m_crect.translated(horizontalOffset(), 0)); + } else { + viewport()->update(m_crect.translated(horizontalOffset(), 0)); + } +} + +/*! + \return whether the cursor is currently visible +*/ +bool QEditor::isCursorVisible() const +{ + QPoint pos = m_cursor.documentPosition(); + + const QRect cursor(pos.x(), pos.y(), 1, QDocument::fontMetrics().lineSpacing()); + const QRect display(horizontalOffset(), verticalOffset(), viewport()->width(), viewport()->height()); + + //qDebug() << pos << " belongs to " << display << " ?"; + + return display.contains(pos); //cursor); +} + +/*! + \brief Ensure that the current cursor is visible +*/ +void QEditor::ensureCursorVisible() +{ + if ( !isVisible() ) + { + setFlag(EnsureVisible, true); + return; + } + + QPoint pos = m_cursor.documentPosition(); + + const int ls = QDocument::fontMetrics().lineSpacing(); + + int ypos = pos.y(), + yval = verticalOffset(), + ylen = viewport()->height(), + yend = ypos + ls; + + if ( ypos < yval ) + verticalScrollBar()->setValue(ypos / ls); + else if ( yend > (yval + ylen) ) + verticalScrollBar()->setValue(1 + (yend - ylen) / ls); + + int xval = horizontalOffset(), + xlen = viewport()->width(), + xpos = pos.x(); + + if ( xpos < xval ) + { + //qDebug("scroll leftward"); + horizontalScrollBar()->setValue(qMax(0, xpos - 4)); + } else if ( xpos > (xval + xlen - 4) ) { + //qDebug("scroll rightward : %i", xpos - xlen + 4); + horizontalScrollBar() + ->setValue(qMax(horizontalScrollBar()->value(), xpos - xlen + 4)); + } + + setFlag(EnsureVisible, false); +} + +/*! + \brief ensure that a given line is visible by updating scrollbars if needed +*/ +void QEditor::ensureVisible(int line) +{ + if ( !m_doc ) + return; + + const int ls = QDocument::fontMetrics().lineSpacing(); + int ypos = m_doc->y(line), + yval = verticalOffset(), + ylen = viewport()->height(), + yend = ypos + ls; + + if ( ypos < yval ) + verticalScrollBar()->setValue(ypos / ls); + else if ( yend > (yval + ylen) ) + verticalScrollBar()->setValue(1 + (yend - ylen) / ls); + +} + +/*! + \brief Ensure that a given rect is visible by updating scrollbars if needed +*/ +void QEditor::ensureVisible(const QRect &rect) +{ + if ( !m_doc ) + return; + + const int ls = QDocument::fontMetrics().lineSpacing(); + int ypos = rect.y(), + yval = verticalOffset(), + ylen = viewport()->height(), + yend = ypos + rect.height(); + + if ( ypos < yval ) + verticalScrollBar()->setValue(ypos / ls); + else if ( yend > (yval + ylen) ) + verticalScrollBar()->setValue(1 + (yend - ylen) / ls); + + //verticalScrollBar()->setValue(rect.y()); +} + +/*! + \return the rectangle occupied by the current cursor + + This will either return a cursorRect for the current cursor or + the selectionRect() if the cursor has a selection. + + The cursor position, which would be the top left corner of the actual + rectangle occupied by the cursor can be obtained using QDocumentCursor::documentPosition() + + The behavior of this method may surprise newcomers but it is actually quite sensible + as this rectangle is mainly used to specify the update rect of the widget and the whole + line needs to be updated to properly update the line background whenever the cursor move + from a line to another. +*/ +QRect QEditor::cursorRect() const +{ + return m_cursor.hasSelection() ? selectionRect() : cursorRect(m_cursor); +} + +/*! + \return the rectangle occupied by the selection in viewport coordinates + + If the current cursor does not have a selection, its cursorRect() is returned. + + The returned rectangle will always be bigger than the actual selection has + it is actually the union of all the rectangles occupied by all lines the selection + spans over. +*/ +QRect QEditor::selectionRect() const +{ + if ( !m_cursor.hasSelection() ) + return cursorRect(m_cursor); + + QDocumentSelection s = m_cursor.selection(); + + if ( s.startLine == s.endLine ) + return cursorRect(m_cursor); + + int y = m_doc->y(s.startLine); + QRect r = m_doc->lineRect(s.endLine); + int height = r.y() + r.height() - y; + + r = QRect(0, y, viewport()->width(), height); + r.translate(-horizontalOffset(), -verticalOffset()); + return r; +} + +/*! + \return the rectangle occupied by the given line, in viewport coordinates + + The width of the returned rectangle will always be the viewport width. +*/ +QRect QEditor::lineRect(int line) const +{ + if ( !m_doc ) + return QRect(); + + QRect r = m_doc->lineRect(line); + r.setWidth(viewport()->width()); + r.translate(-horizontalOffset(), -verticalOffset()); + + return r; +} + +/*! + \overload + + \note This function relies on QDocumentLine::lineNumber() so avoid + it whenever possible as it is much slower than providing a line number + directly. +*/ +QRect QEditor::lineRect(const QDocumentLine& l) const +{ + //qFatal("bad practice..."); + + if ( !m_doc ) + return QRect(); + + QRect r = m_doc->lineRect(l); + r.setWidth(viewport()->width()); + r.translate(-horizontalOffset(), -verticalOffset()); + + return r; +} + +/*! + \return The line rect of the given cursor +*/ +QRect QEditor::cursorRect(const QDocumentCursor& c) const +{ + return lineRect(c.lineNumber()); +} + +/*! + \brief creates a valid QMimeData object depending on the selection +*/ +QMimeData* QEditor::createMimeDataFromSelection() const +{ + QMimeData *d = new QMimeData; + + if ( !m_cursor.hasSelection() ) + { + qWarning("Generated empty MIME data"); + return d; + } + + if ( m_mirrors.isEmpty() ) + { + d->setText(m_cursor.selectedText()); + } else { + QString serialized = m_cursor.selectedText(); + + foreach ( const QDocumentCursor& m, m_mirrors ) + { + serialized += '\n'; + serialized += m.selectedText(); + } + + d->setText(serialized); + d->setData("text/column-selection", serialized.toLocal8Bit()); + } + + //qDebug("generated selection from : \"%s\"", qPrintable(d->text())); + + return d; +} + +static void fixLineEndings(QString& text) +{ + // deal with Dos first to avoid line endings duplication + text.replace("\r\n", "\n"); + + text.replace('\r', '\n'); +} + +/*! + \brief Inserts the content of a QMimeData object at the cursor position + + \note Only plain text is supported... \see QMimeData::hasText() +*/ +void QEditor::insertFromMimeData(const QMimeData *d) +{ + bool s = m_cursor.hasSelection(); + + if ( !d ) + return; + + if ( d->hasFormat("text/uri-list") ) + { + + return; + } + + if ( m_cursor.isValid() ) + { + if ( d->hasFormat("text/column-selection") ) + { + clearCursorMirrors(); + + QStringList columns = QString::fromLocal8Bit( + d->data("text/column-selection") + ).split('\n'); + + m_doc->beginMacro(); + + if ( s ) + m_cursor.removeSelectedText(); + + int col = m_cursor.columnNumber(); + //m_cursor.insertText(columns.takeFirst()); + insertText(m_cursor, columns.takeFirst()); + QDocumentCursor c = m_cursor; + + while ( columns.count() ) + { + // check for end of doc and add line if needed... + c.setColumnNumber(c.line().length()); + + if ( c.atEnd() ) + c.insertText("\n"); + else + c.movePosition(1, QDocumentCursor::NextCharacter); + + // align + c.setColumnNumber(qMin(col, c.line().length())); + + // copy content of clipboard + //c.insertText(columns.takeFirst()); + insertText(c, columns.takeFirst()); + addCursorMirror(c); + } + + m_doc->endMacro(); + + } else { + m_doc->beginMacro(); + + //if ( s ) + //{ + // m_cursor.removeSelectedText(); + //} + + QString txt; + + if ( d->hasFormat("text/plain") ) + txt = d->text(); + else if ( d->hasFormat("text/html") ) + txt = d->html(); + + fixLineEndings(txt); + + insertText(m_cursor, txt); + + for ( int i = 0; i < m_mirrors.count(); ++i ) + { + insertText(m_mirrors[i], txt); + } + + m_doc->endMacro(); + } + + ensureCursorVisible(); + setFlag(CursorOn, true); + + emitCursorPositionChanged(); + } +} + +/*! + \brief Removes all cursor mirrors +*/ +void QEditor::clearCursorMirrors() +{ + if ( m_mirrors.isEmpty() ) + return; + + m_curPlaceHolder = -1; + repaintCursor(); + + for ( int i = 0; i < m_mirrors.count(); ++i ) + { + m_mirrors[i].setAutoUpdated(false); + } + + m_mirrors.clear(); + + viewport()->update(); +} + +/*! + \brief Add a cursor mirror +*/ +void QEditor::addCursorMirror(const QDocumentCursor& c) +{ + if ( c.isNull() || (c == m_cursor) || m_mirrors.contains(c) ) + return; + + m_mirrors << c; + + // necessary for smooth mirroring + m_mirrors.last().setSilent(true); + m_mirrors.last().setAutoUpdated(true); +} + +/*! + \internal + \brief Copy the selection to the clipboard +*/ +void QEditor::setClipboardSelection() +{ + QClipboard *clipboard = QApplication::clipboard(); + + if ( !clipboard->supportsSelection() || !m_cursor.hasSelection() ) + return; + + QMimeData *data = createMimeDataFromSelection(); + + clipboard->setMimeData(data, QClipboard::Selection); +} + +/*! + \internal + \brief Scroll contents + + Refer to QAbstractScrollArea doc for more info. +*/ +void QEditor::scrollContentsBy(int dx, int dy) +{ + #ifdef Q_GL_EDITOR + viewport()->update(); + #else + const int ls = m_doc->fontMetrics().lineSpacing(); + viewport()->scroll(dx, dy * ls); + #endif +} + +/*! + \internal + \brief Workaround inconsistent width determination of viewport width + accross platfroms when scrollbars are visible... +*/ +int QEditor::wrapWidth() const +{ + #ifdef Q_WS_WIN + //if ( verticalScrollBar()->isVisible() ) + // return viewport()->width() - verticalScrollBar()->width(); + #endif + return viewport()->width(); +} + +/*! + \internal + \brief Slot called whenever document width changes + + Horizontal scrollbar is updated here. + + \note ensureCursorVisible() is NOT called. +*/ +void QEditor::documentWidthChanged(int newWidth) +{ + if ( flag(LineWrap) ) + { + horizontalScrollBar()->setMaximum(0); + return; + } + + int nv = qMax(0, newWidth - wrapWidth()); + + horizontalScrollBar()->setMaximum(nv); + + //ensureCursorVisible(); +} + +/*! + \internal + \brief Slot called whenever document height changes + + Vertical scrollbar is updated here (maximum is changed + and value is modified if needed to ensure that the cursor is visible) +*/ +void QEditor::documentHeightChanged(int newHeight) +{ + if ( flag(LineWrap) ) + { + m_doc->setWidthConstraint(wrapWidth()); + } + const int ls = m_doc->fontMetrics().lineSpacing(); + verticalScrollBar()->setMaximum(qMax(0, 1 + (newHeight - viewport()->height()) / ls)); + //ensureCursorVisible(); +} + +/*! + \internal + \brief Request paint event upon modification + \param i first modified line + \param n number of modified lines +*/ +void QEditor::repaintContent(int i, int n) +{ + if ( !m_doc ) + return; + + #ifdef Q_GL_EDITOR + viewport()->update(); + #else + if ( n <= 0 ) + { + viewport()->update(); + } + + QRect frect = m_doc->lineRect(i); + + const int yoff = verticalOffset() + viewport()->height(); + + if ( frect.y() > yoff ) + return; + + if ( n == 1 ) + { + frect.translate(0, -verticalOffset()); + //qDebug() << frect; + viewport()->update(frect); + return; + } + + QRect lrect = m_doc->lineRect(i + n - 1); + + if ( (n > 0) && (lrect.y() + lrect.height()) < verticalOffset() ) + return; + + //qDebug("repainting %i lines starting from %ith one", n, i); + + //rect.setWidth(viewport()->width()); + //rect.setHeight(qMin(viewport()->height(), rect.height() * n)); + + const int paintOffset = frect.y() - verticalOffset(); + const int paintHeight = lrect.y() + lrect.height() - frect.y(); + const int maxPaintHeight = viewport()->height() - paintOffset; + + QRect rect = QRect( + frect.x(), + paintOffset, + viewport()->width(), + (n <= 0) + ? + maxPaintHeight + : + qMin(maxPaintHeight, paintHeight) + ); + + //qDebug() << rect; + + viewport()->update(rect); + #endif +} + +/*! + \internal + \brief Update function called upon editing action + \param i First modified line + \param n Number of modified lines + + If more than one line has been modified this function + causes a repaint from the first visible line to the end + of the viewport due to the way QAbstractScrollArea + handles scrolling. + + \note This function used to update formatting but + the highlighting has been moved to QDocument recently +*/ +void QEditor::updateContent (int i, int n) +{ + if ( !m_doc ) + return; + + //qDebug("updating %i, %i", i, n); + + bool cont = n > 1; + + repaintContent(i, cont ? -1 : n); +} + +/*! + \internal +*/ +void QEditor::markChanged(QDocumentLineHandle *l, int mark, bool on) +{ + emit markChanged(fileName(), l, mark, on); +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qeditor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qeditor.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,471 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QEDITOR_H_ +#define _QEDITOR_H_ + +#include "qce-config.h" + +/*! + \file qeditor.h + \brief Definition of the QEditor class +*/ + +#include +#include +#include +#include +#include +#include + +#include "qdocument.h" +#include "qdocumentcursor.h" + +#ifdef _QMDI_ + #include "qmdiclient.h" +#endif + +class QMenu; +class QAction; +class QMimeData; +class QTextCodec; +class QActionGroup; + +class QReliableFileWatch; + +class QDocumentLineHandle; + +class QLanguageDefinition; +class QCodeCompletionEngine; + +class QEditorInputBindingInterface; + +class QCE_EXPORT QEditor : public QAbstractScrollArea +#ifdef _QMDI_ +, public qmdiClient +#endif +{ + friend class QEditConfig; + friend class QEditorFactory; + + Q_OBJECT + + public: + enum CodecUpdatePolicy + { + NoUpdate = 0, + UpdateOld = 1, + UpdateDefault = 2, + UpdateCustom = 4, + + UpdateAll = 7 + }; + + enum EditFlag + { + None = 0, + + Overwrite = 0x001, + CursorOn = 0x002, + ReadOnly = 0x004, + MousePressed = 0x008, + MaybeDrag = 0x010, + Selection = 0x020, + EnsureVisible = 0x040, + + FoldedCursor = 0x100, + + Internal = 0x00000fff, + + LineWrap = 0x00001000, + + CtrlNavigation = 0x00010000, + CursorJumpPastWrap = 0x00020000, + + ReplaceTabs = 0x00100000, + RemoveTrailing = 0x00200000, + PreserveTrailingIndent = 0x00400000, + AdjustIndent = 0x00800000, + + AutoCloseChars = 0x01000000, + AutoIndent = 0x02000000, + + Accessible = 0xfffff000 + }; + + Q_DECLARE_FLAGS(State, EditFlag) + + struct PlaceHolder + { + class Affector + { + public: + virtual ~Affector() {} + virtual void affect(const QStringList& base, int ph, const QKeyEvent *e, int mirror, QString& after) const = 0; + }; + + PlaceHolder() : length(0), autoRemove(false), affector(0) {} + PlaceHolder(const PlaceHolder& ph) : length(ph.length), autoRemove(ph.autoRemove), affector(ph.affector) + { + cursor = ph.cursor; + mirrors << ph.mirrors; + } + + int length; + bool autoRemove; + Affector *affector; + QDocumentCursor cursor; + QList mirrors; + }; + + QEditor(QWidget *p = 0); + QEditor(bool actions, QWidget *p = 0); + QEditor(const QString& s, QWidget *p = 0); + QEditor(const QString& s, bool actions, QWidget *p = 0); + virtual ~QEditor(); + + bool flag(EditFlag) const; + + bool canUndo() const; + bool canRedo() const; + + QString text() const; + QString text(int line) const; + + QTextCodec* codec() const; + QDocument* document() const; + + QList inputBindings() const; + + bool isCursorVisible() const; + QDocumentCursor cursor() const; + + int cursorMirrorCount() const; + QDocumentCursor cursorMirror(int i) const; + + QLanguageDefinition* languageDefinition() const; + QCodeCompletionEngine* completionEngine() const; + + QAction* action(const QString& s); + + virtual QRect cursorRect() const; + virtual QRect selectionRect() const; + virtual QRect lineRect(int line) const; + virtual QRect lineRect(const QDocumentLine& l) const; + virtual QRect cursorRect(const QDocumentCursor& c) const; + + #ifndef _QMDI_ + QString name() const; + QString fileName() const; + + bool isContentModified() const; + #endif + + bool isInConflict() const; + + int wrapWidth() const; + + inline int horizontalOffset() const + { return horizontalScrollBar()->isVisible() ? horizontalScrollBar()->value() : 0; } + inline int verticalOffset() const + { return verticalScrollBar()->isVisible() ? verticalScrollBar()->value() * m_doc->fontMetrics().lineSpacing() : 0; } + + inline QPoint mapToContents(const QPoint &point) const + { + return QPoint( point.x() + horizontalOffset(), + point.y() + verticalOffset() ); + } + + inline QPoint mapFromContents(const QPoint &point) const + { + return QPoint( point.x() - horizontalOffset(), + point.y() - verticalOffset() ); + } + + virtual bool protectedCursor(const QDocumentCursor& c) const; + + static int defaultFlags(); + static void setDefaultFlags(int f); + + static QTextCodec* defaultCodec(); + static void setDefaultCodec(int mib, int update); + static void setDefaultCodec(QTextCodec *c, int update); + static void setDefaultCodec(const char *name, int update); + static void setDefaultCodec(const QByteArray& name, int update); + + static QEditorInputBindingInterface* registeredInputBinding(const QString& n); + static QString defaultInputBindingId(); + static QStringList registeredInputBindingIds(); + static void registerInputBinding(QEditorInputBindingInterface *b); + static void unregisterInputBinding(QEditorInputBindingInterface *b); + static void setDefaultInputBinding(QEditorInputBindingInterface *b); + static void setDefaultInputBinding(const QString& b); + + static inline const QList& editors() { return m_editors; } + + public slots: + void undo(); + void redo(); + + void cut(); + void copy(); + void paste(); + + void selectAll(); + + void find(); + void findNext(); + void replace(); + + void gotoLine(); + + void indentSelection(); + void unindentSelection(); + + void commentSelection(); + void uncommentSelection(); + + void setLineWrapping(bool on); + + virtual void save(); + void save(const QString& filename); + + virtual void print(); + + virtual void retranslate(); + + virtual void write(const QString& s); + + void addAction(QAction *a, const QString& menu, const QString& toolbar = QString()); + void removeAction(QAction *a, const QString& menu, const QString& toolbar = QString()); + + void load(const QString& file); + void setText(const QString& s); + + void setCodec(int mib); + void setCodec(QTextCodec *c); + void setCodec(const char *name); + void setCodec(const QByteArray& name); + + void setDocument(QDocument *d); + + void addInputBinding(QEditorInputBindingInterface *b); + void removeInputBinding(QEditorInputBindingInterface *b); + void setInputBinding(QEditorInputBindingInterface *b); + + void setCursor(const QDocumentCursor& c); + + void setLanguageDefinition(QLanguageDefinition *d); + + void setCompletionEngine(QCodeCompletionEngine *e); + + void zoom(int n); + + void setPanelMargins(int l, int t, int r, int b); + void getPanelMargins(int *l, int *t, int *r, int *b) const; + + void setTitle(const QString& title); + + void highlight(); + + void clearPlaceHolders(); + void removePlaceHolder(int i); + void addPlaceHolder(const PlaceHolder& p, bool autoUpdate = true); + + int placeHolderCount() const; + int currentPlaceHolder() const; + + void nextPlaceHolder(); + void previousPlaceHolder(); + void setPlaceHolder(int i); + + virtual void setFileName(const QString& f); + + signals: + void loaded(QEditor *e, const QString& s); + void saved(QEditor *e, const QString& s); + + void contentModified(bool y); + void titleChanged(const QString& title); + + void textEdited(QKeyEvent *e); + void cursorPositionChanged(); + + void copyAvailable(bool y); + + void undoAvailable(bool y); + void redoAvailable(bool y); + + void markChanged(const QString& f, QDocumentLineHandle *l, int mark, bool on); + + public slots: + void checkClipboard(); + void reconnectWatcher(); + void fileChanged(const QString& f); + + void setContentClean(bool y); + + void emitCursorPositionChanged(); + + virtual void setContentModified(bool y); + + protected: + virtual bool event(QEvent *e); + + virtual void paintEvent(QPaintEvent *e); + virtual void timerEvent(QTimerEvent *e); + + virtual void keyPressEvent(QKeyEvent *e); + + virtual void inputMethodEvent(QInputMethodEvent* e); + + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + + virtual void dragEnterEvent(QDragEnterEvent *e); + virtual void dragLeaveEvent(QDragLeaveEvent *e); + virtual void dragMoveEvent(QDragMoveEvent *e); + virtual void dropEvent(QDropEvent *e); + + virtual void changeEvent(QEvent *e); + virtual void showEvent(QShowEvent *); + virtual void wheelEvent(QWheelEvent *e); + virtual void resizeEvent(QResizeEvent *e); + virtual void focusInEvent(QFocusEvent *e); + virtual void focusOutEvent(QFocusEvent *e); + + virtual void contextMenuEvent(QContextMenuEvent *e); + + virtual void closeEvent(QCloseEvent *e); + + virtual bool focusNextPrevChild(bool next); + + virtual bool moveKeyEvent(QDocumentCursor& c, QKeyEvent *e, bool *leave); + virtual bool isProcessingKeyEvent(QKeyEvent *e, int *offset = 0); + virtual bool processCursor(QDocumentCursor& c, QKeyEvent *e, bool& b); + + virtual void startDrag(); + virtual QMimeData* createMimeDataFromSelection() const; + virtual void insertFromMimeData(const QMimeData *d); + + virtual void scrollContentsBy(int dx, int dy); + + // got to make it public for bindings + public: + void setFlag(EditFlag f, bool b); + + void pageUp(QDocumentCursor::MoveMode moveMode); + void pageDown(QDocumentCursor::MoveMode moveMode); + + void selectionChange(bool force = false); + + void repaintCursor(); + void ensureCursorVisible(); + void ensureVisible(int line); + void ensureVisible(const QRect &rect); + + void preInsert(QDocumentCursor& c, const QString& text); + void insertText(QDocumentCursor& c, const QString& text); + + QDocumentLine lineAtPosition(const QPoint& p) const; + QDocumentCursor cursorForPosition(const QPoint& p) const; + + void setClipboardSelection(); + void setCursorPosition(const QPoint& p); + + void setCursorPosition(int line, int index); + void getCursorPosition(int &line, int &index); + + void clearCursorMirrors(); + void addCursorMirror(const QDocumentCursor& c); + + protected slots: + void documentWidthChanged(int newWidth); + void documentHeightChanged(int newWidth); + + void repaintContent(int i, int n); + void updateContent (int i, int n); + + void markChanged(QDocumentLineHandle *l, int mark, bool on); + + void bindingSelected(QAction *a); + + void lineEndingSelected(QAction *a); + void lineEndingChanged(int lineEnding); + + protected: + enum SaveState + { + Undefined, + Saving, + Saved, + Conflict + }; + + void init(bool actions = true); + void updateBindingsMenu(); + + #ifndef _QMDI_ + QString m_name, m_fileName; + #endif + + QMenu *pMenu; + QHash m_actions; + + QMenu *m_lineEndingsMenu; + QActionGroup *m_lineEndingsActions; + + QMenu *m_bindingsMenu; + QAction *aDefaultBinding; + QActionGroup *m_bindingsActions; + + char m_saveState; + quint16 m_checksum; + + QDocument *m_doc; + QTextCodec *m_codec; + QList m_bindings; + + QLanguageDefinition *m_definition; + QPointer m_completionEngine; + + QDocumentCursor m_cursor, m_doubleClick, m_dragAndDrop; + + QList m_mirrors; + + int m_curPlaceHolder, m_cphOffset; + QList m_placeHolders; + + int m_state; + bool m_selection; + QRect m_crect, m_margins; + QPoint m_clickPoint, m_dragPoint; + QBasicTimer m_blink, m_scroll, m_click, m_drag; + + static QReliableFileWatch* watcher(); + + static int m_defaultFlags; + static QTextCodec *m_defaultCodec; + + static QList m_editors; + static QEditorInputBindingInterface *m_defaultBinding; + static QHash m_registeredBindings; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QEditor::State); + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qeditorfactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qeditorfactory.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,341 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qeditorfactory.h" + +#ifdef _QSAFE_SHARED_SETTINGS_ + +/*! + \file qeditorfactory.cpp + \brief Implementation of the QEditorFactory class. +*/ + +#include "qcodeedit.h" + +#include "qformatscheme.h" +#include "qlanguagefactory.h" +#include "qcodecompletionengine.h" + +#include "qfoldpanel.h" +#include "qlinemarkpanel.h" +#include "qlinenumberpanel.h" +#include "qlinechangepanel.h" +#include "qgotolinepanel.h" +#include "qstatuspanel.h" +#include "qsearchreplacepanel.h" + +#include "qeditor.h" +#include "qlinemarksinfocenter.h" + +#include "qsettingsserver.h" + +#include +#include +#include +#include +#include + +/*! + \ingroup editor + @{ + + \class QEditorFactory + \brief Convenience class that manages editors. + + QCodeEdit widgets are created through QEditorFactory using a simple + QString representing a panel id. Each panel id is associated to a + serialized panel layout (\see QPanelLayout::serialized() ). + + \see QCodeEdit + \see QLanguageFactory + \see QFormatScheme +*/ + +/*! + \brief Construct a working editor factory +*/ +QEditorFactory::QEditorFactory(QSettingsServer *s) + : +#ifdef _QMDI_ + qmdiClientFactory(s), +#else + QObject(s), +#endif + QSettingsClient(s, "editor") +{ + m_defaultScheme = new QFormatScheme(QCE::fetchDataFile("formats.qxf"), this); + + QDocument::setFormatFactory(m_defaultScheme); + + m_languageFactory = new QLanguageFactory(m_defaultScheme, this); + + foreach ( QString dp, QCE::dataPathes() ) + { + m_languageFactory->addDefinitionPath(dp); + } + + if ( value("version").toInt() != 3 ) + { + setValue("version", 3); + + // setup default layouts... + beginGroup("layouts"); + + setValue("default", "default"); + + beginGroup("availables"); + + beginGroup("empty"); + setValue("struct", QString()); + setValue("name", "No panels"); + endGroup(); + + beginGroup("default"); + setValue("struct", + QString::number(QCodeEdit::West) + + "{" + + Q_PANEL_ID(QLineMarkPanel) + + "," + + Q_PANEL_ID(QLineNumberPanel) + + "," + + Q_PANEL_ID(QFoldPanel) + + "," + + Q_PANEL_ID(QLineChangePanel) + + "}" + + + QString::number(QCodeEdit::South) + + "{" + + Q_PANEL_ID(QStatusPanel) + + "," + + Q_PANEL_ID(QGotoLinePanel) + + "," + + Q_PANEL_ID(QSearchReplacePanel) + + "}" + ); + + setValue("name", "Default panel layout"); + endGroup(); + + beginGroup("simple"); + setValue("struct", + QString::number(QCodeEdit::West) + + "{" + + Q_PANEL_ID(QLineNumberPanel) + + "," + + Q_PANEL_ID(QFoldPanel) + + "}" + + + QString::number(QCodeEdit::South) + + "{" + + Q_PANEL_ID(QStatusPanel) + + "}" + ); + + setValue("name", "Trimmed-down panel layout"); + endGroup(); + + endGroup(); + + endGroup(); + } + +} + +/*! + \brief dtor +*/ +QEditorFactory::~QEditorFactory() +{ + +} + +/*! + \reimp + \brief Creates an editor with default layout for the given file + \param filename file to load in the editor + \return a managed QEditor object +*/ +qmdiClient* QEditorFactory::createClient(const QString& filename) const +{ + return editor(filename, defaultLayout())->editor(); +} + +/*! + \brief Create a managed editor + \param f file to load + \param layout panel layout to use +*/ +QCodeEdit* QEditorFactory::editor(const QString& f, const QString& layout) const +{ + QCodeEdit *e = new QCodeEdit(layout.isEmpty() ? defaultLayout() : layout); + + //m_config->hookEditor(def, e); + + connect(e->editor() , SIGNAL( loaded(QEditor*, QString) ), + this , SLOT ( loaded(QEditor*, QString) ) ); + + connect(e->editor() , SIGNAL( saved(QEditor*, QString) ), + this , SLOT ( saved(QEditor*, QString) ) ); + + // set syntax handlers + m_languageFactory->setLanguage(e->editor(), f); + + if ( f.count() && QFile::exists(f) ) + { + // load contents + e->editor()->load(f); + + // set line marks back... + QLineMarksInfoCenter::instance()->flush(f); + } else { + e->editor()->setTitle(tr("untitled")); + e->editor()->setContentModified(true); + } + + return e; +} + +/*! + \overload + \param f file to load + \param l language definition to use + \param s format scheme to use + \param en code completion engine to use + \param layout panel layout to use +*/ +QCodeEdit* QEditorFactory::editor( const QString& f, + QLanguageDefinition *l, + QFormatScheme *s, + QCodeCompletionEngine *en, + const QString& layout) const +{ + QCodeEdit *e = new QCodeEdit(layout.isEmpty() ? defaultLayout() : layout); + + //m_config->hookEditor(def, e); + + connect(e->editor() , SIGNAL( loaded(QEditor*, QString) ), + this , SLOT ( loaded(QEditor*, QString) ) ); + + connect(e->editor() , SIGNAL( saved(QEditor*, QString) ), + this , SLOT ( saved(QEditor*, QString) ) ); + + // set syntax handlers + //m_languageFactory->setLanguage(e->editor(), l, en); + e->editor()->setLanguageDefinition(l); + e->editor()->document()->setFormatScheme(s ? s : m_defaultScheme); + e->editor()->setCompletionEngine(en ? en->clone() : 0); + + if ( f.count() && QFile::exists(f) ) + { + // load contents + e->editor()->load(f); + + // set line marks back... + QLineMarksInfoCenter::instance()->flush(f); + } else { + e->editor()->setTitle(tr("untitled")); + e->editor()->setContentModified(true); + } + + return e; +} + +/*! + \brief Called whenever a managed editor save its content + + Update the language definition/code completion engine if needed + and emits the fileSaved signal +*/ +void QEditorFactory::saved(QEditor *e, const QString& f) +{ + if ( !e || !e->document() ) + return; + + m_languageFactory->setLanguage(e, f); + emit fileSaved(f); +} + +/*! + \brief Placeholder +*/ +void QEditorFactory::loaded(QEditor *e, const QString& f) +{ + Q_UNUSED(f) + + if ( !e || !e->document() ) + return; + +} + +/*! + \return The default panel layout +*/ +QString QEditorFactory::defaultLayout() const +{ + QSettingsClient c(*this); + + c.beginGroup("layouts"); + QString a = c.value("default").toString(); + + if ( a.isEmpty() ) + { + c.beginGroup("availables"); + + a = childGroups().at(0); + + c.endGroup(); + } + + c.endGroup(); + + return layout(a); +} + +/*! + \return The layout structure associated with a given layout alias +*/ +QString QEditorFactory::layout(const QString& alias) const +{ + return value("layouts/availables/" + alias + "/struct").toString(); +} + +/*! + \brief Register a layout + \param a layout alias + \param layout layout structure +*/ +void QEditorFactory::registerLayout(const QString& a, const QString& layout) +{ + beginGroup("layouts"); + beginGroup("availables"); + beginGroup(a); + setValue("struct", layout); + setValue("name", a); + endGroup(); + endGroup(); + endGroup(); +} + +/*! + \return an accessor to the settings associated with a given layout alias +*/ +QSettingsClient QEditorFactory::settings(const QString& alias) +{ + return QSettingsClient(*this, "layouts/availables/" + alias); +} + +/*! @} */ + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qeditorfactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qeditorfactory.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QEDITOR_FACTORY_H_ +#define _QEDITOR_FACTORY_H_ + +#include "qce-config.h" + +/*! + \file qeditorfactory.h + \brief Definition of the QEditorFactory class +*/ + +#ifdef _QSAFE_SHARED_SETTINGS_ + +#ifndef _QMDI_ + #define Q_EDITOR_FACTORY_BASE QObject + #define Q_EDITOR_FACTORY_EMIT(client) +#else + #include "qmdiclientfactory.h" + + #define Q_EDITOR_FACTORY_BASE qmdiClientFactory + #define Q_EDITOR_FACTORY_EMIT(client) emit clientCreated(client); +#endif + +#include "qsettingsclient.h" + +#include + +class QEditor; +class QCodeEdit; +class QFormatScheme; +class QLanguageFactory; +class QCodeCompletionEngine; +class QLanguageDefinition; +class QEditorConfiguration; + +class QCE_EXPORT QEditorFactory : public Q_EDITOR_FACTORY_BASE, public QSettingsClient +{ + Q_OBJECT + + public: + QEditorFactory(QSettingsServer *s); + virtual ~QEditorFactory(); + + inline QFormatScheme* defaultFormatScheme() const + { return m_defaultScheme; } + + inline QLanguageFactory* languageFactory() const + { return m_languageFactory; } + + virtual qmdiClient* createClient(const QString& filename) const; + + QCodeEdit* editor( const QString& file, + const QString& layout = QString()) const; + + QCodeEdit* editor( const QString& file, + QLanguageDefinition *l, + QFormatScheme *s = 0, + QCodeCompletionEngine *e = 0, + const QString& layout = QString()) const; + + QString defaultLayout() const; + QString layout(const QString& alias) const; + void registerLayout(const QString& alias, const QString& layout); + + QSettingsClient settings(const QString& alias); + + signals: + void fileSaved(const QString& f); + + private slots: + void saved(QEditor *e, const QString& f); + void loaded(QEditor *e, const QString& f); + + private: + QEditorConfiguration *m_config; + + QFormatScheme *m_defaultScheme; + QLanguageFactory *m_languageFactory; +}; + +#endif // _QSAFE_SHARED_SETTINGS_ + +#endif // ! _QEDITOR_FACTORY_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qeditorinputbinding.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qeditorinputbinding.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qeditorinputbinding.h" + +#include "qeditor.h" +#include "qdocument.h" +#include "qdocumentcursor.h" + +#include + +/*! + \class QEditorInputBindingInterface + \brief A class designed to allow extending user input in a transparent way + + An input binding interface, when set to an editor, can intercept all the events the + editor receive and radically change the behavior. + + The main purpose of this class is twofold : +
    +
  • Allow vi-like (or emacs-like, ...) editing to be implemented with little extra work. + And allow the user to easily switch between input modes
  • +
  • Allow applications using QCE to easily add extra features (e.g extended code + navigation within projects, jump to documentation, ...) with little extra work
  • +
+*/ + +/*! + \class QEditorInputBinding + \brief A "managed" input binding interface + + This subclass of QEditorInputBindingInterface is meant to make the creatio of input + bindings easier and more intuitive by abstracting away most of the low-level event + handling logic. +*/ + +///////////////////////////////////////////////////////////////////////////// + +QEditorInputBinding::MotionCommand::MotionCommand(QDocumentCursor::MoveOperation op, QDocumentCursor::MoveMode m, int n) + : count(n), mode(m), operation(op) +{ + +} + +void QEditorInputBinding::MotionCommand::exec(QEditor *e) +{ + QDocumentCursor c = e->cursor(); + c.movePosition(count, operation, mode); + e->setCursor(c); +} + +QEditorInputBinding::EditCommand::EditCommand(Operation op) + : operation(op) +{ + +} + +void QEditorInputBinding::EditCommand::exec(QEditor *e) +{ + QDocumentCursor c = e->cursor(); + + switch ( operation ) + { + case ClearSelection : + c.clearSelection(); + break; + + case SelectWord : + c.select(QDocumentCursor::WordUnderCursor); + break; + + case SelectLine : + c.select(QDocumentCursor::LineUnderCursor); + break; + + case SelectDocument : + c.movePosition(1, QDocumentCursor::Start, QDocumentCursor::MoveAnchor); + c.movePosition(1, QDocumentCursor::End, QDocumentCursor::KeepAnchor); + break; + + case DeleteChar : + c.deleteChar(); + break; + + case DeletePreviousChar : + c.deletePreviousChar(); + break; + + case DeleteLine : + c.eraseLine(); + break; + + case DeleteSelection : + c.removeSelectedText(); + break; + + case InsertLine : + c.insertLine(); + break; + + case InsertClipBoard : + e->paste(); + return; + + default: + + break; + } + + e->setCursor(c); +} + +QEditorInputBinding::WriteCommand::WriteCommand(const QString& t) + : text(t) +{ + +} + +void QEditorInputBinding::WriteCommand::exec(QEditor *e) +{ + e->write(text); +} + +void QEditorInputBinding::GroupCommand::addCommand(Command *c) +{ + commands << c; +} + +void QEditorInputBinding::GroupCommand::exec(QEditor *e) +{ + foreach ( Command *c, commands ) + c->exec(e); +} + +///////////////////////////////////////////////////////////////////////////// + +QEditorInputBinding::QEditorInputBinding() +{ +} + +QEditorInputBinding::~QEditorInputBinding() +{ + qDeleteAll(m_actions); +} + +void QEditorInputBinding::setMapping(const QKeySequence& ks, Command *cmd) +{ + for ( int i = 0; i < m_keys.count(); ++i ) + { + if ( m_keys.at(i) == ks ) + { + delete m_actions[i]; + m_actions[i] = cmd; + return; + } + } + + m_index << 0; + m_keys << ks; + m_actions << cmd; +} + +bool QEditorInputBinding::isExclusive() const +{ + return false; +} + +bool QEditorInputBinding::keyPressEvent(QKeyEvent *event, QEditor *editor) +{ + bool filter = false; + + for ( int i = 0; i < m_keys.count(); ++i ) + { + int idx = m_index.at(i); + const QKeySequence& ks = m_keys.at(i); + + if ( idx < (int)ks.count() ) + { + if ( ks[idx] == event->key() ) + { + ++idx; + + if ( idx == (int)ks.count() ) + { + //qDebug("match"); + + // key sequence matched + m_actions.at(i)->exec(editor); + + // cancel other in progress matches ? + m_index.fill(0); + + //return true; + } else { + //qDebug("step"); + m_index[i] = idx; + } + + // filter out the event + filter = true; + } else { + m_index[i] = 0; + } + } else { + m_index[i] = 0; + } + } + + return filter; +} + +void QEditorInputBinding::postKeyPressEvent(QKeyEvent *event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) +} + +bool QEditorInputBinding::inputMethodEvent(QInputMethodEvent* event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) + + return false; +} + +void QEditorInputBinding::postInputMethodEvent(QInputMethodEvent *event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) +} + +bool QEditorInputBinding::mouseMoveEvent(QMouseEvent *event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) + + return false; +} + +void QEditorInputBinding::postMouseMoveEvent(QMouseEvent *event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) +} + +bool QEditorInputBinding::mousePressEvent(QMouseEvent *event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) + + return false; +} + +void QEditorInputBinding::postMousePressEvent(QMouseEvent *event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) +} + +bool QEditorInputBinding::mouseReleaseEvent(QMouseEvent *event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) + + return false; +} + +void QEditorInputBinding::postMouseReleaseEvent(QMouseEvent *event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) +} + +bool QEditorInputBinding::mouseDoubleClickEvent(QMouseEvent *event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) + + return false; +} + +void QEditorInputBinding::postMouseDoubleClickEvent(QMouseEvent *event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) +} + +bool QEditorInputBinding::contextMenuEvent(QContextMenuEvent *event, QEditor *editor) +{ + Q_UNUSED(event) + Q_UNUSED(editor) + + return false; +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qeditorinputbinding.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qeditorinputbinding.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QEDITOR_INPUT_BINDING_H_ +#define _QEDITOR_INPUT_BINDING_H_ + +#include "qeditorinputbindinginterface.h" + +#include "qdocumentcursor.h" + +#include +#include +#include +#include + +class QCE_EXPORT QEditorInputBinding : public QEditorInputBindingInterface +{ + public: + class Command + { + public: + virtual ~Command() {} + + virtual void exec(QEditor *e) = 0; + }; + + class MotionCommand : public Command + { + public: + MotionCommand(QDocumentCursor::MoveOperation op, QDocumentCursor::MoveMode m, int n = 1); + + virtual void exec(QEditor *e); + + private: + int count; + QDocumentCursor::MoveMode mode; + QDocumentCursor::MoveOperation operation; + }; + + class EditCommand : public Command + { + public: + enum Operation + { + ClearSelection, + SelectWord, + SelectLine, + SelectDocument, + + DeleteChar, + DeletePreviousChar, + DeleteSelection, + DeleteLine, + + InsertLine, + InsertClipBoard, + }; + + EditCommand(Operation op); + + virtual void exec(QEditor *e); + + private: + Operation operation; + }; + + class WriteCommand : public Command + { + public: + WriteCommand(const QString& t); + + virtual void exec(QEditor *e); + + private: + QString text; + }; + + class GroupCommand : public Command + { + public: + void addCommand(Command *c); + + virtual void exec(QEditor *e); + + private: + QList commands; + }; + + QEditorInputBinding(); + ~QEditorInputBinding(); + + void setMapping(const QKeySequence& ks, Command *cmd); + + virtual bool isExclusive() const; + + virtual bool keyPressEvent(QKeyEvent *event, QEditor *editor); + virtual void postKeyPressEvent(QKeyEvent *event, QEditor *editor); + + virtual bool inputMethodEvent(QInputMethodEvent* event, QEditor *editor); + virtual void postInputMethodEvent(QInputMethodEvent *event, QEditor *editor); + + virtual bool mouseMoveEvent(QMouseEvent *event, QEditor *editor); + virtual void postMouseMoveEvent(QMouseEvent *event, QEditor *editor); + + virtual bool mousePressEvent(QMouseEvent *event, QEditor *editor); + virtual void postMousePressEvent(QMouseEvent *event, QEditor *editor); + + virtual bool mouseReleaseEvent(QMouseEvent *event, QEditor *editor); + virtual void postMouseReleaseEvent(QMouseEvent *event, QEditor *editor); + + virtual bool mouseDoubleClickEvent(QMouseEvent *event, QEditor *editor); + virtual void postMouseDoubleClickEvent(QMouseEvent *event, QEditor *editor); + + virtual bool contextMenuEvent(QContextMenuEvent *event, QEditor *editor); + + private: + QVector m_index; + QVector m_actions; + QVector m_keys; +}; + +#endif // _QEDITOR_INPUT_BINDING_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qeditorinputbindinginterface.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qeditorinputbindinginterface.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QEDITOR_INPUT_BINDING_INTERFACE_H_ +#define _QEDITOR_INPUT_BINDING_INTERFACE_H_ + +#include "qce-config.h" + +class QEditor; + +class QString; +class QKeyEvent; +class QMouseEvent; +class QInputMethodEvent; +class QContextMenuEvent; + +class QEditorInputBindingInterface +{ + public: + virtual ~QEditorInputBindingInterface() {} + + virtual QString id() const = 0; + virtual QString name() const = 0; + + virtual bool isExclusive() const = 0; + + virtual bool keyPressEvent(QKeyEvent *event, QEditor *editor) = 0; + virtual void postKeyPressEvent(QKeyEvent *event, QEditor *editor) = 0; + + virtual bool inputMethodEvent(QInputMethodEvent* event, QEditor *editor) = 0; + virtual void postInputMethodEvent(QInputMethodEvent *event, QEditor *editor) = 0; + + virtual bool mouseMoveEvent(QMouseEvent *event, QEditor *editor) = 0; + virtual void postMouseMoveEvent(QMouseEvent *event, QEditor *editor) = 0; + + virtual bool mousePressEvent(QMouseEvent *event, QEditor *editor) = 0; + virtual void postMousePressEvent(QMouseEvent *event, QEditor *editor) = 0; + + virtual bool mouseReleaseEvent(QMouseEvent *event, QEditor *editor) = 0; + virtual void postMouseReleaseEvent(QMouseEvent *event, QEditor *editor) = 0; + + virtual bool mouseDoubleClickEvent(QMouseEvent *event, QEditor *editor) = 0; + virtual void postMouseDoubleClickEvent(QMouseEvent *event, QEditor *editor) = 0; + + virtual bool contextMenuEvent(QContextMenuEvent *event, QEditor *editor) = 0; +}; + +#endif // _QEDITOR_INPUT_BINDING_INTERFACE_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qeditsession.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qeditsession.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,534 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qeditsession.h" + +/*! + \file qeditsession.cpp + \brief Implementation of the QEditSession class. +*/ + +#include "qeditor.h" +#include "qdocument.h" +#include "qdocument_p.h" +#include "qdocumentline.h" +#include "qdocumentcursor.h" + +#include "qlinemarksinfocenter.h" + +#include +#include +#include +#include + +/*! + \class QEditSession + + \brief A session recording class + + The purpose of this class is to collect session data from several QEditor object, + to serialize it and to re-create the same session by deserializing the stored data. +*/ + +/*! + \brief ctor +*/ +QEditSession::QEditSession(QObject *p) + : QObject(p), m_id(-1), m_delay(0) +{ + +} + +/*! + \brief ctor +*/ +QEditSession::QEditSession(const QString& f, QObject *p) + : QObject(p), m_id(-1), m_delay(0) +{ + setFileName(f); +} + +/*! + \brief dtor +*/ +QEditSession::~QEditSession() +{ + +} + +/*! + \return The update interval, in milliseconds + + A value of zero means the data is NOT automatically updated. + + \see updateData() +*/ +int QEditSession::autoUpdateInterval() const +{ + return m_delay; +} + +/*! + \brief Set the update interval + + If \a ms is strictly positive then the data will be + updated every \a ms milliseconds + + If the session has been given a valid filename, the updated + data will automatically be saved to that file. +*/ +void QEditSession::setAutoUpdateInterval(int ms) +{ + if ( m_delay ) + { + killTimer(m_id); + m_id = -1; + } + + m_delay = ms; + + if ( m_delay ) + { + m_id = startTimer(m_delay); + } +} + +/*! + \return The file name used as storage + + If it is empty then no auto-save is performed. +*/ +QString QEditSession::fileName() const +{ + return m_fileName; +} + +/*! + \brief Set the storage destination + + Every time the data is updated, either when the autoupdate timer + ticks or when updateData() is called programmatically, it will be + written to that file, provided the filename is valid and points + to a writeable location. + + \see updateData() + \see autoUpdateInterval() +*/ +void QEditSession::setFileName(const QString& filename, bool r) +{ + m_fileName = filename; + + if ( r ) + restore(); + +} + +/*! + \brief Add an editor to the session +*/ +void QEditSession::addEditor(QEditor *e) +{ + if ( m_editors.contains(e) ) + return; + + //qDebug("+ 0x%x", e); + + Document *d = new Document; + + m_editors << e; + m_sessionData << d; + + connect(e , SIGNAL( destroyed(QObject*) ), + this, SLOT ( destroyed(QObject*) ) ); + + connect(e , SIGNAL( saved(QEditor*, QString) ), + this, SLOT ( saved(QEditor*, QString) ) ); + + connect(e , SIGNAL( loaded(QEditor*, QString) ), + this, SLOT ( loaded(QEditor*, QString) ) ); + + update(e, d); +} + +/*! + \brief Remove an editor from the session +*/ +void QEditSession::removeEditor(QEditor *e) +{ + int idx = m_editors.indexOf(e); + + if ( idx == -1 ) + return; + + //qDebug("- 0x%x", e); + + disconnect( e , SIGNAL( destroyed(QObject*) ), + this, SLOT ( destroyed(QObject*) ) ); + + disconnect( e , SIGNAL( saved(QEditor*, QString) ), + this, SLOT ( saved(QEditor*, QString) ) ); + + disconnect( e , SIGNAL( loaded(QEditor*, QString) ), + this, SLOT ( loaded(QEditor*, QString) ) ); + + m_editors.removeAt(idx); + delete m_sessionData.takeAt(idx); +} + +/*! + +*/ +void QEditSession::clear(bool cleanup) +{ + if ( cleanup ) + qDeleteAll(m_editors); + + qDeleteAll(m_sessionData); + + m_editors.clear(); + m_sessionData.clear(); +} + +/*! + \brief Serialize session data +*/ +void QEditSession::save() +{ + QFile f(m_fileName); + + if ( f.open(QFile::WriteOnly) ) + { + QDataStream s(&f); + + save(s); + } +} + +/*! + \brief Serialize session data +*/ +void QEditSession::restore() +{ + QFile f(m_fileName); + + if ( f.open(QFile::ReadOnly) ) + { + QDataStream s(&f); + + restore(s); + } +} + +static const char _magic[] = "QES "; + +/*! + \brief Serialize session data +*/ +void QEditSession::save(QDataStream& s) +{ + //qDebug("saving"); + + s << *(reinterpret_cast(_magic)); + s << m_sessionData.count(); + + foreach ( Document *d, m_sessionData ) + { + //qDebug("> %s", qPrintable(d->fileName)); + + s << d->fileName; + s << d->timeStamp; + + s << d->cursors.count(); + + foreach ( const Cursor& c, d->cursors ) + s << c.beginLine << c.beginColumn << c.endLine << c.endColumn; + + s << d->marks.count(); + + QHash >::const_iterator it = d->marks.constBegin(); + const QHash >::const_iterator end = d->marks.constEnd(); + + while ( it != end ) + { + s << it.key() << *it; + } + + s << d->scrollX; + s << d->scrollY; + } +} + +/*! + \brief Deserialize session data +*/ +void QEditSession::restore(QDataStream& s) +{ + //qDebug("restoring"); + + quint32 magic; + + s >> magic; + + if ( magic != *(reinterpret_cast(_magic)) ) + { + qDebug("header mismatch : %i, %i", magic, s.status()); + return; + } + + int documentCount = 0; + + s >> documentCount; + + for ( int i = 0; i < documentCount; ++i ) + { + Document *d = new Document; + + s >> d->fileName; + s >> d->timeStamp; + + //qDebug("> %s", qPrintable(d->fileName)); + + int cursorCount = 0; + + s >> cursorCount; + + bool exist = QFile::exists(d->fileName); + QEditor *e = exist ? createEditor() : 0; + + if ( e ) + e->load(d->fileName); + + for ( int j = 0; j < cursorCount; ++j ) + { + Cursor c; + + s >> c.beginLine; + s >> c.beginColumn; + s >> c.endLine; + s >> c.endColumn; + + d->cursors << c; + } + + int markCount = 0; + s >> markCount; + + for ( int j = 0; j < markCount; ++j ) + { + int line = 0; + QList marks; + + s >> line; + s >> marks; + + d->marks[line] = marks; + + foreach ( int mark, marks ) + e->document()->line(line).addMark(mark); + } + + s >> d->scrollX; + s >> d->scrollY; + + if ( e && cursorCount ) + { + QDocumentCursor c = d->cursors.first().toDocumentCursor(e->document()); + + e->setCursor(c); + + for ( int j = 1; j < cursorCount; ++j ) + { + e->addCursorMirror(d->cursors.at(j).toDocumentCursor(e->document())); + } + } + + // TODO : defer. it does not seem to work properly that way + // TODO : view size independency (store the first visible line number) + e->verticalScrollBar()->setValue(d->scrollY); + e->horizontalScrollBar()->setValue(d->scrollX); + + if ( e ) + { + connect(e , SIGNAL( destroyed(QObject*) ), + this, SLOT ( destroyed(QObject*) ) ); + + connect(e , SIGNAL( saved(QEditor*, QString) ), + this, SLOT ( saved(QEditor*, QString) ) ); + + connect(e , SIGNAL( loaded(QEditor*, QString) ), + this, SLOT ( loaded(QEditor*, QString) ) ); + + m_editors << e; + m_sessionData << d; + + emit restored(e); + } else { + delete d; + } + } +} + +/*! + \brief Updates the data + + Fetches up-to-date session data from the attached editors. + + If the session has been given a valid filename the data will + automatically be saved. + + \note This will NOT affect the automatic updates timing +*/ +void QEditSession::updateData() +{ + for ( int i = 0; i < m_editors.count(); ++i ) + { + QEditor *e = m_editors.at(i); + Document *d = m_sessionData.at(i); + + update(e, d); + } + + save(); +} + +void QEditSession::destroyed(QObject *o) +{ + //qDebug("~ 0x%x", o); + + for ( int i = 0; i < m_editors.count(); ++i ) + { + QEditor *e = m_editors.at(i); + + if ( !e || ((QObject*)e == o) ) + { + delete m_sessionData.takeAt(i); + m_editors.removeAt(i); + break; + } + } +} + +/*! + \brief Called whenever an editor is saved + + This handler is responsible for updating file names and time stamps + which is needed to avoid data loss upon session restoration +*/ +void QEditSession::saved(QEditor *e, const QString& fn) +{ + int idx = m_editors.indexOf(e); + + if ( idx == -1 ) + return; + + //qDebug("saved : %s", qPrintable(fn)); + + Document *d = m_sessionData.at(idx); + + //d->timeStamp = QDateTime::currentDateTime(); + + update(e, d); +} + +/*! + \brief Called whenever an editor is loaded with new content + + This handler is responsible for updating file names and time stamps + which is needed to avoid data loss upon session restoration +*/ +void QEditSession::loaded(QEditor *e, const QString& fn) +{ + int idx = m_editors.indexOf(e); + + if ( idx == -1 ) + return; + + //qDebug("loaded : %s", qPrintable(fn)); + + Document *d = m_sessionData.at(idx); + + //d->timeStamp = QDateTime::currentDateTime(); + + update(e, d); +} + +void QEditSession::update(QEditor *e, Document *d) +{ + if ( !e || !d ) + return; + + //qDebug(">>%s", qPrintable(e->fileName())); + + d->fileName = e->fileName(); + d->timeStamp = QFileInfo(d->fileName).lastModified(); + + d->cursors.clear(); + d->cursors << Cursor(e->cursor()); + + for ( int i = 0; i < e->cursorMirrorCount(); ++i ) + d->cursors << Cursor(e->cursorMirror(i)); + + QLineMarkList marks = QLineMarksInfoCenter::instance()->marks(d->fileName); + + foreach ( const QLineMark& mark, marks ) + { + d->marks[mark.line] << mark.mark; + } + + d->scrollX = e->verticalScrollBar()->value(); + d->scrollY = e->horizontalScrollBar()->value(); +} + +/*! + \internal +*/ +void QEditSession::timerEvent(QTimerEvent *e) +{ + if ( e->timerId() == m_id ) + { + updateData(); + } +} + +QEditor* QEditSession::createEditor() +{ + return new QEditor; +} + +QEditSession::Cursor::Cursor(const QDocumentCursor& c) +{ + beginLine = c.lineNumber(); + beginColumn = c.columnNumber(); + endLine = c.hasSelection() ? c.anchorLineNumber() : -1; + endColumn = c.hasSelection() ? c.anchorColumnNumber() : -1; + + //qDebug("((%i, %i), (%i, %i))", beginLine, beginColumn, endLine, endColumn); +} + +QDocumentCursor QEditSession::Cursor::toDocumentCursor(QDocument *d) const +{ + //qDebug("((%i, %i), (%i, %i))", beginLine, beginColumn, endLine, endColumn); + + QDocumentCursor beg(d, beginLine, beginColumn); + QDocumentCursor end(d, endLine, endColumn); + + if ( endLine != -1 ) + { + end.setSelectionBoundary(beg); + return end; + } + + return beg; +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qeditsession.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qeditsession.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QEDIT_SESSION_H_ +#define _QEDIT_SESSION_H_ + +#include "qce-config.h" + +/*! + \file qeditsession.h + \brief Definition of the QEditSession class. +*/ + +#include +#include +#include +#include +#include + +class QEditor; + +class QDocument; +class QDocumentCursor; +class QDocumentCommand; + +class QDataStream; + +class QCE_EXPORT QEditSession : public QObject +{ + Q_OBJECT + + public: + QEditSession(QObject *p = 0); + QEditSession(const QString& f, QObject *p = 0); + virtual ~QEditSession(); + + int autoUpdateInterval() const; + + QString fileName() const; + + public slots: + virtual void addEditor(QEditor *e); + virtual void removeEditor(QEditor *e); + + virtual void updateData(); + + virtual void setAutoUpdateInterval(int ms); + + virtual void setFileName(const QString& filename, bool restore = false); + + virtual void clear(bool cleanup = false); + + virtual void save(); + virtual void restore(); + + virtual void save(QDataStream& s); + virtual void restore(QDataStream& s); + + signals: + void restored(QEditor *e); + + protected slots: + virtual void destroyed(QObject *o); + + virtual void saved(QEditor *e, const QString& fn); + virtual void loaded(QEditor *e, const QString& fn); + + protected: + virtual void timerEvent(QTimerEvent *e); + + virtual QEditor* createEditor(); + + struct Cursor + { + Cursor() + : beginLine(-1), beginColumn(-1), endLine(-1), endColumn(-1) {} + + Cursor(int line, int column) + : beginLine(line), beginColumn(column), endLine(-1), endColumn(-1) {} + + Cursor(const Cursor& c) + : beginLine(c.beginLine), beginColumn(c.beginColumn), endLine(c.endLine), endColumn(c.endColumn) {} + + Cursor(const QDocumentCursor& c); + + QDocumentCursor toDocumentCursor(QDocument *d) const; + + int beginLine; + int beginColumn; + int endLine; + int endColumn; + }; + + struct Document + { + QString fileName; + QDateTime timeStamp; + + int scrollX, scrollY; + + QList cursors; + QHash > marks; + }; + + virtual void update(QEditor *e, Document *d); + + int m_id; + int m_delay; + QString m_fileName; + + QList m_editors; + QList m_sessionData; +}; + +#endif // ! _QEDIT_SESSION_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qformat.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qformat.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QFORMAT_H_ +#define _QFORMAT_H_ + +/*! + \file qformat.h + \brief Definition of the QFormat class +*/ + +#include +#include +#include + +template +class QVector; + +struct QFormat +{ + inline QFormat() + : weight(QFont::Normal), italic(false), overline(false), underline(false), strikeout(false), waveUnderline(false) + {} + + inline QFormat(const QColor& c) + : weight(QFont::Normal), italic(false), overline(false), underline(false), strikeout(false), waveUnderline(false), foreground(c) + {} + + inline QFormat(int w, const QColor& c) + : weight(w), italic(false), overline(false), underline(false), strikeout(false), waveUnderline(false), foreground(c) + {} + + inline QFormat(int w, bool i, bool u, bool s, const QColor& c) + : weight(w), italic(i), overline(false), underline(u), strikeout(s), waveUnderline(false), foreground(c) + {} + + inline QFormat(int w, bool i, bool o, bool u, bool s, bool wu, const QColor& c) + : weight(w), italic(i), overline(o), underline(u), strikeout(s), waveUnderline(wu), foreground(c) + {} + + inline QFormat(const QFormat& f) + : weight(f.weight), italic(f.italic), + overline(f.overline), underline(f.underline), strikeout(f.strikeout), waveUnderline(f.waveUnderline), + foreground(f.foreground), background(f.background), linescolor(f.linescolor) + {} + + inline QFormat& operator = (const QFormat& f) + { + weight = f.weight; + italic = f.italic; + overline = f.overline; + underline = f.underline; + strikeout = f.strikeout; + foreground = f.foreground; + background = f.background; + linescolor = f.linescolor; + waveUnderline = f.waveUnderline; + + return *this; + } + + inline bool operator == (const QFormat& f) const + { + return (weight == f.weight) + && + (italic == f.italic) + && + (overline == f.overline) + && + (underline == f.underline) + && + (strikeout == f.strikeout) + && + (foreground == f.foreground) + && + (background == f.background) + && + (linescolor == f.linescolor) + && + (waveUnderline == f.waveUnderline) + ; + } + + inline bool operator != (const QFormat& f) const + { + return (weight != f.weight) + || + (italic != f.italic) + || + (overline != f.overline) + || + (underline != f.underline) + || + (strikeout != f.strikeout) + || + (foreground != f.foreground) + || + (background != f.background) + || + (linescolor != f.linescolor) + || + (waveUnderline != f.waveUnderline) + ; + } + + QTextCharFormat toTextCharFormat() const + { + QTextCharFormat f; + f.setFontWeight(weight); + f.setFontItalic(italic); + f.setFontOverline(overline); + f.setFontUnderline(underline); + f.setFontStrikeOut(strikeout); + f.setUnderlineColor(linescolor); + + if ( waveUnderline ) + { + f.setUnderlineStyle(QTextCharFormat::WaveUnderline); + } + + if ( foreground.isValid() ) + f.setForeground(foreground); + + if ( background.isValid() ) + f.setBackground(background); + + return f; + } + + int weight; + bool italic; + bool overline; + bool underline; + bool strikeout; + bool waveUnderline; + QColor foreground; + QColor background; + QColor linescolor; +}; + +Q_DECLARE_TYPEINFO(QFormat, Q_MOVABLE_TYPE); + +struct QFormatRange +{ + inline QFormatRange() + : offset(0), length(0), format(0) + {} + + inline QFormatRange(int o, int l, int f) + : offset(o), length(l), format(f) + {} + + inline bool operator == (const QFormatRange& o) + { return (offset == o.offset) && (length == o.length) && (format == o.format); } + + inline bool operator != (const QFormatRange& o) + { return (offset != o.offset) || (length != o.length) || (format != o.format); } + + int offset; + int length; + int format; +}; + +Q_DECLARE_TYPEINFO(QFormatRange, Q_PRIMITIVE_TYPE); + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qformatfactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qformatfactory.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,16 @@ +/* + Transition header file. +*/ + +#ifndef _QFORMAT_FACTORY_H_ + +#include "qformatscheme.h" + +class QFormatFactory : public QFormatScheme +{ + public: + inline QFormatFactory(QObject *p = 0) : QFormatScheme(p) {} + inline QFormatFactory(const QString& f, QObject *p = 0) : QFormatScheme(f, p) {} +}; + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qformatscheme.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qformatscheme.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,493 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qformatscheme.h" + +/*! + \file qformatscheme.cpp + \brief Implementation of QFormatScheme + + \see QFormatScheme +*/ + +/*! + \ingroup language + @{ + + \class QFormatScheme + \brief A storage/configuration class for shared highlighting formats + + It stores text formats used by highlighters interfaces and provides + a default serializing format in QXF format (XML-based). + + \see QLanguageFactory + \see QHighlighter +*/ + +#include "qformat.h" + +#include +#include +#include +#include +#include +#include + +#define QFORMAT_VERSION "1.0" + +static bool bool_cast(const QString& s) +{ + return !QString::compare(s, QLatin1String("true")) || s.toUInt() == 1; +} + +/*! + \brief Constructor +*/ +QFormatScheme::QFormatScheme(QObject *p) + : QObject(p) +{ + setFormat("normal", QFormat()); +} + +/*! + \brief Constructor + \param f Filename of a format settings file to load +*/ +QFormatScheme::QFormatScheme(const QString& f, QObject *p) + : QObject(p) +{ + load(f); +} + +/*! + \brief Destructor +*/ +QFormatScheme::~QFormatScheme() +{ +} + +/*! + \brief Re-initialize the format scheme + + Calling this method leaves the format scheme with only one + format : the "normal" one, set to a default-constructed QFormat +*/ +void QFormatScheme::clear() +{ + m_formatKeys.clear(); + m_formatValues.clear(); + + setFormat("normal", QFormat()); +} + +/*! + \brief Load format settings from a file + \param f file to load data from + + The default implementation loads data in QXF format (XML-based) + + \note Previous content is discarded +*/ +void QFormatScheme::load(const QString& f) +{ + clear(); + m_settings = f; + + QFile settings(f); + + if ( settings.open(QFile::ReadOnly | QFile::Text) ) + { + QDomDocument doc; + doc.setContent(&settings); + + load(doc.documentElement()); + } +} + +/*! + \brief Save the format settings to a file + \param f target file (if none specified, last value passed to load is used) + + The default implementation saves data in QXF format (XML-based) +*/ +void QFormatScheme::save(const QString& f) const +{ + QFile settings(f.count() ? f : m_settings); + + if ( settings.open(QFile::WriteOnly | QFile::Text) ) + { + QDomDocument doc("QXF"); + QDomElement root = doc.createElement("QXF"); + save(root); + doc.appendChild(root); + settings.write(doc.toByteArray(4)); + } +} + +/*! + \overload + \param elem Source element to scan + \param ignoreNewIds whether unknown format identifiers should be ignored + + The given dom element must contain a proper version attribute and format + data as child elements (<format> tags) + + \note Previous content is not discarded +*/ +void QFormatScheme::load(const QDomElement& elem, bool ignoreNewIds) +{ + if ( elem.attribute("version") < QFORMAT_VERSION ) + { + qWarning("Format encoding version mismatch : [found]%s != [expected]%s", + qPrintable(elem.attribute("version")), + QFORMAT_VERSION); + + return; + } + + QDomElement e, c; + QDomNodeList l, f = elem.elementsByTagName("format"); + + for ( int i = 0; i < f.count(); i++ ) + { + e = f.at(i).toElement(); + + if ( ignoreNewIds && !m_formatKeys.contains(e.attribute("id")) ) + continue; + + l = e.childNodes(); + + QFormat fmt; + + for ( int i = 0; i < l.count(); i++ ) + { + c = l.at(i).toElement(); + + if ( c.isNull() ) + continue; + + QString field = c.tagName(), + value = c.firstChild().toText().data(); + + if ( field == "bold" ) + fmt.weight = bool_cast(value) ? QFont::Bold : QFont::Normal; + else if ( field == "italic" ) + fmt.italic = bool_cast(value); + else if ( field == "overline" ) + fmt.overline = bool_cast(value); + else if ( field == "underline" ) + fmt.underline = bool_cast(value); + else if ( field == "strikeout" ) + fmt.strikeout = bool_cast(value); + else if ( field == "waveUnderline" ) + fmt.waveUnderline = bool_cast(value); + else if ( field == "color" || field == "foreground" ) + fmt.foreground = QColor(value); + else if ( field == "background" ) + fmt.background = QColor(value); + else if ( field == "linescolor" ) + fmt.linescolor = QColor(value); + + } + + setFormat(e.attribute("id"), fmt); + } +} + +/*! + \overload +*/ +void QFormatScheme::save(QDomElement& elem) const +{ + QDomDocument doc = elem.ownerDocument(); + elem.setAttribute("version", QFORMAT_VERSION); + + for ( int i = 0; i < m_formatKeys.count(); ++i ) + { + QDomText t; + QDomElement f, c = doc.createElement("format"); + + c.setAttribute("id", m_formatKeys.at(i)); + + const QFormat& fmt = m_formatValues.at(i); + + f = doc.createElement("bold"); + t = doc.createTextNode((fmt.weight == QFont::Bold) ? "true" : "false"); + f.appendChild(t); + c.appendChild(f); + + f = doc.createElement("italic"); + t = doc.createTextNode(fmt.italic ? "true" : "false"); + f.appendChild(t); + c.appendChild(f); + + f = doc.createElement("overline"); + t = doc.createTextNode(fmt.overline ? "true" : "false"); + f.appendChild(t); + c.appendChild(f); + + f = doc.createElement("underline"); + t = doc.createTextNode(fmt.underline ? "true" : "false"); + f.appendChild(t); + c.appendChild(f); + + f = doc.createElement("strikeout"); + t = doc.createTextNode(fmt.strikeout ? "true" : "false"); + f.appendChild(t); + c.appendChild(f); + + f = doc.createElement("waveUnderline"); + t = doc.createTextNode(fmt.waveUnderline ? "true" : "false"); + f.appendChild(t); + c.appendChild(f); + + if ( fmt.foreground.isValid() ) + { + f = doc.createElement("foreground"); + t = doc.createTextNode(fmt.foreground.name()); + f.appendChild(t); + c.appendChild(f); + } + + if ( fmt.background.isValid() ) + { + f = doc.createElement("background"); + t = doc.createTextNode(fmt.background.name()); + f.appendChild(t); + c.appendChild(f); + } + + if ( fmt.linescolor.isValid() ) + { + f = doc.createElement("linescolor"); + t = doc.createTextNode(fmt.linescolor.name()); + f.appendChild(t); + c.appendChild(f); + } + + elem.appendChild(c); + } +} + +/*! + \overload + \brief Load format data from a QSettings object + \param s QSettings object from which data will be fetched + \param ignoreNewIds whether unknown format identifiers should be ignored + + The QSettings object is assumed to be initialized properly and to + point to a correct location. + + \note Previous content is not discarded +*/ +void QFormatScheme::load(QSettings& s, bool ignoreNewIds) +{ + QString version = s.value("version").toString(); + + if ( version < QFORMAT_VERSION ) + { + qWarning("Format encoding version mismatch : [found]%s != [expected]%s", + qPrintable(version), + QFORMAT_VERSION); + + return; + } + + s.beginGroup("data"); + + QStringList l = s.childGroups(); + + foreach ( QString id, l ) + { + if ( ignoreNewIds && !m_formatKeys.contains(id) ) + continue; + + s.beginGroup(id); + + QFormat fmt; + QStringList fields = s.childKeys(); + + foreach ( QString field, fields ) + { + QString value = s.value(field).toString(); + + if ( field == "bold" ) + fmt.weight = bool_cast(value) ? QFont::Bold : QFont::Normal; + else if ( field == "italic" ) + fmt.italic = bool_cast(value); + else if ( field == "overline" ) + fmt.overline = bool_cast(value); + else if ( field == "underline" ) + fmt.underline = bool_cast(value); + else if ( field == "strikeout" ) + fmt.strikeout = bool_cast(value); + else if ( field == "waveUnderline" ) + fmt.waveUnderline = bool_cast(value); + else if ( field == "color" || field == "foreground" ) + fmt.foreground = QColor(value); + else if ( field == "background" ) + fmt.background = QColor(value); + else if ( field == "linescolor" ) + fmt.linescolor = QColor(value); + + } + + setFormat(id, fmt); + s.endGroup(); + } + + s.endGroup(); +} + +/*! + \overload +*/ +void QFormatScheme::save(QSettings& s) const +{ + s.setValue("version", QFORMAT_VERSION); + + s.beginGroup("data"); + + for ( int i = 0; i < m_formatKeys.count(); ++i ) + { + s.beginGroup(m_formatKeys.at(i)); + + const QFormat& fmt = m_formatValues.at(i); + + s.setValue("bold", (fmt.weight == QFont::Bold) ? "true" : "false"); + s.setValue("italic", fmt.italic ? "true" : "false"); + s.setValue("overline", fmt.overline ? "true" : "false"); + s.setValue("underline", fmt.underline ? "true" : "false"); + s.setValue("strikeout", fmt.strikeout ? "true" : "false"); + s.setValue("waveUnderline", fmt.waveUnderline ? "true" : "false"); + + if ( fmt.foreground.isValid() ) + { + s.setValue("foreground", fmt.foreground.name()); + } + + if ( fmt.background.isValid() ) + { + s.setValue("background", fmt.background.name()); + } + + if ( fmt.linescolor.isValid() ) + { + s.setValue("linescolor", fmt.linescolor.name()); + } + + s.endGroup(); + } + + s.endGroup(); +} + +/*! + \return The number of available formats +*/ +int QFormatScheme::formatCount() const +{ + return m_formatValues.count(); +} + +/*! + \return A list of available format keys +*/ +QStringList QFormatScheme::formats() const +{ + return m_formatKeys.toList(); +} + +/*! + \return The format key associated to integer format id \a ifid +*/ +QString QFormatScheme::id(int ifid) const +{ + return m_formatKeys.at(ifid); +} + +/*! + \return The integer format id associated to format key \a fid +*/ +int QFormatScheme::id(const QString& sfid) const +{ + int idx = m_formatKeys.indexOf(sfid); + + return (idx == -1) ? 0 : idx; +} + +/*! + \return The text format associated with format key \a fid + + \warning Use at your own risks : if there are no format associated + with the requested id this function will crash +*/ +QFormat& QFormatScheme::formatRef(int ifid) +{ + return m_formatValues[ifid]; +} + +/*! + \return The a reference to the text format associated with format key \a fid + + \warning Use at your own risks : if there are no format associated + with the requested id this function will crash. +*/ +QFormat& QFormatScheme::formatRef(const QString& sfid) +{ + return m_formatValues[id(sfid)]; +} + +/*! + \return The text format associated with format key \a fid +*/ +QFormat QFormatScheme::format(int ifid) const +{ + //qDebug("Querying format id %i within ]-1, %i[", ifid, m_formatValues.count()); + + return (ifid < m_formatValues.count()) ? m_formatValues.at(ifid) : QFormat(); +} + +/*! + \return The text format associated with format key \a fid +*/ +QFormat QFormatScheme::format(const QString& sfid) const +{ + return format(id(sfid)); +} + +/*! + \brief Set text format for key + \param fid Format key + \param fmt Format value +*/ +void QFormatScheme::setFormat(const QString& fid, const QFormat& fmt) +{ + const int idx = m_formatKeys.indexOf(fid); + + if ( idx != -1 ) + { + m_formatValues[idx] = fmt; + } else { + + //qDebug("adding format %s [%i]", qPrintable(fid), m_formatKeys.count()); + + m_formatKeys << fid; + m_formatValues << fmt; + } +} + +/*! @} */ + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qformatscheme.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qformatscheme.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QFORMAT_SCHEME_H_ +#define _QFORMAT_SCHEME_H_ + +/*! + \file qformatscheme.h + \brief Definition of the QFormatScheme class. + + \see QFormatScheme +*/ + +/*! + \defgroup language Language framework +*/ + +#include "qce-config.h" + +#include +#include + +struct QFormat; +class QString; +class QSettings; +class QStringList; +class QDomElement; + +class QCE_EXPORT QFormatScheme : public QObject +{ + Q_OBJECT + + public: + QFormatScheme(QObject *p = 0); + QFormatScheme(const QString& f, QObject *p = 0); + virtual ~QFormatScheme(); + + void clear(); + + virtual void load(const QString& filename); + virtual void save(const QString& filename = QString()) const; + + virtual void load(const QDomElement& doc, bool ignoreNewIds = false); + virtual void save(QDomElement& elem) const; + + virtual void load(QSettings& s, bool ignoreNewIds = false); + virtual void save(QSettings& s) const; + + int formatCount() const; + QStringList formats() const; + + virtual QString id(int ifid) const; + virtual int id(const QString& sfid) const; + + virtual QFormat& formatRef(int ifid); + virtual QFormat& formatRef(const QString& sfid); + + virtual QFormat format(int ifid) const; + virtual QFormat format(const QString& sfid) const; + + public slots: + virtual void setFormat(const QString& fid, const QFormat& fmt); + + protected: + QString m_settings; + + QVector m_formatKeys; + QVector m_formatValues; +}; + +#endif // !_QFORMAT_SCHEME_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qlanguagedefinition.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qlanguagedefinition.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qlanguagedefinition.h" + +/*! + \file qlanguagedefinition.cpp + \brief Implementation of QLanguageDefinition + + \see QLanguageDefinition +*/ + +/*! + \ingroup language + @{ + + \class QLanguageDefinition + \brief Interface for language definition. + + This class is meant to be subclassed, see \see QGenericDefinition for more + informations, and added to a QLanguageFactory. + + A language definition is a wrapper that creates interfaces for a given file + extension from internally handled data (XML files in the case of + QGenericDefinition) + + \see QLanguageFactory +*/ + +#include "qdocument.h" +#include "qdocumentline.h" +#include "qdocumentcursor.h" + +#include "qlanguagefactory.h" + +#include + +/*! + \brief Empty constructor +*/ +QLanguageDefinition::QLanguageDefinition() +{ +} + +/*! + \brief Empty destructor +*/ +QLanguageDefinition::~QLanguageDefinition() +{ +} + +/*! + \fn QLanguageDefinition::language() + + \return The language supported by this definition +*/ + +/*! + \fn QLanguageDefinition::extensions() + + \return the file extensions corrseponding to the supported language + + \see language() + \see QFileInfo::completeSuffix() +*/ + +/*! + \brief Entry point for syntax highlighting +*/ +int QLanguageDefinition::tokenize(QDocument *d, int line, int count) +{ + Q_UNUSED(d) + Q_UNUSED(line) + + return count; +} + +/*! + \brief Return the string starting a single line comment, if any offered by the language +*/ +QString QLanguageDefinition::singleLineComment() const +{ + return QString(); +} + +/*! + \brief Let language specify which line mark should be toggled by left clicking a line mark panel +*/ +QString QLanguageDefinition::defaultLineMark() const +{ + return QString(); +} + +/*! + \brief Brace matching entry point +*/ +void QLanguageDefinition::clearMatches(QDocument *d) +{ + Q_UNUSED(d) +} + +/*! + \brief Brace matching entry point +*/ +void QLanguageDefinition::match(QDocumentCursor& c) +{ + Q_UNUSED(c) +} + +/*! + \brief Return the indent to use when inserting a line at a given cursor position +*/ +QString QLanguageDefinition::indent(const QDocumentCursor& c) +{ + Q_UNUSED(c) + + return QString(); +} + +/*! + \brief Determines whether the given key event at the given position should cause unindent to happen +*/ +bool QLanguageDefinition::unindent (const QDocumentCursor& c, const QString& ktxt) +{ + Q_UNUSED(c) + Q_UNUSED(ktxt) + + return false; +} + +/*! + \brief Expand a collapsed block at a given line +*/ +void QLanguageDefinition::expand(QDocument *d, int line) +{ + Q_UNUSED(d) + Q_UNUSED(line) +} + +/*! + \brief Collapse a text block at a given line +*/ +void QLanguageDefinition::collapse(QDocument *d, int line) +{ + Q_UNUSED(d) + Q_UNUSED(line) +} + +/*! + \brief Compute the collapse state of a line +*/ +int QLanguageDefinition::blockFlags(QDocument *d, int line, int depth) const +{ + Q_UNUSED(d) + Q_UNUSED(line) + Q_UNUSED(depth) + + return 0; +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qlanguagedefinition.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qlanguagedefinition.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QLANGUAGE_DEFINITION_H_ +#define _QLANGUAGE_DEFINITION_H_ + +#include "qce-config.h" + +/*! + \file qlanguagedefinition.h + \brief Definition of the QLanguageDefinition class. + + \see QLanguageDefinition +*/ + +#include "qformat.h" + +#include + +class QKeyEvent; +class QDocument; +class QDocumentCursor; + +#define QCE_FOLD_FLAGS(flags, open, close) ((flags) | (open & QLanguageDefinition::OpenMask) | ((close << 12) & QLanguageDefinition::CloseMask)) +#define QCE_FOLD_OPEN_COUNT(flags) ((flags) & QLanguageDefinition::OpenMask) +#define QCE_FOLD_CLOSE_COUNT(flags) (((flags) & QLanguageDefinition::CloseMask) >> 12) + +class QCE_EXPORT QLanguageDefinition +{ + public: + /// Collapse state of a line + enum CollapseFlag + { + None = 0x00000000, ///< The line cannot be collapsed nor expanded + Collapsible = 0x10000000, ///< The line is expanded and can thus be collapsed + Collapsed = 0x20000000, ///< The line is collapsed and can thus be expanded + Closure = 0x40000000, ///< The line is expanded and mark the end of a block + + CloseMask = 0x00fff000, ///< Number of actual closing fold mark + OpenMask = 0x00000fff ///< Number of actual open fold mark + }; + + Q_DECLARE_FLAGS(CollapseState, CollapseFlag); + + QLanguageDefinition(); + virtual ~QLanguageDefinition(); + + virtual QString language() const = 0; + virtual QStringList extensions() const = 0; + + virtual int tokenize(QDocument *d, int line, int count); + + virtual QString singleLineComment() const; + + virtual QString defaultLineMark() const; + + virtual void match(QDocumentCursor& c); + virtual void clearMatches(QDocument *d); + + virtual QString indent(const QDocumentCursor& c); + virtual bool unindent (const QDocumentCursor& c, const QString& ktxt); + + virtual void expand(QDocument *d, int line); + virtual void collapse(QDocument *d, int line); + virtual int blockFlags(QDocument *d, int line, int depth = 0) const; +}; + +#endif // _QLANGUAGE_DEFINITION_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qlanguagefactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qlanguagefactory.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qlanguagefactory.h" + +/*! + \file qlanguagefactory.cpp + \brief Implementation of QLanguageFactory + + \see QLanguageFactory +*/ + +/*! + \ingroup language + @{ + + \class QLanguageFactory + \brief A class managing language definitions. + + It stores language definitions, added programmatically or found in XML files, + in specified locations and only if generic components are built-in. From + these definitions, QLanguageFactory generates matchers, indenters and + highlighters for a text editor, according to a file name. + + \see QLanguageDefinition +*/ + +#include +#include +#include + +#ifdef _QCODE_EDIT_DEBUG_ +#include +#endif + +#include "qeditor.h" +#include "qformatscheme.h" + +#include "qlanguagedefinition.h" +#include "qcodecompletionengine.h" + +#ifdef QNFA_BUILD +#include "qnfadefinition.h" +#endif + +/*! + \brief Empty constructor +*/ +QLanguageFactory::QLanguageFactory(QFormatScheme *fmt, QObject *p) + : QObject(p), m_defaultFormatScheme(fmt) +{ + +} + +/*! + \brief Empty destructor +*/ +QLanguageFactory::~QLanguageFactory() +{ + foreach ( QString l, m_languages ) + { + const LangData& d = m_data[l]; + + if ( d.s != m_defaultFormatScheme ) + delete d.s; + + delete d.d; + //delete d.e; + } +} + +/*! + \return a list of languages supported by this factory +*/ +QStringList QLanguageFactory::languages() const +{ + return m_languages; +} + +/*! + \return a list of file filters supported by this factory + + \note This list is NEVER empty and the last item is always "All files (*)" +*/ +QStringList QLanguageFactory::fileFilters() const +{ + QStringList l; + + foreach ( QString lng, m_languages ) + l << tr("%1 files (*.%2)").arg(lng).arg(m_data[lng].extensions.join(" *.")); + + l << tr("All files (*)"); + + return l; +} + +/*! + \param e target editor + \param file filename displayed by the editor + + The \a file parameter may actuall be either a filename, an extension or the name of the language, + checked in that order. + + If it is a filename, complete extension as higher priority than simple extension + (see QFileInfo suffix() and completeSuffix()). + + Matches are first done case-sensitively. + + If no matching language definition is found for all three possible interpretations of the \a file + parameter, the same search is done case-insensitively. + + If no matching language is found the previous language definition/completion engine of the editor + are removed, leaving it blank, and the format scheme of the document is set to the defaultFormatScheme() +*/ +void QLanguageFactory::setLanguage(QEditor *e, const QString& file) +{ + QString lang; + QFileInfo inf(file); + const QString ext = inf.suffix(), + cext = inf.completeSuffix(); + + //qDebug("suff:%s; compSuff:%s", qPrintable(ext), qPrintable(cext)); + + QLanguageDefinition *oldLang = e->languageDefinition(); + + if ( file.count() ) + { + QList lcs; + lcs << Qt::CaseSensitive << Qt::CaseInsensitive; + + foreach ( Qt::CaseSensitivity cs, lcs ) + { + int n = 0, idx = -1; + QStringList ext_langs, cext_langs, fcext_langs; + + foreach ( QString lang, m_languages ) + { + const QStringList& exts = m_data[lang].extensions; + + //qDebug("%s in (%s) ?", qPrintable(ext), qPrintable(exts.join(" "))); + + foreach ( QString x, exts ) + { + if ( !x.compare(ext, cs) ) + ext_langs << lang; + + if ( !x.compare(cext, cs) ) + cext_langs << lang; + + if ( !x.compare(file, cs) ) + fcext_langs << lang; + } + + if ( !lang.compare(file, cs) ) + idx = n; + + ++n; + } + + if ( cext_langs.count() ) + { + // TODO : use MIME types to resolve ambiguity + lang = cext_langs.first(); + } else if ( ext_langs.count() ) { + // TODO : use MIME types to resolve ambiguity + lang = ext_langs.first(); + } else if ( fcext_langs.count() ) { + // TODO : use MIME types to resolve ambiguity + lang = fcext_langs.first(); + } else if ( idx != -1 ) { + lang = m_languages.at(idx); + } + + if ( lang.count() ) + break; + } + } + + if ( lang.isEmpty() ) + { + //qDebug("no lang match for %s", qPrintable(file)); + e->setLanguageDefinition(0); + e->setCompletionEngine(0); + e->document()->setFormatScheme(m_defaultFormatScheme); + + if ( oldLang ) + e->highlight(); + + } else { + //qDebug("lang match for %s : %s", qPrintable(file), qPrintable(lang)); + const LangData& data = m_data[lang]; + + e->setLanguageDefinition(data.d); + e->setCompletionEngine(data.e ? data.e->clone() : 0); + e->document()->setFormatScheme(data.s ? data.s : m_defaultFormatScheme); + + if ( oldLang != data.d ) + e->highlight(); + } +} + +/*! + \brief Adds a language to the factory + + \param d language data + + \note The language data will overwrite any existing one for the same language +*/ +void QLanguageFactory::addLanguage(const QLanguageFactory::LangData& d) +{ + m_data[d.lang] = d; + + if ( !d.e ) + { + foreach ( QCodeCompletionEngine *e, m_unusedEngines ) + { + if ( e->language() == d.lang ) + { + m_data[d.lang].e = e; + break; + } + } + } + + if ( !m_languages.contains(d.lang) ) + m_languages << d.lang; +} + +/*! + \brief Lookup language data for a matching language + + The primary purpose of this function is to make it easy to create configuration dialogs (mainly for + format schemes). Beware though : some language may use the default format scheme. It is recommended + to check for that before modifying a format scheme or users might be surprised... + + \warning This function will lead to crashes if you pass it a language name not contained in languages(). +*/ +const QLanguageFactory::LangData& QLanguageFactory::languageData(const QString& lang) +{ + return m_data[lang]; +} + +/*! + \brief Registers a new completion engine + + \note This engine will NOT be used if there are no language definition for the + language it supports... +*/ +void QLanguageFactory::addLanguageDefinition(QLanguageDefinition *l) +{ + Q_UNUSED(l) + + qWarning("New design does not allow this sorry..."); +} + +/*! + \brief Registers a new completion engine + + \note This engine will NOT be used if there are no language definition for the + language it supports... +*/ +void QLanguageFactory::addCompletionEngine(QCodeCompletionEngine *e) +{ + foreach ( QString l, m_languages ) + { + if ( l == e->language() ) + { + m_data[l].e = e; + return; + } + } + + m_unusedEngines << e; +} + +/*! + \brief Fetches syntax definitions from files in \a path +*/ +void QLanguageFactory::addDefinitionPath(const QString& path) +{ + QDir d(path); + + foreach ( QString f, d.entryList(QDir::Files | QDir::Readable) ) + { + #ifdef QNFA_BUILD + if ( f.endsWith(".qnfa") ) + { + //qDebug("loading file %s", qPrintable(f)); + QFileInfo info(d.filePath(f)); + QString specificFormatScheme = QDir(info.path()).filePath(info.baseName() + ".qxf"); + + QFormatScheme *scheme = m_defaultFormatScheme; + + if ( QFile::exists(specificFormatScheme) ) + { + scheme = new QFormatScheme(specificFormatScheme); + } + + LangData data; + QNFADefinition::load(d.filePath(f), &data, scheme); + + //qDebug("%s : (%s | %s)", qPrintable(data.lang), qPrintable(data.mime), qPrintable(data.extensions.join(", "))); + addLanguage(data); + //addLanguageDefinition(new QNFADefinition(d.filePath(f), this)); + } + #endif + } +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qlanguagefactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qlanguagefactory.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QLANGUAGE_FACTORY_H_ +#define _QLANGUAGE_FACTORY_H_ + +/*! + \file qlanguagefactory.h + \brief Definition of the QLanguageFactory class. + + \see QLanguageFactory +*/ + +/*! + \defgroup language Language framework +*/ + +#include "qce-config.h" + +#include +#include +#include + +class QEditor; +class QFormatScheme; +class QLanguageDefinition; +class QCodeCompletionEngine; + +class QCE_EXPORT QLanguageFactory : public QObject +{ + Q_OBJECT + + public: + struct LangData + { + QString lang, mime; + QStringList extensions; + + QFormatScheme *s; + + QLanguageDefinition *d; + QCodeCompletionEngine *e; + }; + + QLanguageFactory(QFormatScheme *fmt, QObject *p = 0); + virtual ~QLanguageFactory(); + + QStringList languages() const; + QStringList fileFilters() const; + + const LangData& languageData(const QString& lang); + + void addDefinitionPath(const QString& path); + + inline QFormatScheme* defaultFormatScheme() const { return m_defaultFormatScheme; } + + public slots: + void addLanguage(const LangData& d); + void addLanguageDefinition(QLanguageDefinition *l); + void addCompletionEngine(QCodeCompletionEngine *e); + + virtual void setLanguage(QEditor *e, const QString& f); + + private: + QStringList m_languages; + QHash m_data; + QList m_unusedEngines; + + QFormatScheme *m_defaultFormatScheme; +}; + +#endif // _QLANGUAGE_FACTORY_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qlinemarksinfocenter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qlinemarksinfocenter.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,643 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qlinemarksinfocenter.h" + +/*! + \file qlinemarksinfocenter.cpp + \brief Implementation of QLineMarksInfoCenter + + \see QLineMarksInfoCenter +*/ + +/*! + \ingroup language + @{ + + \class QLineMarksInfoCenter + \brief A class managing line marks accross all managed editors + + QLineMarksInfoCenter provides mean to read/write line marks on managed editors + but also to serialize and deserialize that data. +*/ + +#include "qeditor.h" +#include "qcodeedit.h" + +#include "qdocument.h" +#include "qdocument_p.h" +#include "qdocumentline.h" + +#include +#include +#include + +#define QLINE_MARKS_DUMP_VERSION 1 +#define QLINE_MARKS_DUMP_VERSION_MIN 1 + +QLineMarksInfoCenter* QLineMarksInfoCenter::m_instance = 0; + +QLineMarksInfoCenter* QLineMarksInfoCenter::instance() +{ + if ( !m_instance ) + m_instance = new QLineMarksInfoCenter; + + return m_instance; +} + +void QLineMarksInfoCenter::destroy() +{ + if ( m_instance ) + delete m_instance; + + m_instance = 0; +} + +QLineMarksInfoCenter::QLineMarksInfoCenter() + : QObject(0) +{ + qRegisterMetaType("QLineMark"); +} + +QLineMarksInfoCenter::~QLineMarksInfoCenter() +{ + +} + +/*! + \return the list of line marks set on a given file +*/ +QLineMarkList QLineMarksInfoCenter::marks(const QString& file) +{ + QLineMarkList l; + bool check = file.count(); + + foreach ( QLineMarkHandle m, m_lineMarks ) + { + if ( !check || (m.file == file) ) + l << QLineMark(file, m.line->line() + 1, m.mark); + } + + return l; +} + +/*! + \brief Remove all line marks on all files +*/ +void QLineMarksInfoCenter::clear() +{ + foreach ( QLineMarkHandle m, m_lineMarks ) + { + removeLineMark(m); + } +} + +/*! + \brief Remove all line marks on a given file +*/ +void QLineMarksInfoCenter::removeMarks(const QString& file) +{ + foreach ( QLineMarkHandle m, m_lineMarks ) + if ( m.file == file ) + removeLineMark(m); + +} + +/*! + \brief Add a line mark + + If the target file is not found the toggling will be delayed. +*/ +void QLineMarksInfoCenter::addLineMark(const QLineMark& mark) +{ + QEditor *e = QCodeEdit::managed(mark.file); + + if ( !e ) + { + m_delayed << mark; + return; + } + + QDocumentLine l = e->document()->line(mark.line - 1); + + if ( !l.isValid() ) + return; + + e->setCursor(QDocumentCursor(e->document(), mark.line - 1)); + l.addMark(mark.mark); +} + +/*! + \brief Remove a line mark + + If the target file is not found the addition will be delayed. +*/ +void QLineMarksInfoCenter::toggleLineMark(const QLineMark& mark) +{ + QEditor *e = QCodeEdit::managed(mark.file); + + if ( !e ) + { + m_delayed << mark; + return; + } + + QDocumentLine l = e->document()->line(mark.line - 1); + + if ( !l.isValid() ) + return; + + e->setCursor(QDocumentCursor(e->document(), mark.line - 1)); + l.toggleMark(mark.mark); +} + +/*! + \brief Toggle a line mark + + If the target file is not found the removal will be delayed. +*/ +void QLineMarksInfoCenter::removeLineMark(const QLineMark& mark) +{ + QEditor *e = QCodeEdit::managed(mark.file); + + if ( !e ) + return; + + QDocumentLine l = e->document()->line(mark.line - 1); + + if ( !l.isValid() ) + return; + + l.removeMark(mark.mark); + + //e->setCursor(QDocumentCursor(l)); +} + +/*! + \brief Add a line mark +*/ +void QLineMarksInfoCenter::addLineMark(const QLineMarkHandle& mark) +{ + QDocumentLine l(mark.line); + + if ( l.isValid() ) + l.addMark(mark.mark); + +} + +/*! + \brief Toggle a line mark +*/ +void QLineMarksInfoCenter::toggleLineMark(const QLineMarkHandle& mark) +{ + QDocumentLine l(mark.line); + + if ( l.isValid() ) + l.toggleMark(mark.mark); + +} + +/*! + \brief Remove a line mark +*/ +void QLineMarksInfoCenter::removeLineMark(const QLineMarkHandle& mark) +{ + QDocumentLine l(mark.line); + + if ( l.isValid() ) + l.removeMark(mark.mark); + +} + +/*! + \brief Flush all delayed line marks addition/removal/toggling for a given file +*/ +void QLineMarksInfoCenter::flush(const QString& file) +{ + QLineMarkList::iterator i = m_delayed.begin(); + + while ( i != m_delayed.end() ) + { + if ( i->file == file ) + { + addLineMark(*i); + i = m_delayed.erase(i); + } else { + ++i; + } + } +} + +/*! + \brief Load serialized line marks data from a file +*/ +void QLineMarksInfoCenter::loadMarks(const QString& f) +{ + QFile file(f); + + if ( !file.open(QFile::ReadOnly) ) + return; + + QDataStream stream(&file); + + int version; + + stream >> version; + + if ( version < QLINE_MARKS_DUMP_VERSION_MIN ) + { + qWarning("QLineMarksInfoCenter : dump file version mismatch"); + return; + } else if ( version > QLINE_MARKS_DUMP_VERSION ) { + qWarning("QLineMarksInfoCenter : dump file version mismatch"); + return; + } + + QLineMark mark; + + while ( !stream.atEnd() ) + { + stream >> mark; + + addLineMark(mark); + } +} + +/*! + \brief Write serialized line marks data to a file +*/ +void QLineMarksInfoCenter::saveMarks(const QString& f) +{ + QFile file(f); + + if ( !file.open(QFile::WriteOnly) ) + return; + + QDataStream stream(&file); + + stream << QLINE_MARKS_DUMP_VERSION; + + foreach ( QLineMarkHandle mark, m_lineMarks ) + { + stream << mark.line->line() + 1; + stream << mark.file; + stream << QLineMarksInfoCenter::instance()->markTypeId(mark.mark); + //stream << mark; + } +} + +QDataStream& operator >> (QDataStream& d, QLineMark& m) +{ + int line; + QString file, mark; + + d >> line; + d >> file; + d >> mark; + + m.line = line; + m.file = file; + m.mark = QLineMarksInfoCenter::instance()->markTypeId(mark); + + return d; +} + +QDataStream& operator << (QDataStream& d, const QLineMark& m) +{ + int line = m.line; + QString file = m.file, + mark = QLineMarksInfoCenter::instance()->markTypeId(m.mark); + + d << line; + d << file; + d << mark; + + return d; +} + +/*! + \brief Load line marks definition from a file +*/ +void QLineMarksInfoCenter::loadMarkTypes(const QString& f) +{ + QFile file(f); + + if ( !file.open(QFile::ReadOnly | QFile::Text) ) + return; + + // TODO : prefer QXmlStreamReader when building against Qt 4.3.0 + + QDomDocument doc; + doc.setContent(&file); + + QDomNodeList l = doc.documentElement().childNodes(); + + for ( int i = 0; i < l.count(); i++ ) + { + QDomElement e = l.at(i).toElement(); + + if ( e.isNull() || (e.tagName() != "mark") ) + continue; + + QLineMarkType t; + QDomNodeList c = e.childNodes(); + + //qDebug("mark {"); + + for ( int j = 0; j < c.count(); j++ ) + { + QDomElement attr = c.at(j).toElement(); + + if ( attr.isNull() ) + continue; + + const QString field = attr.tagName(); + const QString value = attr.firstChild().toText().data(); + + //qDebug("\t%s = %s;", qPrintable(field), qPrintable(value)); + + const bool flag = (value == "true") || value.toUInt(); + + if ( field == "id" ) + { + t.id = value; + } else if ( field == "user" ) { + t.user = flag; + } else if ( field == "focus" ) { + t.focus = flag; + } else if ( field == "icon" ) { + t.icon = QPixmap(value); + } else if ( field == "color" ) { + //t.color = QColor(value); + + /* + color value MUST be a valid value for QColor::setNamedColor() + with one exception though : an alpha channel indication (unsupported + by QColor::setNamedColor()) can be prepended using '@' followed by a + sequence of hex digits (preferabily two of them...) + + examples : + #ff0c80 + #ff0c80@80 + blue + blue@10 + */ + + if ( value.contains('@') ) + { + t.color = QColor(value.section('@', 0, 0, QString::SectionSkipEmpty)); + t.color.setAlpha(value.section('@', 1, 1, QString::SectionSkipEmpty).toUInt(0, 16)); + } else { + t.color = QColor(value); + } + } else if ( field == "priority" ) { + t.priority = value.toUInt(); + } else if ( field == "persistency" ) { + t.persistency = value.toUInt(); + } else if ( field == "rule" ) { + t.rules << value; + } + + } + + m_lineMarkTypes << t; + + //qDebug("};"); + } +} + +/*! + \brief int -> string mark type identifier conversion +*/ +QString QLineMarksInfoCenter::markTypeId(int id) +{ + return ((id >= 0) && (id < m_lineMarkTypes.count())) ? m_lineMarkTypes.at(id).id : QString(); +} + +/*! + \brief string -> int mark type identifier conversion +*/ +int QLineMarksInfoCenter::markTypeId(const QString& id) +{ + for ( int idx = 0; idx < m_lineMarkTypes.count(); ++idx ) + if ( m_lineMarkTypes.at(idx).id == id ) + return idx; + + return -1; +} + +/*! + \return The mark type definition associated with a given id +*/ +QLineMarkType QLineMarksInfoCenter::markType(int id) +{ + return ((id >= 0) && (id < m_lineMarkTypes.count())) ? m_lineMarkTypes.at(id) : QLineMarkType(); +} + +/*! + \return the mark type definition associated with a given id +*/ +QLineMarkType QLineMarksInfoCenter::markType(const QString& id) +{ + foreach ( QLineMarkType t, m_lineMarkTypes ) + if ( t.id == id ) + return t; + + return QLineMarkType(); +} + +/*! + \return A list of available mark types + \param context context filter (no filtering is performed if empty) +*/ +QStringList QLineMarksInfoCenter::availableMarkTypes(const QString& context) +{ + QStringList l; + + foreach ( QLineMarkType t, m_lineMarkTypes ) + { + if ( + context.count() + && + ( + !t.user + || + ( + t.rules.contains("#out") + && + !t.rules.contains(context) + ) + || + ( + t.rules.contains("#in") + && + t.rules.contains("!" + context) + ) + ) + ) + { + //qDebug("mark[%s] mismatched", qPrintable(t.id)); + } else { + l << t.id; + } + } + + return l; +} + +/*! + \return the mark that has the highest priority among a list of marks +*/ +int QLineMarksInfoCenter::priority(const QList& marks) +{ + int higher = -1; + int mark = marks.isEmpty() ? -1 : marks.at(0); + + for ( int i = 0; i < m_lineMarkTypes.count(); ++i ) + { + if ( marks.contains(i) && (m_lineMarkTypes.at(i).priority > higher) ) + { + mark = i; + higher = m_lineMarkTypes.at(i).priority; + } + } + + return mark; +} + +/*! + \return the mark that has the highest priority among a list of marks +*/ +QString QLineMarksInfoCenter::priority(const QStringList& marks) +{ + QString mark; + int higher = -1; + + foreach ( QLineMarkType t, m_lineMarkTypes ) + { + if ( marks.contains(t.id) && (t.priority > higher) ) + { + mark = t.id; + higher = t.priority; + } + } + + return (mark.count() || !marks.count()) ? mark : marks.at(0); +} + +/*! + \brief Useless for now +*/ +QList QLineMarksInfoCenter::marksLayout(const QString& context) +{ + QList l; + + + foreach ( QString id, availableMarkTypes(context) ) + { + l << QStringList(id); + } + + /* + foreach ( QLineMarkType t, availableMarks(context) ) + { + + } + */ + + return l; +} + +/*! + \internal +*/ +void QLineMarksInfoCenter::cursorMoved(QEditor *e) +{ + foreach ( const QLineMarkHandle& lmh, m_lineMarks ) + { + QLineMarkType t = markType(lmh.mark); + + if ( + (e->fileName() != lmh.file) + || + (e->document() != lmh.line->document()) + || + (t.persistency == 2) + ) + continue; + + if ( !t.persistency || (lmh.line != e->cursor().line().handle()) ) + { + removeLineMark(lmh); + cursorMoved(e); + break; + } + } +} + +/*! + \internal +*/ +void QLineMarksInfoCenter::lineDeleted(QDocumentLineHandle *h) +{ + QLineMarkHandleList::iterator i = m_lineMarks.begin(); + + while ( i != m_lineMarks.end() ) + { + if ( i->line == h ) + { + QLineMark mrk(i->file, i->line->line() + 1, i->mark); + + i = m_lineMarks.erase(i); + + emit lineMarkRemoved(mrk); + } else { + ++i; + } + } +} + +/*! + \brief Entry point for changes in documents + + Every document notify through this function a change in its line marks... +*/ +void QLineMarksInfoCenter::markChanged(const QString& f, QDocumentLineHandle *line, int mark, bool on) +{ + QLineMarkHandle m(f, line, mark); + bool in = m_lineMarks.contains(m); + QLineMark mrk(f, line->line() + 1, mark); + + if ( !on && in ) + { + m_lineMarks.removeAll(m); + + emit lineMarkRemoved(mrk); + } else if ( on && !in ) { + m_lineMarks << m; + + emit lineMarkAdded(mrk); + } + + /* + foreach ( const QLineMarkHandle& h, m_lineMarks ) + { + qDebug("\t%s:%i [%i]", qPrintable(h.file), h.line->line() + 1, h.mark); + } + */ +} + +/*! @} */ + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qlinemarksinfocenter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qlinemarksinfocenter.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QLINE_MARKS_INFO_CENTER_H_ +#define _QLINE_MARKS_INFO_CENTER_H_ + +/*! + \file qlinemarksinfocenter.h + \brief Definition of the QLineMarksInfoCenter class. + + \see QLineMarksInfoCenter +*/ + +/*! + \defgroup language Language framework +*/ + +#include "qce-config.h" + +#include +#include +#include +#include +#include + +class QEditor; +class QDataStream; +class QDocumentLineHandle; + +struct QLineMark +{ + inline QLineMark() : line(-1) {} + + inline QLineMark(const QString& f, int l, int m) + : mark(m), line(l), file(f) + {} + + inline bool operator == (const QLineMark& m) + { return (line == m.line) && (file == m.file) && (mark == m.mark); } + + inline bool operator != (const QLineMark& m) + { return (line != m.line) || (file != m.file) || (mark != m.mark); } + + int mark; + int line; + QString file; +}; + +Q_DECLARE_METATYPE(QLineMark) + +typedef QList QLineMarkList; + +Q_DECLARE_TYPEINFO(QLineMark, Q_MOVABLE_TYPE); + +struct QLineMarkHandle +{ + inline QLineMarkHandle() : line(0) {} + + inline QLineMarkHandle(const QString& f, QDocumentLineHandle *l, int m) + : mark(m), line(l), file(f) + {} + + inline bool operator == (const QLineMarkHandle& m) + { return (line == m.line) && (file == m.file) && (mark == m.mark); } + + inline bool operator != (const QLineMarkHandle& m) + { return (line != m.line) || (file != m.file) || (mark != m.mark); } + + int mark; + QDocumentLineHandle *line; + QString file; +}; + +Q_DECLARE_METATYPE(QLineMarkHandle) + +typedef QList QLineMarkHandleList; + +Q_DECLARE_TYPEINFO(QLineMarkHandle, Q_MOVABLE_TYPE); + +QCE_EXPORT QDataStream& operator >> (QDataStream& d, QLineMark& m); +QCE_EXPORT QDataStream& operator << (QDataStream& d, const QLineMark& m); + +struct QLineMarkType +{ + inline QLineMarkType() + : user(false), focus(false), priority(-1), persistency(0) + {} + + bool user; + bool focus; + QString id; + QPixmap icon; + QColor color; + int priority; + int persistency; + QStringList rules; +}; + +Q_DECLARE_METATYPE(QLineMarkType) + +typedef QList QLineMarkTypeList; + +Q_DECLARE_TYPEINFO(QLineMarkType, Q_MOVABLE_TYPE); + +class QCE_EXPORT QLineMarksInfoCenter : public QObject +{ + friend class QEditor; + friend class QCodeEdit; + + Q_OBJECT + + public: + static QLineMarksInfoCenter* instance(); + static void destroy(); + + QLineMarkList marks(const QString& file = QString()); + + QString markTypeId(int id); + int markTypeId(const QString& id); + + QLineMarkType markType(int id); + QLineMarkType markType(const QString& id); + + int priority(const QList& marks); + QString priority(const QStringList& marks); + + QStringList availableMarkTypes(const QString& context = QString()); + QList marksLayout(const QString& context = QString()); + + public slots: + void loadMarks(const QString& f); + void saveMarks(const QString& f); + + void loadMarkTypes(const QString& f); + + void clear(); + + void removeMarks(const QString& file); + + void addLineMark(const QLineMark& mark); + void toggleLineMark(const QLineMark& mark); + void removeLineMark(const QLineMark& mark); + + void addLineMark(const QLineMarkHandle& mark); + void toggleLineMark(const QLineMarkHandle& mark); + void removeLineMark(const QLineMarkHandle& mark); + + void flush(const QString& file); + + signals: + void lineMarkAdded(const QLineMark& mark); + void lineMarkRemoved(const QLineMark& mark); + + protected: + QLineMarksInfoCenter(); + virtual ~QLineMarksInfoCenter(); + + protected slots: + void cursorMoved(QEditor *e); + void lineDeleted(QDocumentLineHandle *h); + void markChanged(const QString& f, QDocumentLineHandle *h, int mark, bool on); + + private: + QLineMarkList m_delayed; + QLineMarkHandleList m_lineMarks; + QLineMarkTypeList m_lineMarkTypes; + + static QLineMarksInfoCenter *m_instance; +}; + +#endif // !_QLINE_MARKS_INFO_CENTER_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qnfa/light_vector.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qnfa/light_vector.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _LIGHT_VECTOR_H_ +#define _LIGHT_VECTOR_H_ + +#include + +/*! + \file light_vector.h + \brief Definition of the light_vector class +*/ + +template +class light_vector +{ + public: + light_vector() : m_data(0), size(0) {} + ~light_vector() { free(m_data); } + + light_vector& operator = (const light_vector& o) + { + size = o.size; + m_data = o.m_data; + + return *this; + } + + light_vector& operator << (const T& v) + { + append(v); + + return *this; + } + + inline quint16 length() const + { return size; } + + inline quint16 count() const + { return size; } + + inline T* data() + { return m_data; } + + void alloc(int pos, size_t n) + { + size += n; + m_data = !m_data ? (T*)malloc(size * sizeof(T)) : (T*)realloc(m_data, size * sizeof(T)); + + for ( int i = size - 1; (i > pos) && (i >= (int)n); --i ) + m_data[i] = m_data[i - n]; + + //for ( int i = pos; (i < (pos + n)) && ((i + n) < size); ++i ) + // m_data[i + n] = m_data[i]; + + } + + inline void prepend(const T& v) + { + insert(0, v); + } + + void insert(int i, const T& v) + { + i = qBound(0, i, (int)size); + + alloc(i, 1); + m_data[i] = v; + } + + void append(const T& v) + { + ++size; + m_data = !m_data ? (T*)malloc(size * sizeof(T)) : (T*)realloc(m_data, size * sizeof(T)); + m_data[size - 1] = v; + } + + inline const T& at(quint16 i) + { + return *(m_data + i); + } + + inline T& operator [] (quint16 i) + { + return *(m_data + i); + } + + bool contains(const T& v) const + { + for ( int i = 0; i < size; i++ ) + if ( m_data[i] == v ) + return true; + + return false; + } + + private: + T* m_data; + quint16 size; +}; + +#endif // _LIGHT_VECTOR_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qnfa/qnfa.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qnfa/qnfa.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,1039 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qnfa.h" + +/*! + \file qnfa.cpp + \brief Implementation of the core QNFA syntax engine +*/ + +#include +#include + +quint32 QNFA::_count = 0; +static QList _deleted; + +QNFA::QNFA() + : type(Char), assertion(0), actionid(0) +{ + out.next = 0; + + ++_count; + + //qDebug("alloc(0x%x) => QNFA syntax[%i];", this, _count); +} + +QNFA::~QNFA() +{ + --_count; + + // some nfa nodes are shared... gotta make sure they are free'd once only + _deleted << this; + + //qDebug("free(0x%x) => QNFA syntax[%i];", this, _count); + + tree.clear(); + + if ( (type & CxtBeg) && out.branch ) + { + delete out.branch; + out.branch = 0; + } + + if ( out.next && !_deleted.contains(out.next) ) + { + delete out.next; + out.next = 0; + } +} + + +QNFABranch::~QNFABranch() +{ + //qDebug("branch to %i nodes", count()); + for ( int i = 0; i < count(); ++i ) + { + if ( at(i) && !_deleted.contains(at(i)) ) + { + delete (*this)[i]; + (*this)[i] = 0; + } + } +} + +inline bool isWord(QChar c) +{ return c.isLetterOrNumber() || (c.unicode() == '_'); } + +static bool match(QChar cc, QNFA *chain) +{ + bool found = true; + quint16 cu = cc.unicode(); + bool notEmpty = chain->c.count(); + + if ( notEmpty && (chain->c.at(0) == '\0') ) + found = false; + + if ( notEmpty ) + if ( chain->c.contains(cu) ) + return found; + + int ass = chain->assertion; + + if ( ass ) + { + if ( cc.isDigit() ) + { + if ( ass & Digit ) + return found; + } else { + if ( ass & NonDigit ) + return found; + + if ( cc.isSpace() ) + { + if ( ass & Space ) + return found; + } else { + if ( ass & NonSpace ) + return found; + + if ( cc.isLetterOrNumber() || (cu == '_') ) + { + if ( ass & Word ) + return found; + } else { + if ( ass & NonWord ) + return found; + } + } + } + } + + return !found; +} + +void match(QNFAMatchContext *lexer, const QChar *d, int length, QNFAMatchNotifier notify) +{ + if ( !lexer || !lexer->context ) + { + //qWarning("get off you scum!"); + return; + } + + // restore message buffering + + notify.clear(); + + int olvls = lexer->parents.count(), + nlvls = 0, + lvls = olvls; + + if ( lvls ) + notify.startBuffering(); + + // + + quint16 c = 0; + const QChar *di = d; + QNFA *chain = 0, *start = 0; + int index = 0, lastCxt = 0, len, idx; + bool bFound, bEscape = false, bEscaped = false; + bool wPrev = false, wCur = false; + + while ( index < length ) + { + bFound = false; + bEscaped = false; + //bEscape &= !lexer->meaningless.contains(d[index].unicode()); + + //while ( lexer->meaningless.contains(d[index].unicode()) && ((index + 1) < length) ) + // ++index; + + if ( index >= length ) + break; + + c = di->unicode(); + + wCur = isWord(*di); + + int plainIndex = -1, plainMatch, plainLength; + + // try fast plain matching + if ( !(wPrev && wCur) ) + { + //qDebug("trying plain..."); + + //len = 0; + idx = index; + QCharTree::const_iterator it, match, end; + + it = lexer->context->tree.constFind(c); + + if ( it != lexer->context->tree.constEnd() ) + { + //qDebug("plain on %c", c); + do + { + ++di; + ++idx; + + end = it->next.constEnd(); + match = it->next.constFind(0); + + if ( idx < length ) + { + c = di->unicode(); + it = it->next.constFind(c); + } else { + it = end; + } + + if ( it == end ) + { + if ( (match != end) && !isWord(*di) ) + { + //word boundary found + // corresponding token end found + wPrev = isWord(*(di - 1)); + bFound = true; + if ( match->value.action & 0x40000000 ) + { + // try regexps before notifying + plainIndex = index; + plainLength = idx - index; + plainMatch = match->value.action; + //qDebug("ambiguity."); + } else { + notify(index, idx - index, match->value.action); + index = idx; + } + //qDebug("next step : %c", d[index].toLatin1()); + //bMonitor = true; + } + + break; + } + } while ( idx < length ) ; + + if ( bFound ) + { + bEscape = false; + + if ( plainIndex == -1 ) + continue; + + bFound = false; + } + + di -= idx - index; + } + } + + // fallback on regexp-like NFA-based matching + QNFABranch* children = lexer->context->out.branch; + + if ( children ) + { + //qDebug("trying %i sub nfas on %c", children->count(), d[index].toLatin1()); + int max = children->count(); + + for ( quint16 i = 0; i < max; ++i ) + { + len = 0; + idx = index; + start = chain = children->at(i); + + //qDebug("%ith attempt on %c", i, d[index + len].toLatin1()); + + while ( (idx < length) || (chain->type & Match) ) + { + bEscaped = false; + + if ( chain->type & Match ) + { + if ( + (chain->assertion & WordEnd) + && + (idx < length) + && + isWord(*di) + && + isWord(*(di - 1)) + ) + { + //qDebug("end assertion failed..."); + break; + } + + //qDebug("matched to end"); + + if ( chain->type & CxtBeg ) + { + //qDebug("entering context : 0x%x", chain); + + ++nlvls; + + bool notifySub = notify.bufferLevel(); + + if ( notifySub ) + { + // pop one message buffer + notify.stopBuffering(); + } + + // notify content of previous context until nest + notify(lastCxt, index - lastCxt, lexer->context->actionid | 0x80000000); + + if ( notifySub ) + { + // notify sub matches so far to avoid tricky handling later on + notify.flush(); + + //notify.startBuffering(); + } + + // notify begin marker + notify(index, len, start->actionid ? start->actionid : chain->actionid); + + // update context stack + lexer->parents.push(lexer->context); + lexer->context = chain; + + // update nest index + lastCxt = idx; + + // push a message buffer + notify.startBuffering(); + + } else if ( chain->type & CxtEnd ) { + //qDebug("leaving context :"); + + if ( lexer->parents.isEmpty() ) + qFatal("context nesting problem"); + + if ( bEscape ) + { + // not really end : escape found... + + bEscape = false; + bEscaped = true; + } else { + + if ( nlvls ) + --nlvls; + else + --lvls; + + // pop one message buffer + notify.stopBuffering(); + + // notify context content from last nest + notify(lastCxt, index - lastCxt, lexer->context->actionid | 0x80000000); + + // flush sub matches + notify.flush(); + + // update context stack + lexer->context = lexer->parents.pop(); + + if ( lexer->parents.count() ) + notify.startBuffering(); + + // update nest index + lastCxt = idx; + + // notify end marker + notify(index, len, chain->actionid); + + //qDebug("cxt notif..."); + + if ( chain->type & Exclusive ) + index = idx; + + --index; + --di; + + bFound = true; + break; + } + } else if ( chain->type & CxtEsc ) { + //qDebug("matched %s", qPrintable(QString(index, len))); + + //notify(index, len, chain->actionid); + bEscape = !bEscape; + } else { + //qDebug("matched %s", qPrintable(QString(d + index, len))); + + if ( plainIndex != -1 && plainLength >= len ) + { + break; + } + + notify(index, len, chain->actionid); + bEscape = false; + } + + bFound = true; + index = idx; + --index; + --di; + + //qDebug("next step : %c", d[index + 1].toLatin1()); + //bMonitor = true; + + break; + } else { + // "regular" nfa match (no match yet...) + + if ( + (chain->assertion & WordStart) + && + (idx >= 1) + && + ( + isWord(*(di - 1)) + && + isWord(*di) + ) + ) + { + //qDebug("beg assertion failed..."); + + break; + } + + QChar cc = *di; + bool found = match(cc, chain); + + if ( + !(chain->assertion & ZeroOrOne) + && + !(chain->assertion & ZeroOrMore) + && + !found + ) + { + //if ( cc.toLatin1() == ')' ) + // qDebug("mismatch : %c != %c", cc.toLatin1(), chain->c.at(0)); + + break; + } + + if ( found ) + { + //qDebug("%c", d[index + len].toLatin1()); + + if ( + (chain->assertion & OneOrMore) + || + (chain->assertion & ZeroOrMore) + ) + { + do + { + ++di; + ++len; + ++idx; + } while ( + (idx < length) + && + match(*di, chain) + ); + + } else { + ++len; + ++idx; + ++di; + } + + } else { + //qDebug("! %c", d[index + len].toLatin1()); + } + + chain = chain->out.next; + } + } + + if ( bFound ) + break; + + di -= len; + } + } + + if ( !bFound ) + { + if ( plainIndex != -1 ) + { + notify(plainIndex, plainLength, plainMatch); + index = plainIndex + plainLength; + di += plainLength; + continue; + } + + bEscape = false; + //++index; + wPrev = wCur; + } else { + wPrev = isWord(*di); + } + + ++index; + ++di; + } + + // flush messages + + if ( !notify.bufferLevel() ) + return; + + //qDebug("%i context nests", notify.bufferLevel()); + //qDebug("[%i;+00[ : 0x%x", lastCxt, lexer->context->actionid | 0x80000000); + + // pop down one buffer + notify.stopBuffering(); + + // notify overlapping context so far + notify(lastCxt, length - lastCxt, lexer->context->actionid | 0x80000000); + + // notify sub matches + notify.flush(); + + // make sure we leave a blank notifier... + notify.clear(); + + // preserve escape power... + if ( bEscaped ) + return; + + // some existing left AND new one(s) + if ( (olvls == lvls) && nlvls ) + ++lvls; + + // close stay-on-line contexts, if any + QStack::iterator it = lexer->parents.begin() + lvls; + + while ( it != lexer->parents.end() ) + { + if ( (*it)->type & StayOnLine ) + { + //qDebug("staid..."); + it = lexer->parents.erase(it); + } else { + ++it; + } + } + + if ( (lexer->context->type & StayOnLine) && nlvls && lexer->parents.count() ) + lexer->context = lexer->parents.pop(); + +} + +QNFA* lexer() +{ + QNFA *lex = new QNFA; + + lex->type = ContextBegin; + lex->out.branch = new QNFABranch; + + return lex; +} + +QNFA* sharedContext(const QString& start, QNFA *other, bool cs) +{ + QNFA *nfa, *end, *beg = sequence(start.constData(), start.length(), &end, cs); + + nfa = new QNFA; + nfa->type = ContextBegin; + nfa->out.branch = other->out.branch; + + end->out.next = nfa; + + return beg; +} + +QNFA* context(const QString& start, const QString& stop, const QString&, int action, QNFA **handler, bool cs) +{ + QNFA *nfa, *end, *beg = sequence(start.constData(), start.length(), &end, cs); + + nfa = new QNFA; + nfa->type = ContextBegin; + nfa->actionid = action; + nfa->out.branch = new QNFABranch; + + if ( handler ) + *handler = nfa; + //else + // qDebug("no handler set [0x%x]", nfa); + + end->out.next = nfa; + end = nfa; + + QNFA *endmark, *begendmark = sequence(stop.constData(), stop.length(), &endmark, cs); + + nfa = new QNFA; + nfa->type = ContextEnd; + nfa->actionid = action; + + endmark->out.next = nfa; + + //end->out->branch->append(endmark); + addNFA(end, begendmark); + + return beg; +} + +void addWord(QNFA *lexer, const QString& w, int action, bool cs) +{ + if ( !lexer || !(lexer->type & CxtBeg) || !lexer->out.branch ) + return; + + // try using the fastest way if possible + + QString pt; + + if ( plain(w, &pt) && cs ) + { + addWord(lexer->tree, pt, action, cs); + return; + } + + // fallback on (fast) regexp-like NFA-based semi-compiled parsing + QNFA *nfa, *word, *end; + + word = sequence(w.constData(), w.length(), &end, cs); + word->assertion |= WordStart; + + nfa = new QNFA; + nfa->type = Match; + nfa->assertion = WordEnd; + nfa->actionid = action; + + end->out.next = nfa; + + //lexer->out.branch->append(word); + addNFA(lexer, word); +} + +void addSequence(QNFA *lexer, const QString& w, int action, bool cs) +{ + if ( !lexer || !(lexer->type & CxtBeg) || !lexer->out.branch ) + { + return; + } + + QNFA *seq, *end, *nfa; + + seq = sequence(w.constData(), w.length(), &end, cs); + + nfa = new QNFA; + nfa->type = Match; + nfa->actionid = action; + + end->out.next = nfa; + + //lexer->out.branch->append(seq); + addNFA(lexer, seq); +} + +QNFA* sequence(const QChar *d, int length, QNFA **end, bool cs) +{ + QNFA *nfa, *set = 0, *prev = 0, *first = 0; + + for ( int i = 0; i < length; ++i ) + { + QChar c = d[i]; + + if ( c == QLatin1Char('\\') ) + { + c = d[++i]; + + if ( c == QLatin1Char('n') ) + { + c = '\n'; + } else if ( c == QLatin1Char('t') ) { + c = '\t'; + } else if ( c == QLatin1Char('r') ) { + c = '\r'; + } + + if ( set ) + { + set->c << c.unicode(); + } else { + nfa = new QNFA; + nfa->c << c.unicode(); + + if ( prev ) + prev->out.next = nfa; + + prev = nfa; + } + } else if ( c == QLatin1Char('$') ) { + // char classes + c = d[++i]; + + if ( set ) + { + if ( c == QLatin1Char('s') ) + set->assertion |= Space; + else if ( c == QLatin1Char('S') ) + set->assertion |= NonSpace; + else if ( c == QLatin1Char('d') ) + set->assertion |= Digit; + else if ( c == QLatin1Char('D') ) + set->assertion |= NonDigit; + else if ( c == QLatin1Char('w') ) + set->assertion |= Word; + else if ( c == QLatin1Char('W') ) + set->assertion |= NonWord; + else + set->c << QLatin1Char('$').unicode() << c.unicode(); + + } else { + nfa = new QNFA; + + if ( c == QLatin1Char('s') ) + nfa->assertion |= Space; + else if ( c == QLatin1Char('S') ) + nfa->assertion |= NonSpace; + else if ( c == QLatin1Char('d') ) + nfa->assertion |= Digit; + else if ( c == QLatin1Char('D') ) + nfa->assertion |= NonDigit; + else if ( c == QLatin1Char('w') ) + nfa->assertion |= Word; + else if ( c == QLatin1Char('W') ) + nfa->assertion |= NonWord; + else { + nfa->c << QLatin1Char('$').unicode(); + --i; + } + + if ( prev ) + prev->out.next = nfa; + + prev = nfa; + } + } else if ( c == QLatin1Char('[') ) { + + if ( set ) + { + set->c << c.unicode(); + // qWarning("Nested sets are not supported (and useless BTW)..."); + continue; + } + + // enter set... + + set = new QNFA; + + //qDebug("set start"); + + } else if ( c == QLatin1Char(']') ) { + + if ( !set ) + { + qWarning("Unmatched set closing marker"); + continue; + } + + // leave set... + + if ( prev ) + prev->out.next = set; + + prev = set; + set = 0; + + //qDebug("set end"); + /* + } else if ( c == QLatin1Char('(') ) { + // allow trivial groups + + QList cuts; + int idx = i, nest = 1; + + while ( nest && (++idx < length) ) + { + if ( d[idx] == '\\' ) + { + ++idx; + continue; + } else if ( d[idx] == '(' ) { + ++nest; + } else if ( d[idx] == ')' ) { + --nest; + } else if ( (nest == 1) && (d[idx] == '|') ) { + cuts << idx; + } else if ( d[idx] == '[' ) { + while ( ++idx < length ) + { + if ( d[idx] == '\\' ) + { + ++idx; + continue; + } else if ( d[idx] == ']' ) { + break; + } + } + } + } + + */ + } else if ( set ) { + + if ( (c == QLatin1Char('^')) && !set->c.count() ) + { + set->c << '\0'; + continue; + } + + quint16 prev = set->c.count() ? set->c.at(set->c.length() - 1) : '\0'; + + if ( (c == '-') && (prev != '\0') && ((i + 1) < length) ) + { + quint16 cse = d[++i].unicode(); + + for ( quint16 csi = prev + 1; csi <= cse; ++csi ) + { + QChar csc(csi); + + if ( c.isLetter() && !cs ) + set->c << c.toLower().unicode() << c.toUpper().unicode(); + else + set->c << csi; + } + } else { + if ( c.isLetter() && !cs ) + set->c << c.toLower().unicode() << c.toUpper().unicode(); + else + set->c << c.unicode(); + } + //qDebug("set << %c", c.toLatin1()); + + } else if ( c == QLatin1Char('+') ) { + if ( prev ) prev->assertion |= OneOrMore; + } else if ( c == QLatin1Char('*') ) { + if ( prev ) prev->assertion |= ZeroOrMore; + } else if ( c == QLatin1Char('?') ) { + if ( prev ) prev->assertion |= ZeroOrOne; + } else { + nfa = new QNFA; + + if ( c.isLetter() && !cs ) + { + nfa->c << c.toLower().unicode() << c.toUpper().unicode(); + } else { + nfa->c << c.unicode(); + } + + if ( prev ) + prev->out.next = nfa; + + prev = nfa; + } + + if ( !first ) + first = prev; + } + + if ( end ) + { + *end = prev; + } + + return first; +} + +bool plain(const QString& word, QString *dest) +{ + if ( dest ) + dest->clear(); + + for ( int i = 0; i < word.length(); i++ ) + { + QChar c = word.at(i); + + if ( c == QLatin1Char('\\') ) + { + if ( dest && ((i + 1) < word.length()) ) + { + c = word.at(++i); + + if ( c == QLatin1Char('n') ) + dest->append('\n'); + else if ( c == QLatin1Char('t') ) + dest->append('\t'); + else if ( c == QLatin1Char('r') ) + dest->append('\r'); + else + dest->append(c); + } + } else if ( + c == QLatin1Char('[') + || + c == QLatin1Char(']') + || + c == QLatin1Char('+') + || + c == QLatin1Char('*') + || + c == QLatin1Char('?') + || + c == QLatin1Char('$') + ) + { + if ( dest ) + dest->clear(); + + return false; + } else { + + if ( dest ) + dest->append(c); + + } + } + + return true; +} + +void addWord(QCharTree& tree, const QString& w, int action, bool cs) +{ + //qDebug("Adding word to char tree : %s", qPrintable(w)); + + if ( cs ) + { + quint16 u = w.at(0).unicode(); + QCharTree::iterator it = tree.find(u), tmp; + + if ( it == tree.end() ) + it = tree.insert(u, QCharTreeNode(u)); + + for ( int i = 1; i < w.count(); i++ ) + { + u = w.at(i).unicode(); + + //qDebug("char %c", w.at(i).toLatin1()); + + tmp = it->next.find(u); + + if ( tmp == it->next.end() ) + tmp = it->next.insert(u, QCharTreeNode(u)); + + it = tmp; + } + + // add action handler + QCharTreeNode node; + node.value.action = action; + + it->next[0] = node; + } else if ( 0 ) { + QChar c = w.at(0); + quint16 u = c.unicode(); + + QCharTree::iterator it, tmp; + QList l, ltmp; + + if ( c.isLetter() ) + { + u = c.toLower().unicode(); + tmp = tree.find(u); + + if ( tmp == tree.end() ) + tmp = tree.insert(u, QCharTreeNode(u)); + + l << tmp; + + u = c.toUpper().unicode(); + tmp = tree.find(u); + + if ( tmp == tree.end() ) + tmp = tree.insert(u, QCharTreeNode(u)); + + l << tmp; + } else { + tmp = tree.find(u); + + if ( tmp == tree.end() ) + tmp = tree.insert(u, QCharTreeNode(u)); + + l << tmp; + } + + for ( int i = 1; i < w.count(); ++i ) + { + c = w.at(i); + QList lc; + + if ( c.isLetter() ) + lc << c.toLower() << c.toUpper(); + else + lc << c; + + foreach ( c, lc ) + { + u = c.unicode(); + + foreach ( it, l ) + { + tmp = it->next.find(u); + + if ( tmp == it->next.end() ) + tmp = it->next.insert(u, QCharTreeNode(u)); + + ltmp << tmp; + } + } + + l = ltmp; + } + + // add action handler + QCharTreeNode node; + node.value.action = action; + + foreach ( it, l ) + it->next[0] = node; + } +} + +void squeeze(QNFA *nfa) +{ + squeeze(nfa->tree); + + if ( nfa->type & Match ) + { + if ( nfa->out.branch ) + for ( int i = 0; i < nfa->out.branch->count(); ++i ) + squeeze(nfa->out.branch->at(i)); + + } else if ( nfa->out.next ) { + squeeze(nfa->out.next); + } +} + +void squeeze(QCharTreeLevel& lvl) +{ + lvl.squeeze(); + + QCharTreeLevel::iterator it = lvl.begin(); + + while ( it != lvl.end() ) + squeeze((it++)->next); +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qnfa/qnfa.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qnfa/qnfa.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _Q_NFA_H_ +#define _Q_NFA_H_ + +/*! + \file qnfa.h + \brief Definition of the core QNFA syntax engine +*/ + +#include +#include +#include +#include +#include + +#include "light_vector.h" + +struct QNFA; + +typedef light_vector QNFASet; + +class QNFABranch : public light_vector +{ + public: + ~QNFABranch(); +}; + +enum NFAType +{ + Char = 0, + + Match = 1, + + CxtBeg = 2, + CxtEnd = 4, + CxtEsc = 8, + + ContextBegin = Match | CxtBeg, + ContextEnd = Match | CxtEnd, + EscapeSeq = Match | CxtEsc, + + Escaped = 16, + Exclusive = 32, + StayOnLine = 64, + + Reserved = 128 +}; + +enum NFAAssertion +{ + NoAssertion = 0, + + One = 0, // default standard + ZeroOrOne = 1, // ? + ZeroOrMore = 2, // * + OneOrMore = 4, // + + + WordStart = 8, + WordEnd = 16, + + Word = 32, + NonWord = 64, + + Digit = 128, + NonDigit = 256, + + Space = 512, + NonSpace = 1024, + + CaseSensitive = 2048 +}; + +struct QCharTreeNode; + +typedef QHash QCharTreeLevel; + +struct QCharTreeNode +{ + inline QCharTreeNode(quint16 v = 0) { value.unicode = v; } + inline QCharTreeNode(const QCharTreeNode& o) { value = o.value; next = o.next; } + + union + { + int action; + quint16 unicode; + } value; + + QCharTreeLevel next; +}; + +Q_DECLARE_TYPEINFO(QCharTreeNode, Q_MOVABLE_TYPE); + +typedef QCharTreeLevel QCharTree; + +struct QNFA +{ + QNFA(); + ~QNFA(); + + QNFASet c; + QCharTree tree; + + union + { + QNFA *next; + QNFABranch *branch; + } out; + + quint8 type; + quint16 assertion; + + int actionid; + + static quint32 _count; +}; + +struct QNFAMatchContext +{ + inline QNFAMatchContext(QNFA *root = 0) : context(root) {} + + inline QNFAMatchContext& operator = (QNFAMatchContext *c) + { + if ( c ) + { + context = c->context; + parents = c->parents; + meaningless = c->meaningless; + } else { + reset(); + } + + return *this; + } + + inline QNFAMatchContext& operator = (const QNFAMatchContext& c) + { + context = c.context; + parents = c.parents; + meaningless = c.meaningless; + + return *this; + } + + inline void reset() + { + context = 0; + + while ( parents.count() ) + context = parents.pop(); + } + + QNFA *context; + QNFASet meaningless; + QStack parents; +}; + +class QNFAMatchHandler +{ + public: + virtual ~QNFAMatchHandler() {} + + virtual void matched(int pos, int len, int action) = 0; +}; + +class QNFAMatchNotifier +{ + private: + struct Command + { + inline Command(int p, int len, int act) + : pos(p), length(len), action(act) {} + + int pos; + int length; + int action; + }; + + typedef QList CommandList; + + public: + inline QNFAMatchNotifier(QNFAMatchHandler *h) + : handler(h) {} + + inline QNFAMatchNotifier& operator = (const QNFAMatchNotifier& n) + { + handler = n.handler; + + return *this; + } + + inline void operator () (int pos, int len, int action) + { + if ( handler && (m_buffers.isEmpty() || m_pending.count()) ) + handler->matched(pos, len, action); + else + m_buffers.top() << Command(pos, len, action); + } + + inline int bufferLevel() const + { + return m_buffers.count(); + } + + inline void startBuffering() + { + m_buffers.push(CommandList()); + } + + inline void stopBuffering() + { + m_pending = m_buffers.pop(); + } + + inline void flush() + { + foreach ( Command c, m_pending ) + handler->matched(c.pos, c.length, c.action); + + m_pending.clear(); + } + + inline void clear() + { + m_pending.clear(); + m_buffers.clear(); + } + + private: + QNFAMatchHandler *handler; + + CommandList m_pending; + QStack m_buffers; +}; + +void match( QNFAMatchContext *lexer, + const QChar *d, + int length, + QNFAMatchNotifier notify); + +inline void match( QNFAMatchContext *lexer, + const QString& s, + QNFAMatchNotifier notify) +{ match(lexer, s.constData(), s.length(), notify); } + +QNFA* lexer(); + +void squeeze(QNFA *nfa); +void squeeze(QCharTreeLevel& lvl); + +QNFA* sharedContext(const QString& start, + QNFA *other, + bool cs); + +QNFA* context( const QString& start, + const QString& stop, + const QString& escape, + int action, + QNFA **handler = 0, + bool cs = true); + +inline void addNFA(QNFA *context, QNFA *nfa) +{ context->out.branch->append(nfa); } + +bool plain(const QString& word, QString *dest); + +void addWord( QCharTree& tree, + const QString& w, + int action, + bool cs); + +void addWord( QNFA *lexer, + const QString& w, + int action, + bool cs); + +void addSequence( QNFA *lexer, + const QString& w, + int action, + bool cs); + +QNFA* sequence( const QChar *d, + int length, + QNFA **end, + bool cs); + +inline QNFA* sequence( + const QString& s, + QNFA **end, + bool cs) +{ return sequence(s.constData(), s.length(), end, cs); } + +#endif //!_Q_NFA_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qnfa/qnfadefinition.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qnfa/qnfadefinition.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,1804 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qnfadefinition.h" + +/*! + \file qnfadefinition.cpp + \brief Implementation of the QNFADefinition class. +*/ + +#include "qnfa.h" + +#include "qformatscheme.h" + +#include "qlinemarksinfocenter.h" + +#include "qdocument.h" +#include "qdocument_p.h" +#include "qdocumentline.h" +#include "qdocumentcursor.h" + +#include +#include + +#include +#include +#include + +uint qHash(const QPointer& p) { return qHash((QDocument*)(p)); } + +class QNFANotifier : public QNFAMatchHandler +{ + public: + QNFANotifier(const QString& line) + { + m_formats.fill(0, line.length()); + } + + QNFANotifier(const QDocumentLine& line) + : m_line(line) + { + m_formats.fill(0, line.length()); + } + + ~QNFANotifier() + { + if ( m_line.isValid() ) + { + m_line.setFormats(m_formats); + m_line.setParentheses(m_parens); + } + } + + const QVector& formats() const + { + return m_formats; + } + + const QVector& parentheses() const + { + return m_parens; + } + + virtual void matched(int pos, int len, int action) + { + //qDebug("action 0x%x on [%s]", action, qPrintable(m_line.text().mid(pos, len))); + + if ( !len ) + return; + + //qDebug("matched \"%s\"", qPrintable(m_line.text().mid(pos, len))); + + if ( + !(action & QNFAAction::Content) + && + ( + action + & + ( + QNFAAction::ParenOpen + | + QNFAAction::ParenClose + | + QNFAAction::Indent + | + QNFAAction::Fold + ) + ) + ) + { + //qDebug("matched paren : %s", qPrintable(m_line.text().mid(pos, len))); + + QParenthesis par( + (action & QNFAAction::ParenMask) >> 8, + 0, + pos, + len + ); + + if ( action & QNFAAction::ParenOpen ) + par.role |= QParenthesis::Open; + + if ( action & QNFAAction::ParenClose ) + par.role |= QParenthesis::Close; + + if ( action & QNFAAction::MatchParen ) + par.role |= QParenthesis::Match; + + if ( action & QNFAAction::Indent ) + par.role |= QParenthesis::Indent; + + if ( action & QNFAAction::Fold ) + par.role |= QParenthesis::Fold; + + m_parens << par; + } + + if ( action & QNFAAction::Highlight ) + { + int i = qMax(pos, 0); + int max = qMin(i + len, m_formats.count()); + + while ( i < max ) + m_formats[i++] = action & QNFAAction::FormatMask; + } + } + + private: + QDocumentLine m_line; + QVector m_formats; + QVector m_parens; +}; + +extern QString *_singleLineCommentTarget; +void embed(QNFA *src, QNFA *dest, int index); +void fillContext(QNFA *cxt, QDomElement e, QFormatScheme *f, QHash& pids, bool cs); + +static inline bool match(const QParenthesis& open, const QParenthesis& close) +{ + return open.id == close.id; +} + +#if 0 +class QNFAMatcher : public QMatcherInterface +{ + public: + QNFAMatcher(QNFADefinition *d, bool indent = false) + : m_definition(d), m_indentFold(indent) + { + + } + + virtual void match(const QDocumentCursor& c, QMatcher *m) + { + //qDebug("trying to match..."); + + QDocument *d = c.document(); + QDocumentLine b = c.line(); + + if ( !d || !b.isValid() ) + return; + + int pos = c.columnNumber(); + const QVector& m_parens = b.parentheses(); + + if ( m_parens.isEmpty() ) + return; + + QParenthesis p; + + m->clearMatches(); + + foreach ( p, m_parens ) + { + if ( (pos != p.offset) && (pos != (p.offset + p.length)) ) + continue; + + int line = c.lineNumber(), + beg = -1, + end = -1, + beglen = 0, + endlen = 0; + + if ( + (p.role & QParenthesis::Open) + && + (p.role & QParenthesis::Match) + ) + { + beg = p.offset; + beglen = p.length; + endlen = matchOpen(d, line, p, end); + + if ( endlen ) + m->addMatch(c.lineNumber(), beg, beglen, line, end, endlen); + + } else if ( + (p.role & QParenthesis::Close) + && + (p.role & QParenthesis::Match) + ) + { + end = p.offset; + endlen = p.length; + beglen = matchClose(d, line, p, beg); + + if ( beglen ) + m->addMatch(line, beg, beglen, c.lineNumber(), end, endlen); + + } + } + } + + virtual QChar autoClose(QChar) + { + return QChar(); + } + + virtual int blockFlags(int b, int depth, QMatcher *m) + { + if ( m_indentFold ) + { + QDocument *d = m->document(); + + QDocumentLine + prev = d->line(b - 1), + curr = d->line(b), + next = d->line(b + 1); + + if ( curr.hasFlag(QDocumentLine::CollapsedBlockStart) ) + return QMatcher::Collapsed; + + int id = m_indentGuess.value(d, 0), + cc = curr.firstChar(), + pc = prev.firstChar(), + nc = next.firstChar(); + + int k = b; + + do { + prev = d->line(--k); + pc = prev.firstChar(); + } while ( prev.isValid() && (pc == -1) ); + + k = b; + + do { + next = d->line(++k); + nc = next.firstChar(); + } while ( next.isValid() && (nc == -1) ); + + if ( !id && (depth > 0) && (pc > 0) ) + { + id = pc / depth; + m_indentGuess[d] = id; + } + + if ( cc != -1 ) + { + if ( nc > cc ) + return QMatcher::Collapsible; + + if ( nc < cc ) + { + if ( !id && depth ) + { + id = cc / depth; + m_indentGuess[d] = id; + } + + //qDebug("cc=%i; nc=%i; id=%i => do=%i", cc, nc, id, (cc - nc) / id); + return QMatcher::Closure | ((cc - nc) / id); + } + } + /* + if ( (nc != cc) && (nc <= 0) && depth ) + { + return QMatcher::Closure | depth; + }*/ + } + + return collapseState(b, m, 0); + } + + int collapseState(int ln, QMatcher *m, QParenthesis *l) + { + Q_UNUSED(m) + + QDocumentLine b = m->document()->line(ln); + + if ( !b.isValid() ) + return QMatcher::None; + + int open = 0; + + if ( b.hasFlag(QDocumentLine::CollapsedBlockStart) ) + return QMatcher::Collapsed; + + foreach ( QParenthesis p, b.parentheses() ) + { + if ( !(p.role & QParenthesis::Fold) ) + continue; + + if ( p.role & QParenthesis::Open ) + ++open; + else + --open; + + if ( l ) + *l = p; + } + + if ( open > 0 ) + return QMatcher::Collapsible; + else if ( open < 0 ) + return QMatcher::Closure | qAbs(open); + + return QMatcher::None; + } + + virtual void expand(int ln, QMatcher *m) + { + Q_UNUSED(m) + + QDocument *d = m->document(); + QDocumentLine b = d->line(ln); + + if ( !b.isValid() || !b.hasFlag(QDocumentLine::CollapsedBlockStart) ) + return; + + int depth = 1, + count = 1, + indent = b.firstChar() / m_indentGuess.value(d, 1); + + QDocumentLine l = d->line(ln + count); + + while ( l.isValid() ) + { + if ( depth == 1 ) + l.setFlag(QDocumentLine::Hidden, false); + + if ( l.hasFlag(QDocumentLine::CollapsedBlockStart) ) + { + ++depth; + } else if ( l.hasFlag(QDocumentLine::CollapsedBlockEnd) ) { + int flags = blockFlags(ln + count, depth + indent, m); + int doff = qMax(1, flags & QMatcher::DataMask); + + if ( (depth == 1) && (doff > 0) ) + l.setFlag(QDocumentLine::CollapsedBlockEnd, false); + + depth -= doff; + + //qDebug("depth offset = %i > depth = %i", doff, depth); + } + + if ( depth <= 0 ) + break; + + ++count; + l = d->line(ln + count); + } + + if ( !l.isValid() ) + { + --count; + l = d->line(ln + count); + l.setFlag(QDocumentLine::CollapsedBlockEnd, false); + } + + b.setFlag(QDocumentLine::CollapsedBlockStart, false); + d->impl()->showEvent(ln, count); + + d->impl()->setHeight(); + d->impl()->emitFormatsChanged(); + } + + virtual void collapse(int ln, QMatcher *m) + { + QDocument *d = m->document(); + QDocumentLine b = d->line(ln); + + if ( !b.isValid() || b.hasFlag(QDocumentLine::CollapsedBlockStart) ) + return; + + int count = 1, + indent = 0; + + QVector lp = b.parentheses(); + + while ( lp.count() ) + { + QParenthesis p = lp.first(); + + if ( (p.role & QParenthesis::Open) && (p.role & QParenthesis::Fold) ) + { + QParenthesis par; + int depth = 1, cd = 0; + QDocumentLine block = d->line(ln + count); + + if ( !b.isValid() ) + return; + + while ( block.isValid() ) + { + const int state = collapseState(ln + count, m, &par); + + if ( state & QMatcher::Closure ) + { +// qDebug("at line %i depth fall by %i", ln + count, state & QMatcher::DataMask); + depth -= state & QMatcher::DataMask; + + if ( depth <= 0 ) + break; + + } else if ( state & (QMatcher::Collapsible | QMatcher::Collapsed) ) { + ++depth; + } + + ++count; + block = d->line(ln + count); + } + + if ( !block.isValid() ) + { + // fold to END for malformed blocks or syntax that allow it + + --count; + block = d->line(ln + count); + } + + b.setFlag(QDocumentLine::CollapsedBlockStart); + + for ( int i = ln + 1; i <= ln + count; ++i ) + { + d->line(i).setFlag(QDocumentLine::Hidden); + } + + block.setFlag(QDocumentLine::Hidden); + block.setFlag(QDocumentLine::CollapsedBlockEnd); + + d->impl()->hideEvent(ln, count); + d->impl()->setHeight(); + + d->impl()->emitFormatsChanged(); + return; + } + + lp.pop_front(); + } + + + if ( !m_indentFold ) + return; + + int i = ln + 1, k; + QDocumentLine block = d->line(i); + + if ( !block.isValid() ) + return; + + indent = block.firstChar(); + + do + { + block = d->line(++i); + k = block.firstChar(); + } while ( block.isValid() && ((k >= indent) || (k == -1)) ); + + if ( k < indent ) + { + block = d->line(--i); + k = block.firstChar(); + } + + while ( block.isValid() && (i > ln) && (k == -1) ) + { + block = d->line(--i); + k = block.firstChar(); + } + + b.setFlag(QDocumentLine::CollapsedBlockStart); + + //qDebug("hidding from %i to %i", ln + 1, i); + + for ( int j = ln + 1; j <= i; ++j ) + { + d->line(j).setFlag(QDocumentLine::Hidden); + } + + d->line(i).setFlag(QDocumentLine::CollapsedBlockEnd); + + d->impl()->hideEvent(ln, i - ln); + d->impl()->setHeight(); + + d->impl()->emitFormatsChanged(); + } + + protected: + int matchOpen(QDocument *d, int& line, QParenthesis p, int& end) + { + int pos = p.offset; + QVector m_parens = d->line(line).parentheses(); + + bool bMatch = false; + QParenthesis par; + QStack parens; + + forever + { + foreach ( par, m_parens ) + { + if ( par.offset < pos ) + continue; + + if ( par.role & QParenthesis::Open ) + { + parens.push(par); + } else if ( par.role & QParenthesis::Close ) { + if ( (bMatch = ::match(parens.top(), par)) ) + { + bMatch &= parens.count() == 1; + + if ( bMatch ) + break; + else if ( parens.count() ) + parens.pop(); + else + qWarning("bad paren nesting..."); + } else { + return 0; + } + } + } + + if ( bMatch ) + break; + + pos = 0; + ++line; + + QDocumentLine b = d->line(line); + + if ( !b.isValid() ) + return 0; + + m_parens = b.parentheses(); + } + + end = par.offset; + + return par.length; + } + + int matchClose(QDocument *d, int& line, QParenthesis p, int& beg) + { + int pos = p.offset; + QVector m_parens = d->line(line).parentheses(); + + QParenthesis par; + bool bMatch = false; + QStack parens; + + forever + { + for ( int i = m_parens.count() - 1; i >= 0; --i ) + { + par = m_parens.at(i); + + if ( par.offset > pos ) + continue; + + if ( par.role & QParenthesis::Close ) + { + parens.push(par); + } else if ( par.role & QParenthesis::Open ) { + if ( (bMatch = ::match(par, parens.top())) ) + { + bMatch &= parens.count() == 1; + + if ( bMatch ) + break; + else if ( parens.count() ) + parens.pop(); + else + qWarning("bad paren nesting..."); + } else { + return 0; + } + } + } + + if ( bMatch ) + break; + + --line; + + QDocumentLine b = d->line(line); + pos = b.length(); + + if ( !b.isValid() ) + return 0; + + m_parens = b.parentheses(); + } + + beg = par.offset; + + return par.length; + } + + private: + QNFADefinition *m_definition; + bool m_indentFold; + QHash m_indentGuess; +}; + +class QNFAIndenter : public QIndenterInterface +{ + public: + QNFAIndenter(QNFADefinition *d) + : m_definition(d) + { + + } + + virtual QString indent(const QDocumentCursor& c, QIndenter *i) + { + Q_UNUSED(i) + + if ( c.isNull() || c.line().isNull() ) + return QString(); + + QDocumentLine b = c.line(); + int pos, max = qMin(c.columnNumber(), b.text().size()); + + QString s = b.text().left(max); + + //qDebug("line %i, column %i : %s", b.lineNumber(), c.columnNumber(), qPrintable(s)); + + for ( pos = 0; pos < max; pos++ ) + if ( !s.at(pos).isSpace() ) + break; + + int indent = 0; + bool open = false; + QString spaces = s.left(pos); + + //qDebug("spaces : \"%s\"", qPrintable(spaces)); + + foreach ( QParenthesis p, b.parentheses() ) + { + if ( p.offset >= max ) + { + break; + } else if ( !(p.role & QParenthesis::Indent) ) { + open = true; + } else if ( p.role & QParenthesis::Close ) { + + if ( open ) + --indent; + + } else { //if ( p.role & QParenthesis::Open ) { + open = true; + ++indent; + } + } + + //qDebug("indent : %i", indent); + + if ( indent > 0 ) + spaces += QString(indent, '\t'); + + return spaces; + } + + virtual bool unindent (const QDocumentCursor& c, QKeyEvent *k) + { + if ( c.isNull() || c.line().isNull() ) + return false; + + QDocumentLine b = c.line(); + int pos, max = qMin(c.columnNumber(), b.text().size()); + + QString ktxt = k->text(), + s = b.text().left(max); + + if ( ktxt.isEmpty() ) + return false; + + for ( pos = 0; pos < max; pos++ ) + if ( !s.at(pos).isSpace() ) + break; + + QString spaces = s.left(pos), + text = s.mid(pos) + ktxt; + + if ( spaces.isEmpty() || text.isEmpty() ) + return false; + + QNFAMatchContext cxt; + QNFANotifier notify(text); + + QDocumentLine prev = b.previous(); + + if ( prev.isValid() ) + { + cxt = prev.matchContext(); + } else { + // first line + cxt = b.matchContext(); + cxt.reset(); + } + + match(&cxt, text, ¬ify); + + const QVector& parens = notify.parentheses(); + + if ( parens.isEmpty() ) + return false; + + QParenthesis p = parens.last(); + + return ( + (p.role & QParenthesis::Close) + && + (p.role & QParenthesis::Indent) + && + (p.offset == 0) + && + (p.length == text.count()) + ); + } + + private: + QNFADefinition *m_definition; +}; + +class QNFAHighlighter : public QHighlighterInterface +{ + public: + QNFAHighlighter(QNFADefinition *d) + : m_definition(d) + { + + } + + virtual QString singleLineComment() const + { + return m_definition->m_singleLineComment; + } + + virtual void highlight(QDocumentLine& block, QFormatScheme *) + { + if ( !block.matchContext()->context ) + block.matchContext()->context = m_definition->m_root; + + QNFANotifier notifier(block); + QString txt = block.text() + "\n"; + match(block.matchContext(), txt, ¬ifier); + } + + private: + QNFADefinition *m_definition; +}; +#endif + +extern bool stringToBool(const QString& s, bool previous); + +QHash QNFADefinition::m_paren; +QHash QNFADefinition::m_contexts; +//QHash QNFADefinition::m_definitions; +QHash QNFADefinition::m_pendingEmbeds; + +void QNFADefinition::load(const QString& file, QLanguageFactory::LangData *d, QFormatScheme *s) +{ + QFile f(file); + + if ( !f.open(QFile::ReadOnly | QFile::Text) ) + { + qWarning("QNFADefinition : failed to open file %s", qPrintable(file)); + return; + } + + load(&f, d, s); +} + +void QNFADefinition::load(QFile *f, QLanguageFactory::LangData *d, QFormatScheme *s) +{ + QDomDocument doc; + doc.setContent(f); + + load(doc, d, s); +} + +void QNFADefinition::load(const QDomDocument& doc, QLanguageFactory::LangData *d, QFormatScheme *s) +{ + QDomElement root = doc.documentElement(); + + QNFADefinition *nd = new QNFADefinition; + + nd->m_language = d->lang = root.attribute("language"); + nd->m_indentFold = stringToBool(root.attribute("indentationFold"), false); + nd->m_extensions = d->extensions = root.attribute("extensions").split(";"); + nd->m_defaultMark = root.attribute("defaultLineMark", "bookmark"); + + /* + qDebug("Generating definition for %s language from XML file : %s", + qPrintable(m_language), + qPrintable(fn)); + */ + + // create root entity + nd->m_root = lexer(); + + _singleLineCommentTarget = &(nd->m_singleLineComment); + fillContext(nd->m_root, root, s, m_paren, true); + _singleLineCommentTarget = 0; + + squeeze(nd->m_root); + + //m_definitions[m_language] = this; + m_contexts[nd->m_language] = nd->m_root; + + flushEmbedRequests(nd->m_language); + + d->d = nd; + d->e = 0; + d->s = s; +} + +QNFADefinition::QNFADefinition() + : m_indentFold(false), m_root(0) +{ + +} + +QNFADefinition::~QNFADefinition() +{ + delete m_root; +} + +QString QNFADefinition::language() const +{ + return m_language; +} + +QStringList QNFADefinition::extensions() const +{ + return m_extensions; +} + +/*! + \brief Entry point for syntax highlighting +*/ +int QNFADefinition::tokenize(QDocument *d, int line, int count) +{ + int n = 0; + bool diffCxt = false; + + QNFA *prevcxt; + QDocumentLine l, prev = d->line(line - 1); + + //qDebug("reformating %i lines from %i...", count, line); + + while ( (n < count) || diffCxt ) + { + l = d->line(line); + + if ( !l.isValid() ) + break; + + // get last context nest to check for noteworthy modifications needing further work + prevcxt = l.matchContext()->context; + //qDebug("\tline %i, cxt : %i", line, prevcxt); + + if ( prev.isValid() ) + { + // go on using previous match context (NFA naming...) + + l.matchContext()->context = prev.matchContext()->context; + l.matchContext()->parents = prev.matchContext()->parents; + l.matchContext()->meaningless = prev.matchContext()->meaningless; + } else { + // first line : reset context.... + + l.matchContext()->reset(); + + /* + qDebug("reset : popping down by %i contexts", line.matchContext()->parents.count()); + + while ( line.matchContext()->parents.count() ) + line.matchContext()->context = line.matchContext()->parents.pop(); + */ + } + + // call the implementation + if ( !l.matchContext()->context ) + l.matchContext()->context = m_root; + + QNFANotifier notifier(l); + QString txt = l.text() + "\n"; + ::match(l.matchContext(), txt, ¬ifier); + + // update cont state, i.e. whether or not highlighting info of next block shall be updated + diffCxt = (prevcxt != l.matchContext()->context); + //qDebug("\t->%i (%i)", l.matchContext()->context, diffCxt); + prev = l; + ++line; + ++n; + } + + return n; +} + +/*! + \brief Return the string starting a single line comment, if any offered by the language +*/ +QString QNFADefinition::singleLineComment() const +{ + return m_singleLineComment; +} + +QString QNFADefinition::defaultLineMark() const +{ + return m_defaultMark; +} + +/*! + \brief Brace matching entry point +*/ +void QNFADefinition::clearMatches(QDocument *d) +{ + QHash, int>::iterator it = m_matchGroups.find(d); + + if ( it != m_matchGroups.end() ) + { + d->clearMatches(*it); + d->flushMatches(*it); + + // erase? + //m_matchGroups.erase(it); + } +} + +/*! + \brief Brace matching entry point +*/ +void QNFADefinition::match(QDocumentCursor& c) +{ + QDocumentLine b = c.line(); + QDocument *d = c.document(); + + if ( !d || !b.isValid() ) + { + qDebug("invalid cursor"); + return; + } + + int gid = 0; + QHash, int>::iterator it = m_matchGroups.find(d); + + if ( it != m_matchGroups.end() ) + { + d->clearMatches(*it); + d->flushMatches(*it); + *it = gid = d->getNextGroupId(); + } else { + gid = d->getNextGroupId(); + m_matchGroups[d] = gid; + } + + QFormatScheme *s = d->formatScheme(); + + if ( !s ) + { + qDebug("no fmt"); + d->releaseGroupId(gid); + m_matchGroups.remove(d); + return; + } + + int matchFID = s->id("braceMatch"), mismatchFID = s->id("braceMismatch"); + + int pos = c.columnNumber(); + const QVector& m_parens = b.parentheses(); + + if ( m_parens.isEmpty() ) + { + // required to properly update display + d->releaseGroupId(gid); + m_matchGroups.remove(d); + return; + } + + QParenthesis p; + + //qDebug("matching on line %i (fid is %i)", c.lineNumber(), fid); + + int matchCount = 0; + foreach ( p, m_parens ) + { + if ( (pos != p.offset) && (pos != (p.offset + p.length)) ) + continue; + + if ( !(p.role & QParenthesis::Match) ) + continue; + + PMatch m; + m.line[0] = c.lineNumber(); + m.column[0] = p.offset; + m.length[0] = p.length; + + if ( (p.role & QParenthesis::Open) && (p.role & QParenthesis::Close) ) + { + matchOpen(d, m); + + if ( m.type == PMatch::Match ) + { + ++matchCount; + + d->addMatch(gid, m.line[0], m.column[0], m.length[0], matchFID); + d->addMatch(gid, m.line[1], m.column[1], m.length[1], matchFID); + } + + m.line[0] = c.lineNumber(); + m.column[0] = p.offset; + m.length[0] = p.length; + + matchClose(d, m); + + if ( m.type == PMatch::Match ) + { + ++matchCount; + + d->addMatch(gid, m.line[0], m.column[0], m.length[0], matchFID); + d->addMatch(gid, m.line[1], m.column[1], m.length[1], matchFID); + } + + continue; + } else if ( p.role & QParenthesis::Open ) { + matchOpen(d, m); + } else if ( p.role & QParenthesis::Close ) { + matchClose(d, m); + } + + if ( m.type != PMatch::Invalid ) + { + ++matchCount; + + int mfid = m.type == PMatch::Match ? matchFID : mismatchFID; + d->addMatch(gid, m.line[0], m.column[0], m.length[0], mfid); + d->addMatch(gid, m.line[1], m.column[1], m.length[1], mfid); + } + } + + if ( matchCount ) + { + d->flushMatches(gid); + } else { + d->releaseGroupId(gid); + m_matchGroups.remove(d); + } +} + +void QNFADefinition::matchOpen(QDocument *d, PMatch& m) +{ + int line = m.line[0]; + int pos = m.column[0]; + QVector m_parens = d->line(line).parentheses(); + + bool bMatch = false; + QParenthesis par; + QStack lines; + QStack parens; + + forever + { + foreach ( par, m_parens ) + { + if ( par.offset < pos ) + continue; + + if ( (par.role & QParenthesis::Open) && (par.role & QParenthesis::Close) ) + { + bool bInsert = true; + + if ( parens.count() ) + { + if ( ::match(par, parens.top()) ) + { + //qDebug("submatch at %i", line); + parens.pop(); + lines.pop(); + + if ( parens.isEmpty() ) + { + bMatch = true; + //qDebug("match at %i", line); + m.type = PMatch::Match; + + break; + } + } else { + bInsert = false; + + do + { + QParenthesis tp = parens.top(); + + if ( (tp.role & QParenthesis::Open) && (tp.role & QParenthesis::Close) ) + { + if ( par.id < tp.id ) + { + parens.pop(); + } else { + break; + } + } else { + break; + } + + } while ( parens.count() ); + + if ( parens.isEmpty() ) + { + bMatch = true; + m.type = PMatch::Mismatch; + break; + } + } + } + + if ( bInsert ) + { + parens.push(par); + lines.push(line); + } + } else if ( par.role & QParenthesis::Open ) { + lines.push(line); + parens.push(par); + } else if ( par.role & QParenthesis::Close ) { + if ( (bMatch = ::match(parens.top(), par)) ) + { + bMatch &= parens.count() == 1; + + if ( bMatch ) + { + m.type = PMatch::Match; + + break; + } else if ( parens.count() ) { + parens.pop(); + lines.pop(); + } else + qWarning("bad paren nesting..."); + } else { + bMatch = true; + + QParenthesis p = parens.pop(); + + m.line[0] = lines.pop(); + m.column[0] = p.offset; + m.length[0] = p.length; + + m.type = PMatch::Mismatch; + + break; + } + } + } + + if ( bMatch ) + break; + + pos = 0; + ++line; + + QDocumentLine b = d->line(line); + + if ( !b.isValid() ) + { + m.type = PMatch::Invalid; + return; + } + + m_parens = b.parentheses(); + } + + m.line[1] = line; + m.column[1] = par.offset; + m.length[1] = par.length; +} + +void QNFADefinition::matchClose(QDocument *d, PMatch& m) +{ + int line = m.line[0]; + int pos = m.column[0]; + QVector m_parens = d->line(line).parentheses(); + + bool bMatch = false; + QParenthesis par; + QStack lines; + QStack parens; + + forever + { + for ( int i = m_parens.count() - 1; i >= 0; --i ) + { + par = m_parens.at(i); + + if ( par.offset > pos ) + continue; + + if ( (par.role & QParenthesis::Open) && (par.role & QParenthesis::Close) ) + { + bool bInsert = true; + + if ( parens.count() ) + { + if ( ::match(par, parens.top()) ) + { + //qDebug("submatch at %i", line); + parens.pop(); + lines.pop(); + + if ( parens.isEmpty() ) + { + //qDebug("match at %i", line); + bMatch = true; + m.type = PMatch::Match; + + break; + } + } else { + bInsert = false; + do + { + QParenthesis tp = parens.top(); + + if ( (tp.role & QParenthesis::Open) && (tp.role & QParenthesis::Close) ) + { + if ( par.id < tp.id ) + { + parens.pop(); + } else { + break; + } + } else { + break; + } + + } while ( parens.count() ); + + if ( parens.isEmpty() ) + { + bMatch = true; + m.type = PMatch::Mismatch; + break; + } + } + } + + if ( bInsert ) + { + parens.push(par); + lines.push(line); + } + } else if ( par.role & QParenthesis::Close ) { + parens.push(par); + lines.push(line); + } else if ( par.role & QParenthesis::Open ) { + if ( ::match(par, parens.top()) ) + { + bMatch = parens.count() == 1; + + if ( bMatch ) + { + m.type = PMatch::Match; + + break; + } else if ( parens.count() ) { + parens.pop(); + lines.pop(); + } else + qWarning("bad paren nesting..."); + } else { + bMatch = true; + + QParenthesis p = parens.pop(); + + m.line[0] = lines.pop(); + m.column[0] = p.offset; + m.length[0] = p.length; + + m.type = PMatch::Mismatch; + + break; + } + } + } + + if ( bMatch ) + break; + + --line; + + QDocumentLine b = d->line(line); + pos = b.length(); + + if ( !b.isValid() ) + { + m.type = PMatch::Invalid; + return; + } + + m_parens = b.parentheses(); + } + + m.line[1] = line; + m.column[1] = par.offset; + m.length[1] = par.length; +} + +/*! + \brief Return the indent to use when inserting a line at a given cursor position +*/ +QString QNFADefinition::indent(const QDocumentCursor& c) +{ + if ( c.isNull() || c.line().isNull() ) + return QString(); + + QDocumentLine b = c.line(); + int pos, max = qMin(c.columnNumber(), b.text().size()); + + QString s = b.text().left(max); + + //qDebug("line %i, column %i : %s", b.lineNumber(), c.columnNumber(), qPrintable(s)); + + for ( pos = 0; pos < max; pos++ ) + if ( !s.at(pos).isSpace() ) + break; + + int indent = 0; + QString spaces = s.left(pos); + + foreach ( QParenthesis p, b.parentheses() ) + { + if ( p.offset >= max ) + break; + + if ( !(p.role & QParenthesis::Indent) ) + continue; + + if ( p.role & QParenthesis::Open ) + { + ++indent; + } else if ( p.role & QParenthesis::Close ) { + --indent; + } + } + + //qDebug("indent : %i", indent); + + if ( indent > 0 ) + spaces += QString(indent, '\t'); + + return spaces; +} + +/*! + \brief Determines whether the given key event at the given position should cause unindent to happen +*/ +bool QNFADefinition::unindent (const QDocumentCursor& c, const QString& ktxt) +{ + if ( c.isNull() || c.line().isNull() || ktxt.isEmpty() ) + return false; + + QDocumentLine b = c.line(); + QDocumentLine prev = c.document()->line(c.lineNumber() - 1); + + if ( !prev.isValid() ) + return false; + + int prevIndent = prev.indent(), curIndent = b.indent(); + int pos, max = qMin(c.columnNumber(), b.text().size()); + + if ( (prevIndent - curIndent) >= c.document()->tabStop() ) + return false; + + QString s = b.text(); + s.insert(max, ktxt); + + //qDebug("outdenting %s", qPrintable(s)); + for ( pos = 0; pos < max; ++pos ) + if ( !s.at(pos).isSpace() ) + break; + + if ( !pos || pos >= c.columnNumber() + ktxt.length() ) + return false; + + QString text = s.mid(pos); + + QNFAMatchContext cxt; + QNFANotifier notify(text); + + if ( prev.isValid() ) + { + cxt = prev.matchContext(); + } else { + // first line + cxt = b.matchContext(); + cxt.reset(); + } + + ::match(&cxt, text, ¬ify); + + const QVector& parens = notify.parentheses(); + + if ( parens.isEmpty() ) + return false; + + //qDebug("%i parentheses", parens.count()); + QParenthesis p = parens.first(); + + //qDebug("%i, %i, %i", p.offset, p.length, c.columnNumber()); + + return ( + (p.role & QParenthesis::Close) + && + (p.role & QParenthesis::Indent) + && + (p.offset <= c.columnNumber() - pos) + && + (p.offset + p.length > c.columnNumber() - pos) + ); + +} + +int QNFADefinition::findBlockEnd(QDocument *d, int line, bool *open) +{ + QDocumentLine b = d->line(line); + + if ( !b.isValid() ) + return -1; + + QVector vp, lp = b.parentheses(); + + while ( lp.count() ) + { + QParenthesis p = lp.first(); + lp.pop_front(); + + if ( !(p.role & QParenthesis::Fold) ) + continue; + + if ( p.role & QParenthesis::Close && vp.count() ) + vp.pop_back(); + + if ( p.role & QParenthesis::Open ) + vp << p; + + } + + if ( vp.count() ) + { + QParenthesis p = vp.first(); + + PMatch m; + m.line[0] = line; + m.column[0] = p.offset; + m.length[0] = p.length; + + matchOpen(d, m); + + if ( + (m.type == PMatch::Match) + || + ( + (m.type == PMatch::Mismatch) + && + (m.line[0] == line) + && + (m.column[0] == p.offset) + ) + ) + { + if ( open ) + { + int flags = blockFlags(d, m.line[1], 0); + + *open = QCE_FOLD_OPEN_COUNT(flags) | (m.type == PMatch::Mismatch); + } + + return m.line[1]; + } else if ( m.type == PMatch::Invalid ) { + return d->lines() - 1; + } + } + + return -1; +} + + +/*! + \brief Expand a collapsed block at a given line +*/ +void QNFADefinition::expand(QDocument *d, int line) +{ + QDocumentLine b = d->line(line); + + if ( !b.isValid() || !b.hasFlag(QDocumentLine::CollapsedBlockStart) ) + return; + + bool open = false; + int end = findBlockEnd(d, line, &open); + + int count = end - line; + + if ( open ) + --count; + + if ( count <= 0 ) + return; + + b.setFlag(QDocumentLine::CollapsedBlockStart, false); + + bool shared = false; + int i = line + 1; + + while ( i < end ) + { + QDocumentLine l = d->line(i); + l.setFlag(QDocumentLine::Hidden, false); + //qDebug("+%i", i); + if ( l.hasFlag(QDocumentLine::CollapsedBlockStart) ) + { + bool bopen = false; + int bend = findBlockEnd(d, i, &bopen); + + i = bend; + + if ( !bopen ) + ++i; + + if ( bend == end ) + { + shared = true; + } + } else { + ++i; + } + } + + if ( !shared ) + { + d->line(end).setFlag(QDocumentLine::CollapsedBlockEnd, false); + } + + if ( !open && !shared ) + { + //qDebug("+%i", end); + d->line(end).setFlag(QDocumentLine::Hidden, false); + } + + //qDebug("expanding %i lines from %i", count, line); + d->impl()->showEvent(line, count); +} + +/*! + \brief Collapse a text block at a given line +*/ +void QNFADefinition::collapse(QDocument *d, int line) +{ + QDocumentLine b = d->line(line); + + if ( !b.isValid() || b.hasFlag(QDocumentLine::CollapsedBlockStart) ) + return; + + //qDebug("collapse line %i", line); + + bool open = false; + int end = findBlockEnd(d, line, &open); + + int count = end - line; + + if ( open ) + --count; + + if ( count <= 0 ) + return; + + b.setFlag(QDocumentLine::CollapsedBlockStart); + + for ( int i = line + 1; i < end; ++i ) + { + if ( !d->line(i).hasFlag(QDocumentLine::Hidden) ) + { + //qDebug("-%i", i); + d->line(i).setFlag(QDocumentLine::Hidden); + } + } + + d->line(end).setFlag(QDocumentLine::CollapsedBlockEnd); + + if ( !open ) + { + if ( !d->line(end).hasFlag(QDocumentLine::Hidden) ) + { + //qDebug("-%i", end); + d->line(end).setFlag(QDocumentLine::Hidden); + } + } + + //qDebug("collapsing %i lines from %i", count, line); + d->impl()->hideEvent(line, count); + + #if 0 + if ( !m_indentFold ) + return; + + int i = ln + 1, k; + QDocumentLine block = d->line(i); + + if ( !block.isValid() ) + return; + + indent = block.firstChar(); + + do + { + block = d->line(++i); + k = block.firstChar(); + } while ( block.isValid() && ((k >= indent) || (k == -1)) ); + + if ( k < indent ) + { + block = d->line(--i); + k = block.firstChar(); + } + + while ( block.isValid() && (i > ln) && (k == -1) ) + { + block = d->line(--i); + k = block.firstChar(); + } + + b.setFlag(QDocumentLine::CollapsedBlockStart); + + //qDebug("hidding from %i to %i", ln + 1, i); + + for ( int j = ln + 1; j <= i; ++j ) + { + d->line(j).setFlag(QDocumentLine::Hidden); + } + + d->line(i).setFlag(QDocumentLine::CollapsedBlockEnd); + + d->impl()->hideEvent(ln, i - ln); + #endif +} + +/*! + \brief Compute the collapse state of a line +*/ +int QNFADefinition::blockFlags(QDocument *d, int line, int depth) const +{ + Q_UNUSED(depth) + + QDocumentLine b = d->line(line); + + if ( !b.isValid() ) + return None; + + int ret = None; + short open = 0, close = 0; + + foreach ( QParenthesis p, b.parentheses() ) + { + if ( !(p.role & QParenthesis::Fold) ) + continue; + + if ( p.role & QParenthesis::Close ) + { + if ( open ) + --open; + else + ++close; + + } + + if ( p.role & QParenthesis::Open ) + { + ++open; + } + } + + if ( b.hasFlag(QDocumentLine::CollapsedBlockStart) ) + ret = Collapsed; + + if ( open ) + ret |= Collapsible; + + if ( close ) + ret |= Closure; + + //qDebug("%i : ret : %i (%i, %i)", line, ret, ret & 0xffff0000, short(((short)0) | (ret & 0xffff))); + + return QCE_FOLD_FLAGS(ret, open, close); +} + +void QNFADefinition::addContext(const QString& id, QNFA *nfa) +{ + //qDebug("registering context : %s", qPrintable(id)); + m_contexts[id] = nfa; +} + +void QNFADefinition::flushEmbedRequests(const QString& lang) +{ + //qDebug("flushing requests for : %s", qPrintable(lang)); + QHash::iterator it; + + it = m_pendingEmbeds.begin(); + + while ( it != m_pendingEmbeds.end() ) + { + QString r = it.key(); + + if ( + r.startsWith(lang) + && + ( + (r.count() == lang.count()) + || + (r.at(lang.count()) == ':') + ) + ) + { + QNFA *src = m_contexts.value(r); + + if ( !src ) + { + ++it; + continue; + } + + foreach( const EmbedRequest& request, *it ) + { + //qDebug("embedding %s in 0x%x at index %i", + // qPrintable(r), request.target, request.index); + + embed(src, request.target, request.index); + } + + it = m_pendingEmbeds.erase(it); + } else { + ++it; + } + } +} + +void QNFADefinition::addEmbedRequest(const QString& cxt, QNFA *dest) +{ + //qDebug("Adding request : %s", qPrintable(cxt)); + + if ( m_contexts.contains(cxt) ) + { + //qDebug("dealing with request : %s", qPrintable(cxt)); + embed(m_contexts[cxt], dest, dest->out.branch->count()); + } else { + m_pendingEmbeds[cxt] << EmbedRequest(dest, dest->out.branch->count()); + } +} + +void QNFADefinition::shareEmbedRequests(QNFA *src, QNFA *dest, int offset) +{ + QHash::iterator it = m_pendingEmbeds.begin(); + + while ( it != m_pendingEmbeds.end() ) + { + foreach ( const EmbedRequest& request, *it ) + { + if ( request.target == src ) + { + //qDebug("sharing one embed request between 0x%x and 0x%x at %i + %i", src, dest, offset, request.index); + it->append(EmbedRequest(dest, request.index + offset)); + } + } + + ++it; + } +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qnfa/qnfadefinition.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qnfa/qnfadefinition.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QNFA_DEFINITION_H_ +#define _QNFA_DEFINITION_H_ + +/*! + \file qnfadefinition.h + \brief Definition of the QNFADefinition class. +*/ + +#include "qlanguagefactory.h" +#include "qlanguagedefinition.h" + +#include +#include +#include + +struct QNFA; + +class QParenthesis; + +class QFile; +class QDomDocument; + +class QNFAAction +{ + public: + enum + { + NoAction = 0, + + FormatMask = 0x00000fff, + ParenMask = 0x00fff000, + + Highlight = 0x01000000, + Indent = 0x02000000, + ParenOpen = 0x04000000, + ParenClose = 0x08000000, + MatchParen = 0x10000000, + Fold = 0x20000000, + + Ambiguous = 0x40000000, + + Content = 0x80000000 + }; + + inline static int format(int id) + { return id & FormatMask; } + + inline static int parenthesis(int id) + { return id & ParenMask; } +}; + +class QCE_EXPORT QNFADefinition : public QLanguageDefinition +{ + public: + QNFADefinition(); + virtual ~QNFADefinition(); + + virtual QString language() const; + virtual QStringList extensions() const; + + virtual int tokenize(QDocument *d, int line, int count); + + virtual QString singleLineComment() const; + + virtual QString defaultLineMark() const; + + virtual void clearMatches(QDocument *d); + virtual void match(QDocumentCursor& c); + + virtual QString indent(const QDocumentCursor& c); + virtual bool unindent (const QDocumentCursor& c, const QString& ktxt); + + virtual void expand(QDocument *d, int line); + virtual void collapse(QDocument *d, int line); + virtual int blockFlags(QDocument *d, int line, int depth) const; + + static void load(QFile *f, QLanguageFactory::LangData *d, QFormatScheme *s); + static void load(const QString& file, QLanguageFactory::LangData *d, QFormatScheme *s); + static void load(const QDomDocument& doc, QLanguageFactory::LangData *d, QFormatScheme *s); + + static void addContext(const QString& id, QNFA *nfa); + static void addEmbedRequest(const QString& lang, QNFA *dest); + static void shareEmbedRequests(QNFA *src, QNFA *dest, int offset); + + private: + bool m_indentFold; + QString m_language, + m_defaultMark, + m_singleLineComment; + + QStringList m_extensions; + + QNFA *m_root; + + QHash, int> m_matchGroups; + + static QHash m_paren; + static QHash m_contexts; + + struct PMatch + { + PMatch() : type(Invalid) + { + line[0] = -1; + line[1] = -1; + + column[0] = -1; + column[1] = -1; + + length[0] = 0; + length[1] = 0; + } + + enum Type + { + Invalid, + Match, + Mismatch + }; + + char type; + + int line[2]; + int column[2]; + int length[2]; + }; + + void matchOpen(QDocument *d, PMatch& m); + void matchClose(QDocument *d, PMatch& m); + + int findBlockEnd(QDocument *d, int line, bool *open = 0); + + static void flushEmbedRequests(const QString& lang); + + struct EmbedRequest + { + inline EmbedRequest(QNFA *nfa, int idx) : index(idx), target(nfa) {} + + int index; + QNFA *target; + }; + + typedef QList EmbedRequestList; + + static QHash m_pendingEmbeds; +}; + +#endif // !_QNFA_DEFINITION_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qnfa/xml2qnfa.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qnfa/xml2qnfa.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,466 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +/*! + \file xml2qnfa.cpp + \brief Implementation of the QNFA builder (fetch syntax engine data from XML files) +*/ + +#include "qnfa.h" +#include "qnfadefinition.h" + +#include "qformatscheme.h" + +#include +#include +#include +#include + +/* + + + + + + + + + + ... + + + +*/ + +QString *_singleLineCommentTarget = 0; + +bool stringToBool(const QString& s, bool previous) +{ + if ( s.isEmpty() ) + return previous; + + return + ( + (s == "true") + || + (s == "enabled") + ) + || + ( + (s != "false") + && + (s != "disabled") + && + QVariant(s).toBool() + ); +} + +int pid(const QString& s, QHash& pids) +{ + if ( pids.contains(s) ) + return pids.value(s); + + int id = (pids.count() + 1) << 12; + + pids[s] = id; + + return id; +} + +int action(QDomElement c, QFormatScheme *f, QHash& pids, int fid = 0) +{ + QString paren, spid, spt, sfid; + + sfid = c.attribute("format"); + + if ( sfid.count() ) + { + fid |= QNFAAction::Highlight | QNFAAction::format(f->id(sfid)); + } + + paren = c.attribute("parenthesis"); + + if ( paren.count() ) + { + spid = paren.section(':', 0, -2); + spt = paren.section(':', -1, -1); + + if ( spt.endsWith("@nomatch") ) + { + spt.chop(8); + } else { + fid |= QNFAAction::MatchParen; + } + + if ( spid.count() ) + { + /*qDebug("paren : [%s|%s] => 0x%x", + qPrintable(spid), qPrintable(spt), + (spt == "open" ? QNFAAction::ParenOpen : QNFAAction::ParenClose) | pid(spid, pids)); + */ + + if ( spt == "open" ) + fid |= QNFAAction::ParenOpen; + else if ( spt == "close" ) + fid |= QNFAAction::ParenClose; + else if ( spt == "boundary" ) + fid |= QNFAAction::ParenOpen | QNFAAction::ParenClose; + + fid |= QNFAAction::parenthesis(pid(spid, pids)); + + /* + qDebug("paren : [%s|%s] => 0x%x", + qPrintable(spid), qPrintable(spt), + fid & QNFAAction::ParenMask); + */ + } + } + + if ( stringToBool(c.attribute("indent"), false) ) + fid |= QNFAAction::Indent; + + if ( stringToBool(c.attribute("fold"), false) ) + fid |= QNFAAction::Fold; + + // TODO : determine ambiguity automatically + if ( stringToBool(c.attribute("ambiguous"), false) ) + fid |= QNFAAction::Ambiguous; + + return fid; +} + +void copy(const QCharTreeLevel& src, QCharTreeLevel& dest) +{ + QCharTreeLevel::const_iterator it = src.constBegin(); + + while ( it != src.constEnd() ) + { + //qDebug("copying char tree level %c", QChar(it.key()).toLatin1()); + + if ( !dest.contains(it.key()) ) + dest[it.key()] = *it; + else + copy(it->next, dest[it.key()].next); + + ++it; + } +} + +void embed(QNFA *src, QNFA *dest, int idx = 0) +{ + QNFA *nfa; + const int n = src->out.branch->count(); + + //dest->out.branch->alloc(idx, n); + + //qDebug("\tembedding %i children", n); + + for ( int i = 0; i < n; ++i ) + { + nfa = src->out.branch->at(i); + + if ( nfa->type & Reserved ) + continue; + + //qDebug("\t\teffectively embedding 0x%x at %i", nfa, idx); + dest->out.branch->insert(idx++, nfa); + } + + copy(src->tree, dest->tree); +} + +void fillContext(QNFA *cxt, QDomElement e, QFormatScheme *f, QHash& pids, bool cs); +void fillContext(QNFA *cxt, QDomNodeList l, QFormatScheme *f, QHash& pids, bool cs); + +void addToContext( QNFA *cxt, QDomElement c, int fid, + QFormatScheme *f, QHash& pids, + const QStringList& pref, + const QStringList& suff, + bool cs) +{ + QString tag = c.tagName(); + + if ( !c.hasChildNodes() && tag != "embed" ) + return; + + cs = stringToBool(c.attribute("caseSensitive"), cs); + + if ( (tag == "start") || (tag == "stop") || (tag == "escape") ) + { + return; + } else if ( tag == "word" ) { + //qDebug("adding word : %s", qPrintable(c.firstChild().toText().data())); + const QString value = c.firstChild().toText().data(); + + if ( pref.isEmpty() && suff.isEmpty() ) + { + addWord(cxt, value, fid, cs); + } else if ( pref.count() ) { + foreach ( const QString& p, pref ) + { + if ( suff.isEmpty() ) + { + addWord(cxt, p + value, fid, cs); + } else { + foreach ( const QString& s, suff ) + { + addWord(cxt, p + value + s, fid, cs); + } + } + } + } else { + foreach ( const QString& s, suff ) + { + addWord(cxt, value + s, fid, cs); + } + } + } else if ( tag == "sequence" ) { + const QString value = c.firstChild().toText().data(); + + //qDebug("adding sequence : %s [0x%x]", qPrintable(value), cxt); + if ( pref.isEmpty() && suff.isEmpty() ) + { + addSequence(cxt, value, fid, cs); + } else if ( pref.count() ) { + foreach ( const QString& p, pref ) + { + if ( suff.isEmpty() ) + { + addSequence(cxt, p + value, fid, cs); + } else { + foreach ( const QString& s, suff ) + { + addSequence(cxt, p + value + s, fid, cs); + } + } + } + } else { + foreach ( const QString& s, suff ) + { + addSequence(cxt, value + s, fid, cs); + } + } + } else if ( tag == "list" ) { + QDomNodeList children = c.childNodes(); + QStringList prefixes, suffixes; + + //qDebug("starting list"); + for ( int j = 0; j < children.count(); ++j ) + { + QDomElement cc = children.at(j).toElement(); + + if ( cc.isNull() ) + continue; + + const QString role = cc.tagName(); + const QString value = cc.firstChild().toText().data(); + + if ( role == "prefix" ) + prefixes << value; + else if ( role == "suffix" ) + suffixes << value; + else + addToContext(cxt, cc, action(cc, f, pids, fid), f, pids, prefixes, suffixes, cs); + } + //qDebug("ending list"); + + } else if ( tag == "embed" ) { + QNFADefinition::addEmbedRequest(c.attribute("target"), cxt); + } else if ( tag == "context" ) { + QNFA *nfa, + *start = 0, + *hstart = 0, + *cstart = 0, + *stop = 0, + *hstop = 0, + *escape = 0, + *hescape = 0; + + QList lStart, lStop, lEscape; + + QString attr; + int defact = action(c, f, pids); + QDomNodeList children = c.childNodes(); + + QString _id = c.attribute("id"); + bool trans = stringToBool(c.attribute("transparency"), false); + bool stay = stringToBool(c.attribute("stayOnLine"), false); + + for ( int j = 0; j < children.count(); ++j ) + { + QDomElement child = children.at(j).toElement(); + + if ( child.isNull() || !child.hasChildNodes() ) + continue; + + bool tcs = stringToBool(child.attribute("caseSensitive"), cs); + + int act = action(child, f, pids); + + if ( !(act & QNFAAction::Highlight) ) + act |= QNFAAction::Highlight | QNFAAction::format(defact); + + const QString role = child.tagName(); + const QString value = child.firstChild().toText().data(); + + if ( role == "start" ) + { + start = sequence(value, &hstart, tcs); + start->actionid = act; + //start->type |= Reserved; + lStart << start; + + if ( !cstart ) + { + cstart = new QNFA; + cstart->type = ContextBegin | (stay ? StayOnLine : 0); + cstart->actionid = defact; + cstart->out.branch = new QNFABranch; + + // only the first sequence comes + if ( _singleLineCommentTarget && (_id == "comment/single") ) + *_singleLineCommentTarget = value; + + } + + hstart->out.next = cstart; + hstart->actionid = act; + //hstart = nfa; + + //qDebug("cxt start seq : %s [0x%x, 0x%x]", qPrintable(value), start, nfa); + + } else if ( role == "stop" ) { + stop = sequence(value, &hstop, tcs); + stop->actionid = act; + stop->type |= Reserved; + lStop << stop; + + nfa = new QNFA; + nfa->type = ContextEnd; + nfa->actionid = act; + //nfa->out.branch = new QNFABranch; + + hstop->out.next = nfa; + hstop->actionid = act; + hstop = nfa; + + attr = child.attribute("exclusive"); + + if ( attr.isEmpty() || (attr == "true") || attr.toUInt() ) + hstop->type |= Exclusive; + + //qDebug("cxt stop seq : %s [0x%x]", qPrintable(value), act); + + } else if ( role == "escape" ) { + + escape = sequence(value, &hescape, tcs); + escape->type |= Reserved; + lEscape << escape; + + nfa = new QNFA; + nfa->type = EscapeSeq; + nfa->actionid = action(child, f, pids); + //nfa->out.branch = new QNFABranch; + + hescape->out.next = nfa; + //hescape = nfa; + } + } + + if ( hstart ) + { + //qDebug("starting cxt %s:0x%x [0x%x]", qPrintable(c.attribute("id")), cstart, cxt); + + foreach ( escape, lEscape ) + { + //cstart->type |= Escaped; + addNFA(cstart, escape); + } + + //qDebug("after esc : %i", cstart->out.branch->count()); + + foreach ( stop, lStop ) + addNFA(cstart, stop); + + //qDebug("after stop : %i", cstart->out.branch->count()); + } else { + cstart = new QNFA; + cstart->type = ContextBegin | (stay ? StayOnLine : 0); + cstart->actionid = defact; + cstart->out.branch = new QNFABranch; + } + + fillContext(cstart, c, f, pids, cs); + + if ( c.hasAttribute("id") ) + { + QNFADefinition::addContext( + c.ownerDocument().documentElement().attribute("language") + + ":" + + _id, + cstart + ); + + } else { + //qDebug("unregistered context"); + } + + //qDebug("after sub : %i", cstart->out.branch->count()); + + if ( trans ) + { + QNFADefinition::shareEmbedRequests(cxt, cstart, cstart->out.branch->count()); + embed(cxt, cstart, cstart->out.branch->count()); + } + + if ( hstart ) + { + foreach ( start, lStart ) + addNFA(cxt, start); + + //qDebug("ending cxt"); + } else { + + } + + //fillContext(subcxt, c, f, pids); + } else { + //qDebug("unhandled tag : %s", qPrintable(tag)); + } +} + +void fillContext(QNFA *cxt, QDomElement e, QFormatScheme *f, QHash& pids, bool cs) +{ + cs = stringToBool(e.attribute("caseSensitive"), cs); + + fillContext(cxt, e.childNodes(), f, pids, cs); +} + +void fillContext(QNFA *cxt, QDomNodeList l, QFormatScheme *f, QHash& pids, bool cs) +{ + //qDebug("filling context from %i nodes", l.count()); + + for ( int i = 0; i < l.count(); i++ ) + { + QDomElement c = l.at(i).toElement(); + + if ( c.isNull() ) + continue; + + addToContext(cxt, c, action(c, f, pids), f, pids, QStringList(), QStringList(), cs); + } + + //qDebug("context filled"); +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qpanellayout.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qpanellayout.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,435 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qpanellayout.h" + +/*! + \file qpanellayout.cpp + \brief Implementation of the QPanelLayout class. +*/ + +#include "qpanel.h" +#include "qeditor.h" + +#include +#include + +#ifdef Q_WS_WIN +// panel position fix required on some systems to work around a bug in QAbstractScrollArea +#define _PANEL_POSITION_FIX_ +#endif + +/*! + \class QPanelLayout + \brief A specialized layout taking care of panel display + + The panel layout is specialized in several ways : +
    +
  • It only operates on specific widgets (which inherit QPanel)
  • +
  • It can only layout widgets in the viewport margins of a QEditor (could work with + any QAbstractScrollArea if a single method was made public instead of protected...) + so it does not qualify as a "real" layout (contrary to grid/box layouts)
  • +
  • It positions widgets on the border of the editor in the same way the Border Layout + example does (most of the layout code actually comes from there).
  • +
  • It provides serialization/deserialization of its layout structure
  • +
+*/ + +/* + The layouting code is inspired from a Qt4 example : Border Layout +*/ + +/*! + \brief ctor +*/ +QPanelLayout::QPanelLayout(QEditor *p) + : QLayout(p), m_parent(p) +{ + setSpacing(0); +} + +/*! + \brief ctor + \param layout structure to deserailize +*/ +QPanelLayout::QPanelLayout(const QString& layout, QEditor *p) + : QLayout(p), m_parent(p) +{ + setSpacing(0); + addSerialized(layout); +} + +/*! + \brief dtor +*/ +QPanelLayout::~QPanelLayout() +{ + QLayoutItem *l; + + while ( (l = takeAt(0)) ) + delete l; +} + +/*! + \return A serialized layout strucure +*/ +QString QPanelLayout::serialized() const +{ + /* + Scheme : + + QPanelLayout::Position '{' comma-separated list of identifiers '}' + */ + + QHash posMap; + + for ( int i = 0; i < m_list.size(); ++i ) + { + PanelWrapper *wrapper = m_list.at(i); + Position position = wrapper->position; + + QPanel *panel = qobject_cast(wrapper->item->widget()); + + if ( !panel ) + continue; + + if ( !posMap.contains(position) ) + { + posMap[position] = QString::number(position) + "{" + panel->id() + "}"; + } else { + QString& ref = posMap[position]; + + ref.insert(ref.count() - 2, QString(",") + panel->id()); + } + } + + return QStringList(posMap.values()).join(""); +} + +/*! + \brief Add the content of a serialized layout structure +*/ +void QPanelLayout::addSerialized(const QString& layout) +{ + //qDebug("layout : %s", qPrintable(layout)); + + int last = 0, i = 0; + bool inList = false; + Position position = West; + + while ( i < layout.length() ) + { + if ( inList ) + { + if ( layout.at(i) == '}' ) + inList = false; + + if ( !inList || (layout.at(i) == ',') ) + { + QPanel *panel = QPanel::panel(layout.mid(last, i - last), m_parent); + + if ( panel ) + { + panel->attach(m_parent); + addWidget(panel, position); + + //qDebug("\tpanel : %s", qPrintable(layout.mid(last, i - last))); + } + + last = i + 1; + } + } else if ( layout.at(i) == '{' ) { + inList = true; + position = Position(layout.mid(last, i - last).toInt()); + + //qDebug("position : %i [%s]", position, qPrintable(layout.mid(last, i - last))); + + last = i + 1; + } + + ++i; + } + + update(); +} + +/*! + \return the list of panels managed by the layout +*/ +QList QPanelLayout::panels() const +{ + QList l; + + foreach ( PanelWrapper *w, m_list ) + { + QPanel *p = qobject_cast(w->item->widget()); + + if ( p ) + l << p; + } + + return l; +} + +/*! + \return the count of managed panels +*/ +int QPanelLayout::count() const +{ + return m_list.count(); +} + +/*! + \internal +*/ +bool QPanelLayout::hasHeightForWidth() const +{ + return false; +} + +/*! + \internal +*/ +Qt::Orientations QPanelLayout::expandingDirections() const +{ + return Qt::Horizontal | Qt::Vertical; +} + +/*! + \internal +*/ +QSize QPanelLayout::sizeHint() const +{ + return calculateSize(SizeHint); +} + +/*! + \internal +*/ +QSize QPanelLayout::minimumSize() const +{ + return calculateSize(MinimumSize); +} + +/*! + \internal +*/ +void QPanelLayout::addItem(QLayoutItem *item) +{ + add(item, West); +} + +/*! + \brief Add a panel at a given position +*/ +void QPanelLayout::addWidget(QWidget *widget, Position position) +{ + add(new QWidgetItem(widget), position); +} + +/*! + \internal +*/ +QLayoutItem* QPanelLayout::itemAt(int idx) const +{ + PanelWrapper *wrapper = m_list.value(idx); + + if ( wrapper ) + return wrapper->item; + else + return 0; + +} + +/*! + \internal +*/ +QLayoutItem* QPanelLayout::takeAt(int idx) +{ + if ( (idx >= 0) && (idx < m_list.size()) ) + { + PanelWrapper *layoutStruct = m_list.takeAt(idx); + return layoutStruct->item; + } + + return 0; +} + +/*! + \internal +*/ +void QPanelLayout::setGeometry(const QRect &r) +{ + //qDebug("laying out %i panels", count()); + #ifdef _PANEL_POSITION_FIX_ + QScrollBar *vb = m_parent->verticalScrollBar(), + *hb = m_parent->horizontalScrollBar(); + + QRect rect( r.x(), r.y(), + r.width() - (vb->isVisibleTo(m_parent) ? vb->width() : 0), + r.height() - (hb->isVisibleTo(m_parent) ? hb->height() : 0) + ); + #else + QRect rect( r.x(), r.y(), + r.width(), + r.height() + ); + #endif + + int i, + eastWidth = 0, + westWidth = 0, + northHeight = 0, + southHeight = 0, + centerHeight = 0; + + QLayout::setGeometry(rect); + + for ( i = 0; i < m_list.size(); ++i ) + { + PanelWrapper *wrapper = m_list.at(i); + QLayoutItem *item = wrapper->item; + Position position = wrapper->position; + + if ( item->isEmpty() ) + continue; + + if ( position == North ) + { + item->setGeometry(QRect( + rect.x(), + northHeight, + rect.width(), + item->sizeHint().height() + ) + ); + + northHeight += item->geometry().height() + spacing(); + } else if (position == South) { + item->setGeometry(QRect(item->geometry().x(), + item->geometry().y(), + rect.width(), + item->sizeHint().height() + ) + ); + + southHeight += item->geometry().height() + spacing(); + + item->setGeometry(QRect(rect.x(), + rect.y() + rect.height() - southHeight + spacing(), + item->geometry().width(), + item->geometry().height() + ) + ); + + } + } + + centerHeight = rect.height() - northHeight - southHeight; + + for ( i = 0; i < m_list.size(); ++i ) + { + PanelWrapper *wrapper = m_list.at(i); + QLayoutItem *item = wrapper->item; + Position position = wrapper->position; + + if ( item->isEmpty() ) + continue; + + if ( position == West ) + { + item->setGeometry(QRect(rect.x() + westWidth, + northHeight, + item->sizeHint().width(), + centerHeight + ) + ); + + westWidth += item->geometry().width() + spacing(); + } else if (position == East) { + item->setGeometry(QRect(item->geometry().x(), + item->geometry().y(), + item->sizeHint().width(), + centerHeight + ) + ); + + eastWidth += item->geometry().width() + spacing(); + + item->setGeometry(QRect(rect.x() + rect.width() - eastWidth + spacing(), + northHeight, + item->geometry().width(), + item->geometry().height() + ) + ); + + } + } + + /* + if ( center ) + center->item->setGeometry(QRect(westWidth, northHeight, + rect.width() - eastWidth - westWidth, + centerHeight)); + */ + //qDebug("{%i, %i, %i, %i}", westWidth, northHeight, eastWidth, southHeight); + m_parent->setPanelMargins(westWidth, northHeight, eastWidth, southHeight); +} + +/*! + \internal +*/ +void QPanelLayout::add(QLayoutItem *item, Position position) +{ + QPanel *p; + + if ( (p = qobject_cast(item->widget())) ) + { + //p->setParent(m_parent); + p->setVisible(p->defaultVisibility()); + } + + m_list.append(new PanelWrapper(item, position)); +} + +/*! + \internal +*/ +QSize QPanelLayout::calculateSize(SizeType sizeType) const +{ + QSize totalSize; + + for ( int i = 0; i < m_list.size(); ++i ) + { + QSize itemSize; + PanelWrapper *wrapper = m_list.at(i); + Position position = wrapper->position; + + if ( sizeType == MinimumSize ) + itemSize = wrapper->item->minimumSize(); + else // ( sizeType == SizeHint ) + itemSize = wrapper->item->sizeHint(); + + if ( (position == North) || (position == South) ) // || (position == Center) ) + totalSize.rheight() += itemSize.height(); + + if ( (position == West) || (position == East) ) // || (position == Center) ) + totalSize.rwidth() += itemSize.width(); + + } + + return totalSize; +} + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qpanellayout.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qpanellayout.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QPANEL_LAYOUT_H_ +#define _QPANEL_LAYOUT_H_ + +#include "qce-config.h" + +/*! + \file qpanellayout.h + \brief Definition of the QPanelLayout class +*/ + +#include +#include +#include + +class QPanel; +class QEditor; + +class QCE_EXPORT QPanelLayout : public QLayout +{ + Q_OBJECT + + public: + enum Position + { + West, + North, + South, + East + }; + + QPanelLayout(QEditor *p); + QPanelLayout(const QString& layout, QEditor *p); + virtual ~QPanelLayout(); + + virtual int count() const; + virtual bool hasHeightForWidth() const; + virtual Qt::Orientations expandingDirections() const; + + virtual QSize sizeHint() const; + virtual QSize minimumSize() const; + + virtual QLayoutItem *itemAt(int idx) const; + virtual QLayoutItem *takeAt(int idx); + + QString serialized() const; + void addSerialized(const QString& layout); + + QList panels() const; + + public slots: + virtual void addItem(QLayoutItem *item); + virtual void setGeometry(const QRect &rect); + + void add(QLayoutItem *item, Position position); + void addWidget(QWidget *widget, Position position); + + private: + QPointer m_parent; + + struct PanelWrapper + { + PanelWrapper(QLayoutItem *i, Position p) + { + item = i; + position = p; + } + + QLayoutItem *item; + Position position; + }; + + enum SizeType { MinimumSize, SizeHint }; + QSize calculateSize(SizeType sizeType) const; + + QList m_list; +}; + +#endif // _QPANEL_LAYOUT_H_ + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qreliablefilewatch.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qreliablefilewatch.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qreliablefilewatch.h" + +/*! + \file qreliablefilewatch.cpp + \brief Implementation of the QReliableFileWatch class. +*/ + +#include +#include + +/*! + \class QReliableFileWatch + \brief A specialized file monitor that works around some issues in QFileSystemWatcher +*/ + +QReliableFileWatch::QReliableFileWatch(QObject *p) + : QFileSystemWatcher(p) +{ + connect(this, SIGNAL( fileChanged(QString) ), + this, SLOT ( sourceChanged(QString) ) ); + +} + +QReliableFileWatch::~QReliableFileWatch() +{ + +} + +void QReliableFileWatch::addWatch(const QString& file, QObject *recipient) +{ + QHash::iterator it = m_targets.find(file); + + if ( it != m_targets.end() ) + { + it->recipients << recipient; + } else { + QFile f(file); + + Watch w; + w.state = Clean; + w.size = f.size(); + w.recipients << recipient; + m_targets[file] = w; + + addPath(file); + } +} + +void QReliableFileWatch::removeWatch(QObject *recipient) +{ + removeWatch(QString(), recipient); +} + +void QReliableFileWatch::removeWatch(const QString& file, QObject *recipient) +{ + QHash::iterator it = m_targets.find(file); + + if ( it == m_targets.end() ) + { + if ( !recipient ) + return; + + // given recipient stop watching any file + + it = m_targets.begin(); + + while ( it != m_targets.end() ) + { + int n = it->recipients.removeAll(recipient); + + if ( n && it->recipients.isEmpty() ) + { + // no more recipients watching this file + removePath(it.key()); + it = m_targets.erase(it); + } else { + ++it; + } + } + } else { + if ( recipient ) + { + // given recipient stops watching given file + it->recipients.removeAll(recipient); + + if ( it->recipients.isEmpty() ) + { + // no more recipients watching this file + removePath(it.key()); + m_targets.erase(it); + } + } else { + // stop watching given file at all + m_targets.erase(it); + } + } +} + +void QReliableFileWatch::timerEvent(QTimerEvent *e) +{ + if ( e->timerId() != m_timer.timerId() ) + return QFileSystemWatcher::timerEvent(e); + + int postponedEmissions = 0; + QHash::iterator it = m_targets.begin(); + + while ( it != m_targets.end() ) + { + if ( it->state & Duplicate ) + { + // postpone signal emission... + ++postponedEmissions; + it->state = Recent; + } else if ( it->state & Recent ) { + // send signal + it->state = Clean; + + QFile f(it.key()); + + if ( f.size() == it->size ) + { + // TODO : avoid signal emission if checksum match + // DO this in editor or here? + } + + //qDebug("%s emission.", qPrintable(it.key())); + + it->recipients.removeAll(0); + + foreach ( QObject *r, it->recipients ) + QMetaObject::invokeMethod(r, "fileChanged", Q_ARG(QString, it.key())); + + //it = m_state.erase(it); + } + + ++it; + } + + if ( postponedEmissions ) + { + //qDebug("%i postponed emissions", postponedEmissions); + m_timer.start(20, this); + } +} + +void QReliableFileWatch::sourceChanged(const QString& filepath) +{ + m_timer.stop(); + + QHash::iterator it = m_targets.find(filepath); + + if ( it == m_targets.end() ) + return; + + //qDebug("%s modified.", qPrintable(filepath)); + + if ( it->state ) + { + it->state = Recent | Duplicate; + } else { + it->state = Recent; + } + + m_timer.start(20, this); +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/qreliablefilewatch.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/qreliablefilewatch.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QRELIABLE_FILE_WATCH_H_ +#define _QRELIABLE_FILE_WATCH_H_ + +#include "qce-config.h" + +/*! + \file qreliablefilewatch.h + \brief Definition of the QReliableFileWatch class +*/ + +#include +#include +#include +#include + +class QCE_EXPORT QReliableFileWatch : protected QFileSystemWatcher +{ + friend class QPointer; + + Q_OBJECT + + public: + QReliableFileWatch(QObject *p = 0); + virtual ~QReliableFileWatch(); + + public slots: + void addWatch(const QString& file, QObject *recipient); + + void removeWatch(QObject *recipient); + void removeWatch(const QString& file, QObject *recipient); + + protected: + virtual void timerEvent(QTimerEvent *e); + + private slots: + void sourceChanged(const QString& filepath); + + private: + enum State + { + Clean = 0, + Recent = 1, + Duplicate = 2 + }; + + struct Watch + { + char state; + qint64 size; + QList< QPointer > recipients; + }; + + QBasicTimer m_timer; + + QHash m_targets; +}; + +#endif // !_QRELIABLE_FILE_WATCH_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/snippets/qsnippet.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/snippets/qsnippet.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,376 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +/*! + \file qsnippet.cpp + \brief Implementation of the builtin snippet types and loaders +*/ + +#include "qsnippet_p.h" + +#include + +/*! + \class QSnippet + \brief The base class for snippets +*/ + +/*! + \class QSnippetPatternLoader + \brief The base class for snippet loaders +*/ + +QSnippetInsertionCommand::QSnippetInsertionCommand(QEditor *e) + : QDocumentCommandBlock(e->document()), m_editor(e), m_cursor(e->cursor()) +{ +} + +QSnippetInsertionCommand::~QSnippetInsertionCommand() +{ + foreach ( const QEditor::PlaceHolder& ph, m_placeHolders ) + delete ph.affector; +} + +void QSnippetInsertionCommand::addPlaceHolder(const QEditor::PlaceHolder& ph) +{ + m_placeHolders << ph; +} + +void QSnippetInsertionCommand::addCommand(QDocumentCommand *c) +{ + c->setTargetCursor(m_cursor.handle()); + QDocumentCommandBlock::addCommand(c); +} + +void QSnippetInsertionCommand::removeCommand(QDocumentCommand *c) +{ + c->setTargetCursor(0); + QDocumentCommandBlock::removeCommand(c); +} + +void QSnippetInsertionCommand::redo() +{ + m_editor->clearPlaceHolders(); + QDocumentCommandBlock::redo(); + + foreach ( const QEditor::PlaceHolder& ph, m_placeHolders ) + m_editor->addPlaceHolder(ph); + + m_editor->nextPlaceHolder(); +} + +void QSnippetInsertionCommand::undo() +{ + // TODO : backup and restore previous placeholders? + m_editor->clearPlaceHolders(); + QDocumentCommandBlock::undo(); + m_editor->setCursor(m_cursor); +} + +// + +bool QCE::Snippets::PlainText::loadSnippet(QSnippet *snip, const QString& pattern) +{ + PlainText *target = dynamic_cast(snip); + + if ( !target ) + { + qWarning("snippet/loader type mismatch."); + return false; + } + + target->m_data = pattern; + + return true; +} + +void QCE::Snippets::PlainText::insert(QEditor *e) const +{ + /* + QDocumentCursor c = e->cursor(); + c.insertText(m_data); + e->setCursor(c); + */ + + e->write(m_data); +} + +// + +QString parsePlaceHolder(const QString& s, int& index, int max, QMap& p, int& line, int& column, int baseSize) +{ + QChar c; + QStringList segments; + int i = index, depth = 1, last = index + 1; + + while ( i + 1 < max ) + { + c = s.at(++i); + + if ( c == QLatin1Char('{') ) + { + ++depth; + } else if ( c == QLatin1Char('}') ) { + --depth; + + if ( !depth ) + { + segments << s.mid(last, i - last); + break; + } + } else if ( c == QLatin1Char(':') ) { + if ( depth == 1 ) + { + segments << s.mid(last, i - last); + last = i + 1; + } + } + } + + if ( segments.isEmpty() ) + { + qWarning("invalid placeholder"); + return QString(); + } + + int id = segments.at(0).toInt(); + + QCE::Snippets::Simple::PlaceHolder& ph = p[id]; + + if ( ph.length == -1 && segments.count() > 1 ) + { + // new placeholder + ph.length = segments.last().count(); + ph.lineOffset = line; + ph.columnOffset = column; + ph.defaultValue = segments.last(); + // TODO : support recursive snippetting of default value... + } else { + // mirror of an existing placeholder + QCE::Snippets::Simple::Anchor a; + a.lineOffset = line; + a.columnOffset = column; + if ( ph.defaultValue.isEmpty() ) + ph.unresolvedMirrors << baseSize << ph.mirrors.count(); + ph.mirrors << a; + } + + index = i + 1; + return ph.defaultValue; +} + +void performRelocation(QCE::Snippets::Simple::Anchor& a, const QHash >& relocationTable, int length) +{ + QHash >::const_iterator reloc = relocationTable.constFind(a.lineOffset); + + if ( reloc == relocationTable.constEnd() ) + return; + + int idx = 0; + int relocOffset = 0; + const QList& offsets = *reloc; + + while ( ((idx + 1) < offsets.count()) && (offsets.at(idx) <= a.columnOffset) ) + { + int off = offsets.at(++idx); + + if ( offsets.at(idx - 1) < a.columnOffset || off != length ) + relocOffset += off; + + ++idx; + } + + a.columnOffset += relocOffset; +} + +bool QCE::Snippets::Simple::loadSnippet(QSnippet *snip, const QString& pattern) +{ + Simple *target = dynamic_cast(snip); + + if ( !target ) + { + qWarning("snippet/loader type mismatch"); + return false; + } + + target->m_base.clear(); + target->m_placeHolders.clear(); + + int index = 0, line = 0, column = 0, max = pattern.length(); + + QString tmp; + QStringList base; + QMap p; + + while ( index < max ) + { + QChar c = pattern.at(index); + + if ( c == QLatin1Char('$') ) + { + base << tmp; + tmp.clear(); + + c = pattern.at(++index); + + if ( c == QLatin1Char('{') ) + { + QString val = parsePlaceHolder(pattern, index, max, p, line, column, base.count()); + base << val; + + if ( val.count() ) + { + int nl = val.count(QLatin1Char('\n')); + + line += nl; + + if ( nl ) + column = val.count() - val.lastIndexOf(QLatin1Char('\n')) - 1; + else + column += val.count(); + } + continue; + } else { + if ( c != QLatin1Char('$') ) + { + c = pattern.at(--index); + } + + ++column; + } + } else if ( c == QLatin1Char('\n') ) { + column = 0; + ++line; + } else { + ++column; + } + + tmp += c; + ++index; + } + + if ( tmp.count() ) + base << tmp; + + QHash > relocationTable; + QMap::iterator it = p.begin(); + + // first : build relocation table (in case several placeholders are on same line + while ( it != p.end() ) + { + if ( it->unresolvedMirrors.count() && it->length ) + { + for ( int i = 0; i + 1 < it->unresolvedMirrors.count(); ++i ) + { + int idx = it->unresolvedMirrors.at(i); + int anchor = it->unresolvedMirrors.at(++i); + + base[idx] = it->defaultValue; + + const Anchor& a = it->mirrors.at(anchor); + relocationTable[a.lineOffset] << a.columnOffset << it->length; + } + + it->unresolvedMirrors.clear(); + } + + ++it; + } + + it = p.begin(); + + // then : apply relocation and store the corrected placeholder data + while ( it != p.end() ) + { + performRelocation(*it, relocationTable, it->length); + + for ( int i = 0; i < it->mirrors.count(); ++i ) + performRelocation(it->mirrors[i], relocationTable, it->length); + + target->m_placeHolders << *it; + ++it; + } + + target->m_base = base.join(QString::null); + + return true; +} + +void QCE::Snippets::Simple::insert(QEditor *e) const +{ + // TODO : move into command and backup for proper undo/redo + e->clearPlaceHolders(); + + QDocument *d = e->document(); + QDocumentCursor c = e->cursor(); + + if ( c.isNull() ) + c = QDocumentCursor(d); + + int line = qMax(c.lineNumber(), 0), column = qMax(c.columnNumber(), 0); + + if ( line != c.lineNumber() || column != c.columnNumber() ) + c = QDocumentCursor(d, line, column); + + QSnippetInsertionCommand *cmd = new QSnippetInsertionCommand(e); + + QDocumentCommand *scmd = 0; + + if ( c.hasSelection() ) + { + QDocumentSelection sel = c.selection(); + + //qDebug("((%i, %i), (%i, %i))", sel.startLine, sel.start, sel.endLine, sel.end); + scmd = new QDocumentEraseCommand(sel.startLine, sel.start, sel.endLine, sel.end, d); + + cmd->addCommand(scmd); + + line = sel.startLine; + column = sel.start; + } + + //qDebug("%s", qPrintable(m_base)); + + if ( scmd ) + { + // trick to get insert command to init properly + scmd->redo(); + } + + cmd->addCommand(new QDocumentInsertCommand(line, column, m_base, d)); + + if ( scmd ) + { + // trick to get insert command to init properly + scmd->undo(); + } + + if ( m_placeHolders.count() ) + { + foreach ( const PlaceHolder& ph, m_placeHolders ) + { + QEditor::PlaceHolder eph; + eph.length = ph.length; + eph.cursor = QDocumentCursor(d, line + ph.lineOffset, ph.columnOffset + (ph.lineOffset ? 0 : column)); + //eph.affector = new StubAffector; + foreach ( const Anchor& a, ph.mirrors ) + eph.mirrors << QDocumentCursor(d, line + a.lineOffset, a.columnOffset + (a.lineOffset ? 0 : column)); + + cmd->addPlaceHolder(eph); + } + } + + d->execute(cmd); +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/snippets/qsnippet.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/snippets/qsnippet.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QSNIPPET_H_ +#define _QSNIPPET_H_ + +#include "qce-config.h" + +/*! + \file qsnippet.h + \brief Definition of the QSnippet class +*/ + +#include + +class QEditor; +class QSnippetManager; + +#include "qsnippetpatternloader.h" + +class QCE_EXPORT QSnippet +{ + friend class QSnippetManager; + + public: + inline QSnippet(const QSnippetPatternLoader *pl) : m_patternLoader(pl) {} + virtual ~QSnippet() {} + + inline QString name() const { return m_name; } + inline void setName(const QString& n) { m_name = n; } + + inline QStringList contexts() const { return m_contexts; } + inline void setContexts(const QStringList& l) { m_contexts = l; } + + inline QString pattern() const + { return m_pattern; } + + inline void setPattern(const QString& p) + { m_pattern = p; m_patternLoader->reloadSnippet(this, p); } + + virtual void insert(QEditor *e) const = 0; + + protected: + QString m_name; + QString m_pattern; + QStringList m_contexts; + const QSnippetPatternLoader *m_patternLoader; +}; + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/snippets/qsnippet_p.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/snippets/qsnippet_p.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QSNIPPET_P_H_ +#define _QSNIPPET_P_H_ + +/*! + \file qsnippet_p.h + \brief Definition of the QSnippetInsertionCommand class +*/ + +#include "qsnippet.h" +#include "qsnippetpatternloader.h" + +#include "qeditor.h" +#include "qdocument.h" +#include "qdocumentcursor.h" +#include "qdocumentcommand.h" + +class QSnippetInsertionCommand : public QDocumentCommandBlock +{ + public: + QSnippetInsertionCommand(QEditor *e); + virtual ~QSnippetInsertionCommand(); + + void addPlaceHolder(const QEditor::PlaceHolder& ph); + + virtual void addCommand(QDocumentCommand *c); + virtual void removeCommand(QDocumentCommand *c); + + virtual void redo(); + virtual void undo(); + + private: + QEditor *m_editor; + QDocumentCursor m_cursor; + QList m_placeHolders; +}; + +#define Q_SNIPPET(T) \ + friend class Loader; \ + public: \ + class Loader : public QSnippetPatternLoader \ + { \ + public: \ + virtual QString type() const { return ""#T; } \ + virtual QSnippet* loadSnippet(const QString& pattern) const \ + { \ + T *snip = new T(this); \ + snip->m_pattern = pattern; \ + bool ok = reloadSnippet(snip, pattern); \ + if ( !ok ) { delete snip; snip = 0; } \ + return snip; \ + } \ + virtual bool reloadSnippet(QSnippet* snip, const QString& pattern) const \ + { return T::loadSnippet(snip, pattern); } \ + }; \ + inline T(const QSnippetPatternLoader *pl) : QSnippet(pl) {} \ + private: \ + + +namespace QCE +{ + namespace Snippets + { + class PlainText : public QSnippet + { + Q_SNIPPET(PlainText) + + public: + virtual void insert(QEditor *e) const; + + static bool loadSnippet(QSnippet *snip, const QString& pattern); + + QString m_data; + }; + + class Simple : public QSnippet + { + Q_SNIPPET(Simple) + + public: + struct Anchor + { + Anchor() : lineOffset(0), columnOffset(0) {} + + int lineOffset; + int columnOffset; + }; + + struct PlaceHolder : public Anchor + { + PlaceHolder() : length(-1) {} + + int length; + QString defaultValue; + QList mirrors; + QList unresolvedMirrors; + }; + + virtual void insert(QEditor *e) const; + + static bool loadSnippet(QSnippet *snip, const QString& pattern); + + QString m_base; + QList m_placeHolders; + }; + } +} + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/snippets/qsnippetbinding.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/snippets/qsnippetbinding.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qsnippetbinding.h" + +/*! + \file qsnippetbinding.cpp + \brief Implementation of the QSnippetBinding class +*/ + +#include "qsnippet.h" +#include "qsnippetmanager.h" + +#include "qeditor.h" +#include "qdocumentcursor.h" + +#include + +/* +class QSnippetCommand : public QEditorInputBinding::Command +{ + public: + QSnippetCommand(QSnippetManager *m, int idx) + : index(idx), manager(m) + { + + } + + virtual void exec(QEditor *e) + { + if ( index < manager->snippetCount() ) + manager->snippet(index)->insert(e); + } + + private: + int index; + QSnippetManager *manager; +}; +*/ + +class QSnippetCommand : public QEditorInputBinding::Command +{ + public: + QSnippetCommand(QSnippet *s) + : snip(s) + { + + } + + virtual void exec(QEditor *e) + { + snip->insert(e); + } + + private: + QSnippet *snip; +}; + +QSnippetBinding::QSnippetBinding(QSnippetManager *manager) + : m_manager(manager) +{ + //for ( int i = 0; i < 10; ++i ) + // setMapping(QKeySequence(Qt::Key_F1 + i), new SnippetCommand(manager, i)); +} + +QString QSnippetBinding::id() const +{ + return "snippet binding"; +} + +QString QSnippetBinding::name() const +{ + return "snippet binding"; +} + +bool QSnippetBinding::keyPressEvent(QKeyEvent *event, QEditor *editor) +{ + /* + if ( event->modifiers() & Qt::ControlModifier ) + { + for ( int i = 0; i < qMin(10, m->snippetCount()); ++i ) + { + if ( event->key() == (Qt::Key_F1 + i) ) + { + m->snippet(i)->insert(editor); + return true; + } + } + } + */ + + if ( (event->modifiers() & Qt::AltModifier) && (event->key() == Qt::Key_Space || event->text() == " ") ) + { + QDocumentCursor c = editor->cursor(); + + //c.select(QDocumentCursor::SelectWord); + + if ( !c.hasSelection() ) + { + c.movePosition(1, QDocumentCursor::PreviousWord, QDocumentCursor::KeepAnchor); + editor->setCursor(c); + } + + QString s = c.selectedText(); + + for ( int i = 0; i < m_manager->snippetCount(); ++i ) + { + QSnippet *snip = m_manager->snippet(i); + + if ( snip->name() == s ) + { + snip->insert(editor); + return true; + } + } + } + + return QEditorInputBinding::keyPressEvent(event, editor); +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/snippets/qsnippetbinding.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/snippets/qsnippetbinding.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QSNIPPET_BINDING_H_ +#define _QSNIPPET_BINDING_H_ + +#include "qce-config.h" + +/*! + \file qsnippetbinding.h + \brief Definition of the QSnippetBinding class +*/ + +#include "qeditorinputbinding.h" + +class QSnippetManager; + +class QCE_EXPORT QSnippetBinding : public QEditorInputBinding +{ + public: + QSnippetBinding(QSnippetManager *manager); + + virtual QString id() const; + virtual QString name() const; + + virtual bool keyPressEvent(QKeyEvent *event, QEditor *editor); + + private: + QSnippetManager *m_manager; +}; + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/snippets/qsnippetedit.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/snippets/qsnippetedit.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qsnippetedit.h" + +/*! + \file qsnippetedit.cpp + \brief Implementation of the QSnippetEdit widget +*/ + +#include "qsnippet.h" +#include "qsnippetmanager.h" + +#include + +/*! + \class QSnippetEdit + \brief A widget for snippets management + + +*/ + +QSnippetEdit::QSnippetEdit(QWidget *p) + : QWidget(p), m_editedSnippet(-1), m_manager(0) +{ + setupUi(this); + setEnabled(false); +} + +QSnippetEdit::QSnippetEdit(QSnippetManager *mgr, QWidget *p) + : QWidget(p), m_editedSnippet(-1), m_manager(0) +{ + setupUi(this); + setSnippetManager(mgr); +} + +QSnippetManager* QSnippetEdit::snippetManager() const +{ + return m_manager; +} + +void QSnippetEdit::setSnippetManager(QSnippetManager *mgr) +{ + if ( m_manager ) + { + disconnect( m_manager , SIGNAL( snippetAdded(QSnippet*) ), + this , SLOT ( snippetAdded(QSnippet*) ) ); + + disconnect( m_manager , SIGNAL( snippetRemoved(int) ), + this , SLOT ( snippetRemoved(int) ) ); + + QListWidgetItem *empty = lwSnippets->takeItem(0); + lwSnippets->clear(); + lwSnippets->addItem(empty); + } + + m_manager = mgr; + setEnabled(mgr); + + if ( m_manager ) + { + connect(m_manager , SIGNAL( snippetAdded(QSnippet*) ), + this , SLOT ( snippetAdded(QSnippet*) ) ); + + connect(m_manager , SIGNAL( snippetRemoved(int) ), + this , SLOT ( snippetRemoved(int) ) ); + + for ( int i = 0; i < m_manager->snippetCount(); ++i ) + { + lwSnippets->addItem(m_manager->snippet(i)->name()); + } + } + + lwSnippets->setCurrentItem(0); +} + +void QSnippetEdit::snippetRemoved(int i) +{ + delete lwSnippets->takeItem(i + 1); +} + +void QSnippetEdit::snippetAdded(QSnippet *s) +{ + lwSnippets->addItem(s->name()); +} + +void QSnippetEdit::retranslate() +{ + QSnippetManager *mgr = snippetManager(); + setSnippetManager(0); + + lwSnippets->clear(); + retranslateUi(this); + + setSnippetManager(mgr); +} + +static const QRegExp _cxt_splitter("\\s*,\\s*"); + +bool QSnippetEdit::maybeSave() +{ + static const QRegExp nonTrivial("\\S"); + + QString pattern = eSnippet->text(); + + if ( pattern.endsWith('\n') ) + pattern.chop(1); + + QString name = leSnippetName->text(); + QStringList contexts = leSnippetScope->text().split(_cxt_splitter); + bool nonTrivialPattern = pattern.contains(nonTrivial); + + if ( m_editedSnippet >= 0 ) + { + QSnippet *snip = m_manager->snippet(m_editedSnippet); + + if ( snip->pattern() != pattern ) + { + int ret = QMessageBox::warning(this, + tr("Unsaved changes"), + tr("Do you want to save pattern changes to snippet %1 ?") + .arg(snip->name()), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Yes + ); + + if ( ret == QMessageBox::Cancel ) + { + return true; + } else if ( ret == QMessageBox::Yes ) { + snip->setPattern(pattern); + } + } + } else if ( nonTrivialPattern ) { + int ret = QMessageBox::warning(this, + tr("Unsaved changes"), + tr("The current snippet data will be discarded. Do you want to continue?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes + ); + + if ( ret == QMessageBox::No ) + { + return true; + } + } + + return false; +} + +void QSnippetEdit::on_lwSnippets_currentRowChanged(int idx) +{ + if ( (idx - 1) == m_editedSnippet ) + return; + + if ( maybeSave() ) + { + lwSnippets->setCurrentRow(m_editedSnippet); + return; + } + + m_editedSnippet = idx - 1; + + if ( idx <= 0 ) + { + eSnippet->setText(QString()); + leSnippetName->setText(QString()); + leSnippetScope->setText(QString()); + } else { + QSnippet *snip = m_manager->snippet(m_editedSnippet); + + eSnippet->setText(snip->pattern()); + leSnippetName->setText(snip->name()); + leSnippetScope->setText(snip->contexts().join(",")); + //eSnippet->highlight(); + } + + eSnippet->setFocus(); +} + +void QSnippetEdit::on_leSnippetName_editingFinished() +{ + if ( m_editedSnippet < 0 ) + return; + + QSnippet *snip = m_manager->snippet(m_editedSnippet); + + snip->setName(leSnippetName->text()); + + lwSnippets->item(m_editedSnippet + 1)->setText(snip->name()); +} + +void QSnippetEdit::on_leSnippetScope_editingFinished() +{ + if ( m_editedSnippet < 0 ) + return; + + QSnippet *snip = m_manager->snippet(m_editedSnippet); + + snip->setContexts(leSnippetScope->text().split(_cxt_splitter)); +} + +void QSnippetEdit::on_tbCreateSnippet_clicked() +{ + QString name = leSnippetName->text(); + QString pattern = eSnippet->text(); + QStringList contexts = leSnippetScope->text().split(_cxt_splitter); + + if ( pattern.endsWith('\n') ) + pattern.chop(1); + + if ( name.isEmpty() || pattern.isEmpty() ) + { + QMessageBox::information(0, tr("Missing data"), tr("Please provide a name and a content to create a new snippet")); + return; + } + + // TODO : allow pattern loader choice... + bool ok = m_manager->loadSnippetFromString(name, pattern, "Simple"); + + if ( !ok ) + { + QMessageBox::warning(0, tr("Error"), tr("Invalid snippet pattern.")); + return; + } + + eSnippet->setText(QString()); + leSnippetScope->clear(); + leSnippetName->clear(); + + + QSnippet *snip = m_manager->snippet(m_manager->snippetCount() - 1); + //snip->setName(name); + snip->setContexts(contexts); + + + lwSnippets->setCurrentRow(0); +} + +void QSnippetEdit::on_tbDeleteSnippet_clicked() +{ + int row = lwSnippets->currentRow() - 1; + + if ( row < 0 ) + { + QMessageBox::warning(0, tr("Error"), tr("Please select a valid snippet to erase")); + return; + } + + m_manager->removeSnippet(row); +} + +void QSnippetEdit::on_bMoreSnippets_clicked() +{ + +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/snippets/qsnippetedit.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/snippets/qsnippetedit.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QSNIPPET_EDIT_H_ +#define _QSNIPPET_EDIT_H_ + +#include "qce-config.h" + +/*! + \file qsnippetedit.h + \brief Definition of the QSnippetEdit widget +*/ + +#include +#include "ui_snippetedit.h" + +class QSnippet; +class QSnippetManager; + +class QCE_EXPORT QSnippetEdit : public QWidget, private Ui::SnippetEdit +{ + Q_OBJECT + + public: + QSnippetEdit(QWidget *p = 0); + QSnippetEdit(QSnippetManager *mgr, QWidget *p = 0); + + QSnippetManager* snippetManager() const; + + public slots: + void setSnippetManager(QSnippetManager *mgr); + + bool maybeSave(); + + void retranslate(); + + private slots: + void on_lwSnippets_currentRowChanged(int idx); + + void on_leSnippetName_editingFinished(); + void on_leSnippetScope_editingFinished(); + + void on_tbCreateSnippet_clicked(); + void on_tbDeleteSnippet_clicked(); + void on_bMoreSnippets_clicked(); + + void snippetRemoved(int i); + void snippetAdded(QSnippet *s); + + private: + int m_editedSnippet; + QSnippetManager *m_manager; +}; + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/snippets/qsnippetmanager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/snippets/qsnippetmanager.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +/*! + \file qsnippetmanager.cpp + \brief Implementation of the QSnippetManager class +*/ + +#include "qsnippetmanager.h" + +#include "qsnippet_p.h" + +#include +#include +#include +#include + +/* +class StubAffector : public QEditor::PlaceHolder::Affector +{ + public: + virtual void affect(const QStringList& base, int ph, const QKeyEvent *e, int mirror, QString& after) const + { + after = after.toUpper(); + } +}; +*/ + +/*! + \class QSnippetManager + \brief A class managing code snippets + + +*/ + +QSnippetManager::QSnippetManager(QObject *p) + : QObject(p) +{ + addPatternLoader(new QCE::Snippets::PlainText::Loader); + addPatternLoader(new QCE::Snippets::Simple::Loader); +} + +QSnippetManager::~QSnippetManager() +{ + qDeleteAll(m_snippets); + qDeleteAll(m_patternLoaders); +} + +int QSnippetManager::snippetCount() const +{ + return m_snippets.count(); +} + +QSnippet* QSnippetManager::snippet(int i) const +{ + return i >= 0 && i < m_snippets.count() ? m_snippets.at(i) : 0; +} + +void QSnippetManager::removeSnippet(int i, bool cleanup) +{ + if ( i < 0 || i >= m_snippets.count() ) + return; + + QSnippet *snip = m_snippets.takeAt(i); + + emit snippetRemoved(i); + emit snippetRemoved(snip); + + if ( cleanup ) + delete snip; +} + +void QSnippetManager::removeSnippet(QSnippet *snip) +{ + int idx = m_snippets.indexOf(snip); + + if ( idx == -1 ) + return; + + m_snippets.removeAt(idx); + + emit snippetRemoved(idx); + emit snippetRemoved(snip); +} + +void QSnippetManager::addSnippet(QSnippet *snip) +{ + if ( !snip ) + return; + + m_snippets << snip; + emit snippetAdded(snip); +} + +bool QSnippetManager::loadSnippetFromString(const QString& name, const QString& s, const QString& type) +{ + QSnippetPatternLoader *pl = patternLoader(type); + + QSnippet *snip = pl->loadSnippet(s); + + if ( snip ) + snip->setName(name); + + addSnippet(snip); + + return snip; +} + +bool QSnippetManager::loadSnippetFromFile(const QString& file, const QString& type) +{ + QFile f(file); + + if ( !f.open(QFile::ReadOnly | QFile::Text) ) + { + qWarning("Unable to load snippet from %s", qPrintable(file)); + return false; + } + + //qDebug("loading from : %s", qPrintable(file)); + + QString s = QString::fromLocal8Bit(f.readAll()); + + static const QRegExp meta("# name:(\\S+) context:(\\S*)[^\n]*\n"); + + int idx = meta.indexIn(s); + bool metaMatch = idx != -1; + + if ( metaMatch ) + { + //qDebug("meta! : %i => %s", idx, qPrintable(meta.cap(0))); + s.remove(idx, meta.matchedLength()); + } + + if ( s.endsWith('\n') ) + s.chop(1); + + bool ok = loadSnippetFromString(metaMatch ? meta.cap(1) : QFileInfo(file).baseName(), s, type); + + if ( ok ) + { + QSnippet *snip = m_snippets.last(); + snip->setContexts(metaMatch ? meta.cap(2).split(",") : QStringList("all")); + } + + return ok; +} + +QSnippetPatternLoader* QSnippetManager::patternLoader(const QString& type) const +{ + foreach ( QSnippetPatternLoader *pl, m_patternLoaders ) + { + if ( pl->type() == type ) + return pl; + } + + return 0; +} + +void QSnippetManager::saveSnippetsToDirectory(const QString& path) +{ + QDir d(path); + + foreach ( QSnippet *snip, m_snippets ) + { + QFile f(d.filePath(snip->name() + ".qcs")); + + if ( !f.open(QFile::WriteOnly | QFile::Text) ) + continue; + + QTextStream s(&f); + s << "# name:" << snip->name() << " context:" << snip->contexts().join(",") << endl; + s << snip->pattern(); + } +} + +void QSnippetManager::loadSnippetsFromDirectory(const QString& path) +{ + QDir d(path); + + QFileInfoList l = d.entryInfoList(QDir::Files | QDir::Readable); + + foreach ( const QFileInfo& info, l ) + { + if ( info.suffix() != "qcs" ) + continue; + + // TODO : pattern selection? + loadSnippetFromFile(info.absoluteFilePath(), "Simple"); + } +} + +void QSnippetManager::addPatternLoader(QSnippetPatternLoader *pl) +{ + m_patternLoaders << pl; +} + +void QSnippetManager::removePatternLoader(QSnippetPatternLoader *pl) +{ + m_patternLoaders.removeAll(pl); +} + +QString QSnippetManager::typeGuess(const QString& pattern) const +{ + Q_UNUSED(pattern) + + return "Simple"; +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/snippets/qsnippetmanager.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/snippets/qsnippetmanager.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QSNIPPET_MANAGER_H_ +#define _QSNIPPET_MANAGER_H_ + +#include "qce-config.h" + +/*! + \file qsnippetmanager.h + \brief Definition of the QSnippetManager class +*/ + +#include + +class QSnippet; +class QSnippetPatternLoader; + +class QCE_EXPORT QSnippetManager : public QObject +{ + Q_OBJECT + + public: + QSnippetManager(QObject *p = 0); + virtual ~QSnippetManager(); + + int snippetCount() const; + QSnippet* snippet(int i) const; + void removeSnippet(int i, bool cleanup = true); + + bool loadSnippetFromFile(const QString& file, const QString& type = QString()); + bool loadSnippetFromString(const QString& name, const QString& pattern, const QString& type = QString()); + + void saveSnippetsToDirectory(const QString& path); + void loadSnippetsFromDirectory(const QString& path); + + public slots: + void addSnippet(QSnippet *s); + void removeSnippet(QSnippet *s); + + void addPatternLoader(QSnippetPatternLoader *pl); + void removePatternLoader(QSnippetPatternLoader *pl); + + signals: + void snippetAdded(QSnippet *s); + + void snippetRemoved(int i); + void snippetRemoved(QSnippet *s); + + private: + QString typeGuess(const QString& pattern) const; + QSnippetPatternLoader* patternLoader(const QString& type) const; + + QList m_snippets; + QList m_patternLoaders; +}; + +#endif // !_SNIPPETS_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/snippets/qsnippetpatternloader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/snippets/qsnippetpatternloader.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QSNIPPET_PATTERN_LOADER_H_ +#define _QSNIPPET_PATTERN_LOADER_H_ + +/*! + \file qsnippetpatternloader.h + \brief Definition of the QSnippetPatternLoader class +*/ + +class QString; + +class QSnippet; + +class QSnippetPatternLoader +{ + public: + virtual ~QSnippetPatternLoader() {} + + virtual QString type() const = 0; + + virtual QSnippet* loadSnippet(const QString& pattern) const = 0; + virtual bool reloadSnippet(QSnippet* snip, const QString& pattern) const = 0; +}; + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/snippets/snippetedit.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/snippets/snippetedit.ui Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,190 @@ + + + SnippetEdit + + + + 0 + 0 + 548 + 486 + + + + + + + + + + + 0 + 0 + + + + + 200 + 16777215 + + + + + empty + + + + new.pngnew.png + + + + + + + + + 0 + 0 + + + + Edit snippet + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + 0 + 0 + + + + Name/trigger + + + leSnippetName + + + + + + + + 0 + 0 + + + + Enter the name of the code snippet, which will also be its full-text trigger, if enabled. + + + + + + + Scope(s) + + + leSnippetScope + + + + + + + Enter a coma-separated list of languages in which the snippet can be used. + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 20 + 40 + + + + + + + + More + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + - + + + + + + + + + + + + + + + + QEditor + QFrame +
qeditor.h
+ 1 +
+
+ + lwSnippets + leSnippetName + leSnippetScope + tbCreateSnippet + tbDeleteSnippet + bMoreSnippets + + + +
diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/editconfig.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/editconfig.ui Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,325 @@ + + EditorConfig + + + + 0 + 0 + 575 + 586 + + + + + + + + 0 + 0 + + + + Font + + + + 4 + + + + + + Monospace + + + + + DejaVu Sans Mono + + + + + + + + QAbstractSpinBox::UpDownArrows + + + 6 + + + 50 + + + 10 + + + + + + + + 0 + 0 + + + + + Monospace + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + text which <i>should</i> be a <b>fair</b> test of the font + + + Qt::AlignCenter + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + + + + Tabulators && Whitespaces + + + + 5 + + + + + + + + 0 + 0 + + + + Tab width + + + spnTabWidth + + + + + + + 4 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Show leading whitespaces + + + true + + + + + + + Show tabs which are neither leading nor trailing + + + true + + + + + + + Show trailing whitespaces + + + true + + + + + + + Replace tabs by blanks + + + + + + + + + + Load && Save + + + + + + Default encoding + + + + + + + + + + Preserve line endings + + + true + + + + + + + false + + + + Local + + + + + Unix/Linux + + + + + DOS/Windows + + + + + Old Mac + + + + + + + + Remove trailing spaces + + + + + + + false + + + Preserve trailing indent + + + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 492 + 10 + + + + + + + + + + chkDetectLE + toggled(bool) + cbLineEndings + setDisabled(bool) + + + 190 + 445 + + + 279 + 444 + + + + + chkAutoRemoveTrailingWhitespace + toggled(bool) + chkPreserveTrailingIndent + setEnabled(bool) + + + 176 + 475 + + + 287 + 479 + + + + + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/formatconfig.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/formatconfig.ui Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,114 @@ + + FormatConfig + + + + 0 + 0 + 539 + 383 + + + + + 0 + 0 + + + + Form + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + Scheme : + + + + + + + + + + + + + + 0 + 0 + + + + QAbstractItemView::NoSelection + + + QAbstractItemView::SelectRows + + + + Identifier + + + + + + + + + + + + + + + + + + + + O + + + + + + + + + + W + + + + + + + + + + + + + + + + + + + + + + + + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/gotoline.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/gotoline.ui Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,77 @@ + + + GotoLine + + + + 0 + 0 + 513 + 45 + + + + Form + + + + + + + + + + + + + &Go to line : + + + spLine + + + + + + + + 0 + 0 + + + + + + + 1 + + + + + + + G&o + + + + + + + 1 + + + Qt::Horizontal + + + + + + + spLine + bGo + bClose + slLine + + + + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/gotolinedialog.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/gotolinedialog.ui Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,147 @@ + + GotoDialog + + + + 0 + 0 + 210 + 103 + + + + + 0 + 0 + + + + Goto line ... + + + + + + + 0 + 0 + + + + Select the line you want to go to : + + + spinLine + + + + + + + 1 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + line + + + 1 + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + spinLine + slideLine + buttonBox + + + + + + + buttonBox + accepted() + GotoDialog + accept() + + + 67 + 165 + + + 205 + 64 + + + + + buttonBox + rejected() + GotoDialog + reject() + + + 44 + 164 + + + 30 + 67 + + + + + slideLine + sliderMoved(int) + spinLine + setValue(int) + + + 35 + 99 + + + 299 + 100 + + + + + spinLine + valueChanged(int) + slideLine + setValue(int) + + + 299 + 112 + + + 82 + 99 + + + + + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qcalltip.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qcalltip.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qcalltip.h" + +/*! + \file qcalltip.cpp + \brief Implementation of the QCallTip class +*/ + +#include +#include + +/*! + \class QCallTip + \brief A widget dedicated to calltips display +*/ + +QCallTip::QCallTip(QWidget *p) + : QWidget(p), m_index(0) +{ + setCursor(Qt::ArrowCursor); + setFocusPolicy(Qt::StrongFocus); + setAttribute(Qt::WA_DeleteOnClose); +} + +QCallTip::~QCallTip() +{ + +} + +QStringList QCallTip::tips() const +{ + return m_tips; +} + +void QCallTip::setTips(const QStringList& l) +{ + m_tips = l; + m_index = 0; +} + +static int arrowWidth = 14; + +void QCallTip::paintEvent(QPaintEvent *e) +{ + Q_UNUSED(e) + + QPainter p(this); + QFontMetrics fm = fontMetrics(); + + m_up = m_down = QRect(); + + bool bPrev = m_index, bNext = (m_index + 1) < m_tips.count(); + int offset = 3, whalf = arrowWidth / 2 - 3; //, hhalf = height() / 2; + + QRect bg(0, 0, fm.width(m_tips.at(m_index)) + 6, fm.height()); + + if ( bPrev ) + { + bg.setWidth(bg.width() + arrowWidth); + } + + if ( bNext ) + { + bg.setWidth(bg.width() + arrowWidth); + } + + p.fillRect(bg, QColor(0xca, 0xff, 0x70)); + //p.drawRect(bg); + + p.save(); + + p.setPen(QColor(0x00, 0x00, 0x00)); + p.drawLine(0, height() - 1, bg.width() - 1, height() - 1); + p.drawLine(bg.width() - 1, height() - 1, bg.width() - 1, 0); + + p.setPen(QColor(0xc0, 0xc0, 0xc0)); + p.drawLine(0, height() - 1, 0, 0); + p.drawLine(0, 0, bg.width() - 1, 0); + + p.restore(); + + int top = height() / 3, bottom = height() - height() / 3; + + if ( bPrev ) + { + int x = offset + arrowWidth / 2 - 1; + + QPoint pts[] = { + QPoint(x - whalf, bottom), + QPoint(x + whalf, bottom), + QPoint(x, top) + }; + + p.drawPolygon(pts, sizeof(pts) / sizeof(QPoint), Qt::WindingFill); + + m_up = QRect(offset, 0, offset + arrowWidth, height()); + offset += arrowWidth; + } + + if ( bNext ) + { + int x = offset + arrowWidth / 2 - 1; + + QPoint pts[] = { + QPoint(x - whalf, top), + QPoint(x + whalf, top), + QPoint(x, bottom) + }; + + p.drawPolygon(pts, sizeof(pts) / sizeof(QPoint), Qt::WindingFill); + + m_down = QRect(offset, 0, offset + arrowWidth, height()); + offset += arrowWidth; + } + + p.drawText(offset, fm.ascent() + 2, m_tips.at(m_index)); + + setFixedSize(bg.size() + QSize(1, 1)); +} + +void QCallTip::keyPressEvent(QKeyEvent *e) +{ + if ( e->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier) ) + { + close(); + + if ( parentWidget() ) + parentWidget()->setFocus(); + + e->ignore(); + return; + } + + QString prefix, text = e->text(); + + switch ( e->key() ) + { + case Qt::Key_Escape : + close(); + + if ( parentWidget() ) + parentWidget()->setFocus(); + + e->accept(); + break; + + case Qt::Key_Enter : + case Qt::Key_Return : + case Qt::Key_Tab : + //hide(); + + close(); + + if ( parentWidget() ) + parentWidget()->setFocus(); + + e->ignore(); + + break; + + case Qt::Key_Up : + + if ( m_index ) + --m_index; + + e->accept(); + update(); + break; + + case Qt::Key_Down : + + if ( (m_index + 1) < m_tips.count() ) + ++m_index; + + e->accept(); + update(); + break; + + case Qt::Key_Backspace : + + close(); + + if ( parentWidget() ) + parentWidget()->setFocus(); + + e->ignore(); + break; + + case Qt::Key_Shift : + case Qt::Key_Alt : + case Qt::Key_Control : + e->ignore(); + break; + + default: + + if ( text.count() && text.at(0).isPrint() ) + { + + } else { + close(); + + if ( parentWidget() ) + parentWidget()->setFocus(); + + } + + e->ignore(); + + break; + } +} + +void QCallTip::focusInEvent(QFocusEvent *e) +{ + QWidget::focusInEvent(e); +} + +void QCallTip::focusOutEvent(QFocusEvent *e) +{ + QWidget::focusOutEvent(e); + + close(); + + if ( parentWidget() ) + parentWidget()->setFocus(); + +} + +void QCallTip::mousePressEvent(QMouseEvent *e) +{ + if ( m_index && m_up.isValid() && m_up.contains(e->pos()) ) + { + --m_index; + } else if ( + ((m_index + 1) < m_tips.count()) + && + m_down.isValid() + && + m_down.contains(e->pos()) + ) + { + ++m_index; + } else { + close(); + + if ( parentWidget() ) + parentWidget()->setFocus(); + + } + + e->accept(); + + update(); +} + +void QCallTip::mouseReleaseEvent(QMouseEvent *e) +{ + e->accept(); +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qcalltip.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qcalltip.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QCALL_TIP_H_ +#define _QCALL_TIP_H_ + +#include "qce-config.h" + +/*! + \file qcalltip.h + \brief Definition of the QCallTip class +*/ + +#include + +class QCE_EXPORT QCallTip : public QWidget +{ + public: + QCallTip(QWidget *p = 0); + virtual ~QCallTip(); + + QStringList tips() const; + void setTips(const QStringList& l); + + protected: + virtual void paintEvent(QPaintEvent *e); + virtual void keyPressEvent(QKeyEvent *e); + virtual void focusInEvent(QFocusEvent *e); + virtual void focusOutEvent(QFocusEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + + private: + int m_index; + QStringList m_tips; + QRect m_up, m_down; +}; + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qeditconfig.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qeditconfig.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,614 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qeditconfig.h" + +/*! + \file qeditconfig.cpp + \brief Implementation of the QEditConfig class. + + \see QEditConfig +*/ + +#include "qeditor.h" +#include "qdocument.h" +#include "qdocument_p.h" + +#include +#include + +/*! + \ingroup dialogs + @{ + + \class QEditConfig + \brief A minimalistic, easy to embed, settings widget. + +*/ + +QEditConfig::QEditConfig(QWidget *w) + : QWidget(w), m_direct(false) +{ + setupUi(this); + + QStringList l; + QList ens = QTextCodec::availableCodecs(); + + foreach ( QByteArray b, ens ) + l << QString::fromLatin1(b); + + cbEncoding->clear(); + cbEncoding->addItems(l); + + restore(); +} + +/*! + \brief run-time translation entry point +*/ +void QEditConfig::retranslate() +{ + retranslateUi(this); +} + +/*! + \brief +*/ +bool QEditConfig::hasUnsavedChanges() const +{ + if ( m_direct ) + return false; + + QFont font = cbFont->currentFont(); + //font.setPointSize(spnFontSize->value()); + + const QFont& docFont = QDocument::font(); + + if ( font.family() != docFont.family() || spnFontSize->value() != docFont.pointSize() ) + { + //qDebug("font!"); + return true; + } + + if ( spnTabWidth->value() != QDocument::tabStop() ) + { + //qDebug("tab stop!"); + return true; + } + + QDocument::LineEnding le = QDocument::defaultLineEnding(); + + if ( chkDetectLE->isChecked() ) + { + if ( le != QDocument::Conservative ) + { + //qDebug("conservative line endings! : %i", le); + return true; + } + } else { + if ( le != QDocument::LineEnding(cbLineEndings->currentIndex() + 1) ) + { + //qDebug("line endings!"); + return true; + } + } + + QDocument::WhiteSpaceMode ws = QDocument::ShowNone; + + if ( chkShowLeadingWhitespace->isChecked() ) + ws |= QDocument::ShowLeading; + + if ( chkShowTrailingWhitespace->isChecked() ) + ws |= QDocument::ShowTrailing; + + if ( chkShowTabsInText->isChecked() ) + ws |= QDocument::ShowTabs; + + if ( ws != QDocument::showSpaces() ) + { + //qDebug("spaces!"); + return true; + } + + QTextCodec *c = QEditor::defaultCodec(); + + if ( cbEncoding->currentText() == "System" ) + { + if ( c && c->name() != "System" ) + { + //qDebug("system codec!"); + return true; + } + } else { + if ( !c || c->name() != cbEncoding->currentText().toLatin1() ) + { + //qDebug("codec!"); + return true; + } + } + + int flags = QEditor::defaultFlags(); + + if ( chkReplaceTabs->isChecked() ) + flags |= QEditor::ReplaceTabs; + else + flags &= ~QEditor::ReplaceTabs; + + if ( chkAutoRemoveTrailingWhitespace->isChecked() ) + flags |= QEditor::RemoveTrailing; + else + flags &= ~QEditor::RemoveTrailing; + + if ( chkPreserveTrailingIndent->isChecked() ) + flags |= QEditor::PreserveTrailingIndent; + else + flags &= ~QEditor::PreserveTrailingIndent; + + if ( flags != QEditor::defaultFlags() ) + { + //qDebug("flags!"); + return true; + } + + return false; +} + +/*! + \return whether user changes are immediately applied +*/ +bool QEditConfig::applyImmediately() const +{ + return m_direct; +} + +/*! + \brief Set whether user changes are immediately applied +*/ +void QEditConfig::setApplyImmediately(bool y) +{ + m_direct = y; +} + +/*! + \brief Apply changes +*/ +void QEditConfig::apply() +{ + QFont font = cbFont->currentFont(); + font.setPointSize(spnFontSize->value()); + + QDocument::setFont(font); + QDocument::setTabStop(spnTabWidth->value()); + + if ( chkDetectLE->isChecked() ) + QDocument::setDefaultLineEnding(QDocument::Conservative); + else + QDocument::setDefaultLineEnding(QDocument::LineEnding(cbLineEndings->currentIndex() + 1)); + + QDocument::WhiteSpaceMode ws = QDocument::ShowNone; + + if ( chkShowLeadingWhitespace->isChecked() ) + ws |= QDocument::ShowLeading; + + if ( chkShowTrailingWhitespace->isChecked() ) + ws |= QDocument::ShowTrailing; + + if ( chkShowTabsInText->isChecked() ) + ws |= QDocument::ShowTabs; + + QDocument::setShowSpaces(ws); + + if ( cbEncoding->currentText() == "System" ) + QEditor::setDefaultCodec(0, QEditor::UpdateAll); + else + QEditor::setDefaultCodec(cbEncoding->currentText().toLatin1(), QEditor::UpdateAll); + + int flags = QEditor::defaultFlags(); + + if ( chkReplaceTabs->isChecked() ) + flags |= QEditor::ReplaceTabs; + else + flags &= ~QEditor::ReplaceTabs; + + if ( chkAutoRemoveTrailingWhitespace->isChecked() ) + flags |= QEditor::RemoveTrailing; + else + flags &= ~QEditor::RemoveTrailing; + + if ( chkPreserveTrailingIndent->isChecked() ) + flags |= QEditor::PreserveTrailingIndent; + else + flags &= ~QEditor::PreserveTrailingIndent; + + QEditor::setDefaultFlags(flags); +} + +/*! + \brief Reset the subcontrols to reflect the current settings + + The name can be a bit misleading at first, it has been chosen + because it directly maps to the effect a "cancel" button would + have on the widget +*/ +void QEditConfig::cancel() +{ + // reload the current config + + bool oldDir = m_direct; + + m_direct = false; + + cbFont->setFont(QDocument::font()); + spnFontSize->setValue(QDocument::font().pointSize()); + + spnTabWidth->setValue(QDocument::tabStop()); + + QDocument::WhiteSpaceMode ws = QDocument::showSpaces(); + chkShowTabsInText->setChecked(ws & QDocument::ShowTabs); + chkShowLeadingWhitespace->setChecked(ws & QDocument::ShowLeading); + chkShowTrailingWhitespace->setChecked(ws & QDocument::ShowTrailing); + + QDocument::LineEnding le = QDocument::defaultLineEnding(); + chkDetectLE->setChecked(le == QDocument::Conservative); + cbLineEndings->setCurrentIndex(le ? le - 1 : 0); + + int flags = QEditor::defaultFlags(); + chkReplaceTabs->setChecked(flags & QEditor::ReplaceTabs); + chkAutoRemoveTrailingWhitespace->setChecked(flags & QEditor::RemoveTrailing); + chkPreserveTrailingIndent->setChecked(flags & QEditor::PreserveTrailingIndent); + + QTextCodec *c = QEditor::defaultCodec(); + cbEncoding->setCurrentIndex(cbEncoding->findText(c ? c->name() : QTextCodec::codecForLocale()->name())); + + m_direct = oldDir; +} + +/*! + \brief Restore default values for all subcontrols + + \note The widgets are changed but these changes are NOT applied. +*/ +void QEditConfig::restore() +{ + // restore default configuration + + bool oldDir = m_direct; + + m_direct = false; + + QFont font("Monospace", 10); + font.setStyleHint(QFont::Courier); + + cbFont->setFont(font); + spnFontSize->setValue(10); + + spnTabWidth->setValue(4); + + chkShowTabsInText->setChecked(true); + chkShowLeadingWhitespace->setChecked(true); + chkShowTrailingWhitespace->setChecked(true); + + chkDetectLE->setChecked(true); + cbLineEndings->setCurrentIndex(0); + + chkReplaceTabs->setChecked(false); + chkAutoRemoveTrailingWhitespace->setChecked(false); + chkPreserveTrailingIndent->setChecked(true); + + cbEncoding->setCurrentIndex(cbEncoding->findText(QTextCodec::codecForLocale()->name())); + + m_direct = oldDir; +} + +/*! + \brief Fills a settings map from the state of the subcontrols +*/ +QMap QEditConfig::dumpKeys() const +{ + QMap m; + + QFont font = cbFont->currentFont(); + font.setPointSize(spnFontSize->value()); + + m.insert("font", font); + m.insert("tab_width", spnTabWidth->value()); + + m.insert("show_leading_whitespace", chkShowLeadingWhitespace->isChecked()); + m.insert("show_trailing_whitespace", chkShowTrailingWhitespace->isChecked()); + m.insert("show_tabs_in_text", chkShowTabsInText->isChecked()); + + m.insert("replace_tabs", chkReplaceTabs->isChecked()); + m.insert("remove_trailing", chkAutoRemoveTrailingWhitespace->isChecked()); + m.insert("preserve_trailing_indent", chkPreserveTrailingIndent->isChecked()); + + m.insert("encoding", cbEncoding->currentText()); + + if ( chkDetectLE->isChecked() ) + m.insert("line_endings", (int)QDocument::Conservative); + else + m.insert("line_endings", cbLineEndings->currentIndex() + 1); + + return m; +} + +/*! + \brief Fills the widget subcontrols from a settings map + + \param keys a key/value map that can be obtained from QSettings +*/ +void QEditConfig::loadKeys(const QMap& keys) +{ + //qDebug("loading %i keys", keys.count()); + + // load + QMap::const_iterator it = keys.constBegin(); + + while ( it != keys.constEnd() ) + { + if ( it.key() == "font" ) + { + QFont f = qvariant_cast(*it); + + cbFont->setCurrentFont(f); + spnFontSize->setValue(f.pointSize()); + + if ( m_direct ) + QDocument::setFont(f); + + lblSampleText->setFont(f); + + } else if ( it.key() == "tab_width" ) { + spnTabWidth->setValue(it->toInt()); + + if ( m_direct ) + on_spnTabWidth_valueChanged(it->toInt()); + } else if ( it.key() == "replace_tabs" ) { + chkReplaceTabs->setChecked(it->toBool()); + if ( m_direct ) + on_chkReplaceTabs_toggled(it->toBool()); + } else if ( it.key() == "remove_trailing" ) { + chkAutoRemoveTrailingWhitespace->setChecked(it->toBool()); + if ( m_direct ) + on_chkAutoRemoveTrailingWhitespace_toggled(it->toBool()); + } else if ( it.key() == "preserve_trailing_indent" ) { + chkPreserveTrailingIndent->setChecked(it->toBool()); + if ( m_direct ) + on_chkPreserveTrailingIndent_toggled(it->toBool()); + } else if ( it.key() == "show_tabs_in_text" ) { + chkShowTabsInText->setChecked(it->toBool()); + if ( m_direct ) + on_chkShowTabsInText_toggled(it->toBool()); + } else if ( it.key() == "show_leading_whitespace" ) { + chkShowLeadingWhitespace->setChecked(it->toBool()); + if ( m_direct ) + on_chkShowLeadingWhitespace_toggled(it->toBool()); + } else if ( it.key() == "show_trailing_whitespace" ) { + chkShowTrailingWhitespace->setChecked(it->toBool()); + if ( m_direct ) + on_chkShowTrailingWhitespace_toggled(it->toBool()); + } else if ( it.key() == "encoding" ) { + cbEncoding->setCurrentIndex(cbEncoding->findText(it->toString())); + if ( m_direct ) + on_cbEncoding_currentIndexChanged(it->toString()); + } else if ( it.key() == "line_endings" ) { + int le = it->toInt(); + + if ( le ) + { + chkDetectLE->setChecked(false); + cbLineEndings->setCurrentIndex(le - 1); + } else { + cbLineEndings->setCurrentIndex(0); + chkDetectLE->setChecked(true); + } + } else { + /* + qWarning("QEditConfig::loadKeys() fed with unsupported settings key : \"%s\" ", + qPrintable(it.key()) + ); + */ + } + + ++it; + } +} + +/*! + \brief Slot used to apply font size settings +*/ +void QEditConfig::on_spnFontSize_valueChanged(int size) +{ + QFont font = cbFont->currentFont(); + font.setPointSize(size); + + lblSampleText->setFont(font); + + if ( m_direct ) + { + QDocument::setFont(font); + emit keyChanged("font", font); + } +} + +/*! + \brief Slot used to apply font family settings +*/ +void QEditConfig::on_cbFont_currentFontChanged(QFont font) +{ + font.setPointSize(spnFontSize->value()); + lblSampleText->setFont(font); + + if ( m_direct ) + { + QDocument::setFont(font); + emit keyChanged("font", font); + } +} + +/*! + \brief Slot used to apply tab width settings +*/ +void QEditConfig::on_spnTabWidth_valueChanged(int n) +{ + if ( m_direct ) + { + QDocument::setTabStop(n); + emit keyChanged("tab_width", n); + } +} + +/*! + \brief Slot used to apply tabs replacement settings +*/ +void QEditConfig::on_chkReplaceTabs_toggled(bool y) +{ + if ( m_direct ) + { + // FIXME + foreach ( QEditor *e, QEditor::m_editors ) + { + e->setFlag(QEditor::ReplaceTabs, y); + } + emit keyChanged("replace_tabs", y); + } +} + +/*! + \brief Slot used to apply tabs display settings +*/ +void QEditConfig::on_chkShowTabsInText_toggled(bool y) +{ + if ( m_direct ) + { + if ( y ) + QDocument::setShowSpaces(QDocument::showSpaces() | QDocument::ShowTabs); + else + QDocument::setShowSpaces(QDocument::showSpaces() & ~QDocument::ShowTabs); + + emit keyChanged("show_tabs_in_text", y); + } +} + +/*! + \brief Slot used to apply trailing whitespace display settings +*/ +void QEditConfig::on_chkShowLeadingWhitespace_toggled(bool y) +{ + if ( m_direct ) + { + if ( y ) + QDocument::setShowSpaces(QDocument::showSpaces() | QDocument::ShowLeading); + else + QDocument::setShowSpaces(QDocument::showSpaces() & ~QDocument::ShowLeading); + + emit keyChanged("show_leading_whitespace", y); + } +} + +/*! + \brief Slot used to apply leading whitespace display settings +*/ +void QEditConfig::on_chkShowTrailingWhitespace_toggled(bool y) +{ + if ( m_direct ) + { + if ( y ) + QDocument::setShowSpaces(QDocument::showSpaces() | QDocument::ShowTrailing); + else + QDocument::setShowSpaces(QDocument::showSpaces() & ~QDocument::ShowTrailing); + + emit keyChanged("show_trailing_whitespace", y); + } +} + +/*! + \brief Slot used to apply encodings settings +*/ +void QEditConfig::on_cbEncoding_currentIndexChanged(const QString& name) +{ + if ( m_direct ) + { + QEditor::setDefaultCodec(name.toLatin1(), QEditor::UpdateAll); + emit keyChanged("encoding", name); + } +} + +/*! + \brief Slot used to apply line endings settings +*/ +void QEditConfig::on_cbLineEndings_currentIndexChanged(int idx) +{ + QDocument::LineEnding le = QDocument::LineEnding(idx + 1); + + if ( m_direct ) + { + QDocument::setDefaultLineEnding(le); + emit keyChanged("line_endings", (int)le); + } +} + +/*! + \brief Slot used to apply line endings auto detectionl settings +*/ +void QEditConfig::on_chkDetectLE_toggled(bool y) +{ + QDocument::LineEnding le = QDocument::Conservative; + + if ( !y ) + { + le = QDocument::LineEnding(cbLineEndings->currentIndex() + 1); + } + + if ( m_direct ) + { + QDocument::setDefaultLineEnding(le); + emit keyChanged("line_endings", (int)le); + } +} + +/*! + \brief Slot used to apply trailing space removal settings +*/ +void QEditConfig::on_chkAutoRemoveTrailingWhitespace_toggled(bool y) +{ + if ( m_direct ) + { + // FIXME + foreach ( QEditor *e, QEditor::m_editors ) + { + e->setFlag(QEditor::RemoveTrailing, y); + } + emit keyChanged("remove_trailing", y); + } +} + +/*! + \brief Slot used to indent preservation settings +*/ +void QEditConfig::on_chkPreserveTrailingIndent_toggled(bool y) +{ + if ( m_direct ) + { + // FIXME + foreach ( QEditor *e, QEditor::m_editors ) + { + e->setFlag(QEditor::PreserveTrailingIndent, y); + } + emit keyChanged("preserve_trailing_indent", y); + } +} + +/*! @} */ + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qeditconfig.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qeditconfig.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QEDIT_CONFIG_H_ +#define _QEDIT_CONFIG_H_ + +#include "qce-config.h" + +/*! + \file qeditconfig.h + \brief Definition of the QEditConfig widget + + \see QEditConfig +*/ + +#include "ui_editconfig.h" + +#include + +class QCE_EXPORT QEditConfig : public QWidget, private Ui::EditorConfig +{ + Q_OBJECT + + public: + QEditConfig(QWidget *w = 0); + + bool hasUnsavedChanges() const; + + bool applyImmediately() const; + + QMap dumpKeys() const; + + public slots: + void retranslate(); + + void apply(); + void cancel(); + void restore(); + + void loadKeys(const QMap& keys); + + void setApplyImmediately(bool y); + + signals: + void keyChanged(const QString& key, const QVariant& value); + + private slots: + void on_spnFontSize_valueChanged(int size); + void on_cbFont_currentFontChanged(QFont font); + + void on_spnTabWidth_valueChanged(int n); + + void on_chkReplaceTabs_toggled(bool y); + + void on_chkShowTabsInText_toggled(bool y); + void on_chkShowLeadingWhitespace_toggled(bool y); + void on_chkShowTrailingWhitespace_toggled(bool y); + + void on_cbEncoding_currentIndexChanged(const QString& name); + void on_cbLineEndings_currentIndexChanged(int idx); + void on_chkDetectLE_toggled(bool y); + void on_chkAutoRemoveTrailingWhitespace_toggled(bool y); + void on_chkPreserveTrailingIndent_toggled(bool y); + + private: + bool m_direct; +}; + +#endif // _QEDIT_CONFIG_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qfoldpanel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qfoldpanel.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qfoldpanel.h" + +/*! + \file qfoldpanel.cpp + \brief Implementation of the QFoldPanel class. + + \see QFoldPanel +*/ + +#include "qeditor.h" + +#include "qdocument.h" +#include "qdocumentline.h" + +#include "qlanguagedefinition.h" + +#include +#include +#include +#include +#include + +/*! + \ingroup widgets + @{ +*/ + +/*! + \class QFoldPanel + \brief A panel that draw fold indicators and provide fold/unfold actions to the user +*/ + +QCE_AUTO_REGISTER(QFoldPanel) + +/*! + \brief Constructor +*/ +QFoldPanel::QFoldPanel(QWidget *p) + : QPanel(p) +{ + setFixedWidth(12); +} + +/*! + \brief Empty destructor +*/ +QFoldPanel::~QFoldPanel() +{ + +} + +/*! + +*/ +QString QFoldPanel::type() const +{ + return "Fold indicators"; +} + +/*! + +*/ +void QFoldPanel::mousePressEvent(QMouseEvent *e) +{ + if ( !editor() || !editor()->languageDefinition() || (e->button() != Qt::LeftButton) ) + { + QPanel::mousePressEvent(e); + return; + } + + bool act = false; + QDocument *doc = editor()->document(); + QLanguageDefinition *def = editor()->languageDefinition(); + + for ( int i = 0; i < m_rects.count(); ++i ) + { + if ( !m_rects.at(i).contains(e->pos()) ) + continue; + + int ln = m_lines.at(i); + + QDocumentLine b = doc->line(ln); + + if ( b.hasFlag(QDocumentLine::CollapsedBlockStart) ) + def->expand(doc, ln); + else if ( def->blockFlags(doc, ln, 0) & QLanguageDefinition::Collapsible ) + def->collapse(doc, ln); + + act = true; + } + + if ( act ) + editor()->setFocus(); + else + QPanel::mousePressEvent(e); + +} + +/*! + +*/ +bool QFoldPanel::paint(QPainter *p, QEditor *e) +{ + QDocument *doc = editor()->document(); + QLanguageDefinition *def = e->languageDefinition(); + + if ( !def || !doc ) + { + return true; + } + + m_rects.clear(); + m_lines.clear(); + + bool bVisible = false; //, + // inCursorBlock = false; + + QDocumentLine block; + const QFontMetrics fm(doc->font()); + + int n, + pos, + depth = 0, + max = doc->lines(), + ls = fm.lineSpacing(), + pageBottom = e->viewport()->height(), + contentsY = e->verticalOffset(); + + pos = - contentsY; + + //qDebug("beg pos : %i", pos); + + for ( n = 0; n < max; ++n ) + { + if ( pos > pageBottom ) + break; + + block = doc->line(n); + + if ( block.isHidden() ) + { + continue; + } + + int len = ls * block.lineSpan(); + int flags = def->blockFlags(doc, n, depth); + short open = QCE_FOLD_OPEN_COUNT(flags); + short close = QCE_FOLD_CLOSE_COUNT(flags); + + bVisible = ((pos + len) >= 0); + + int oldDepth = depth; + + depth -= close; + + if ( depth < 0 ) + depth = 0; + + depth += open; + + if ( open ) + { + if ( flags & QLanguageDefinition::Collapsed ) + { + int bound = (ls - 8) / 2; + int mid = pos + len - ls / 6; + + // outermost block folded : none of the opening is actually opened + depth -= open; + + if ( bVisible ) + { + // draw icon + + if ( bound > 0 && oldDepth > 0 ) + { + p->drawLine(7, pos, 7, pos + bound); + } + + if ( close ) + { + p->drawLine(7, pos + 8 + bound, 7, mid); + p->drawLine(7, mid, 12, mid); + } + + m_lines << n; + m_rects << drawIcon(p, e, 3, pos + bound, true); + } + + int sub = open; + + //qDebug("%i : +%i", n, open); + + while ( sub > 0 && ((n + 1) < max) ) + { + ++n; + block = doc->line(n); + + if ( !block.isHidden() ) + { + if ( bVisible ) + p->drawLine(7, pos + 8 + bound, 7, pos + len); + + --n; + break; + } + + int sflags = def->blockFlags(doc, n, depth + 1); + short sopen = QCE_FOLD_OPEN_COUNT(sflags); + short sclose = QCE_FOLD_CLOSE_COUNT(sflags); + + sub -= sclose; + + if ( sub <= 0 ) + break; + + sub += sopen; + } + + depth += sub; + + if ( bVisible && depth > 0 ) + { + if ( close ) + p->drawLine(7, mid, 7, pos + len); + else + p->drawLine(7, pos + 8 + bound, 7, pos + len); + } + } else { + if ( bVisible ) + { + int bound = (ls - 8) / 2; + + if ( oldDepth > 0 && bound > 0 ) + p->drawLine(7, pos, 7, pos + bound); + + m_lines << n; + m_rects << drawIcon(p, e, 3, pos + bound, false); + + int mid = pos + len - ls / 6; + + if ( close ) + p->drawLine(7, mid, 12, mid); + + if ( bound > 0 ) + p->drawLine(7, pos + 8 + bound, 7, pos + len); + } + } + } else if ( (oldDepth > 0) && bVisible ) { + if ( close ) + { + int mid = pos + len - ls / 6; + + p->drawLine(7, pos, 7, mid); + p->drawLine(7, mid, 12, mid); + + if ( depth > 0 ) + p->drawLine(7, pos, 7, pos + len); + } else { + p->drawLine(7, pos, 7, pos + len); + } + } + + pos += len; + } + + return true; +} + +QRect QFoldPanel::drawIcon( QPainter *p, QEditor *, + int x, int y, bool toExpand) +{ + QRect symbolRect(x, y, 8, 8); + + //p->save(); + //p->setRenderHint(QPainter::Antialiasing); + p->drawRect(symbolRect); + //p->restore(); + + if ( toExpand ) + { + p->drawLine(x + 2, y + 4, x + 6, y + 4); + p->drawLine(x + 4, y + 2, x + 4, y + 6); + } else { + p->drawLine(x + 2, y + 4, x + 6, y + 4); + } + + return symbolRect; +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qfoldpanel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qfoldpanel.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QFOLD_PANEL_H_ +#define _QFOLD_PANEL_H_ + +#include "qpanel.h" + +/*! + \file qfoldpanel.h + \brief Definition of the QFoldPanel class. + + \see QFoldPanel +*/ + +class QDocumentLine; + +class QCE_EXPORT QFoldPanel : public QPanel +{ + Q_OBJECT + + public: + Q_PANEL(QFoldPanel, "Fold Panel") + + QFoldPanel(QWidget *p = 0); + virtual ~QFoldPanel(); + + virtual QString type() const; + + protected: + virtual void mousePressEvent(QMouseEvent *e); + virtual bool paint(QPainter *p, QEditor *e); + + QRect drawIcon( QPainter *p, QEditor *e, + int x, int y, bool expand); + + private: + QList m_rects; + QList m_lines; +}; + +#endif // _QFOLD_PANEL_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qformatconfig.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qformatconfig.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,491 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qformatconfig.h" + +/*! + \file qformatconfig.cpp + \brief Implementation of the QFormatConfig class. + + \see QFormatConfig +*/ + +#include "qformat.h" +#include "qformatscheme.h" + +#include "qeditor.h" +#include "qdocument.h" + +#include "qsimplecolorpicker.h" + +#include +#include +#include +#include + +/*! + \ingroup dialogs + @{ + + \class QFormatConfig + \brief A minimalistic, easy to embed, format settings widget. + +*/ + +QFormatConfig::QFormatConfig(QWidget *w) + : QWidget(w), m_autonomous(false), m_currentScheme(0) +{ + setupUi(this); + + m_frame->hide(); + + m_table->verticalHeader()->hide(); + + m_table->horizontalHeaderItem(1)->setIcon(QIcon(":/bold.png")); + m_table->horizontalHeaderItem(2)->setIcon(QIcon(":/italic.png")); + m_table->horizontalHeaderItem(3)->setIcon(QIcon(":/underline.png")); + //m_table->horizontalHeaderItem(4)->setIcon(QIcon(":/overline.png")); + m_table->horizontalHeaderItem(5)->setIcon(QIcon(":/strikeout.png")); + //m_table->horizontalHeaderItem(6)->setIcon(QIcon(":/waveUnderline.png")); + m_table->horizontalHeaderItem(7)->setIcon(QIcon(":/textcolor.png")); + m_table->horizontalHeaderItem(8)->setIcon(QIcon(":/fillcolor.png")); + m_table->horizontalHeaderItem(9)->setIcon(QIcon(":/strokecolor.png")); + + connect(m_table, SIGNAL( itemSelectionChanged() ), + m_table, SLOT ( clearSelection() ) ); + +} + +/*! + \brief run-time translation entry point +*/ +void QFormatConfig::retranslate() +{ + retranslateUi(this); +} + +/*! + \return Whether the format config widget is in "autonomous" mode +*/ +bool QFormatConfig::isAutonomous() const +{ + return m_autonomous; +} + +/*! + \brief Turn on "autonomous" mode + + When the format config widget is autonomous it will automatically + check for changes upon hide event and ask the user whether he wishes + to commit them. +*/ +void QFormatConfig::setAutonomous(bool y) +{ + m_autonomous = y; +} + +/*! + \brief Check whether there are unsaved in the widget +*/ +bool QFormatConfig::hasUnsavedChanges() const +{ + return modifiedFormats().count(); +} + +/*! + \return the list of format schemes this config widget "manages" +*/ +QList QFormatConfig::schemes() const +{ + return m_schemes; +} + +/*! + \brief Add a format scheme to the config widget + + Users will be able to edit that scheme (switching among schemes using + a combobox if several schemes are added to the widget) +*/ +void QFormatConfig::addScheme(const QString& name, QFormatScheme *scheme) +{ + m_schemes << scheme; + + m_selector->addItem(name); + + if ( m_schemes.count() > 1 && m_schemes.contains(m_currentScheme) ) + { + // show the scheme selector + m_frame->show(); + } + + if ( !m_currentScheme ) + setCurrentScheme(scheme); +} + +/*! + \brief Remove a scheme from the config widget + + \note No attempt is made to commit unsaved changes +*/ +void QFormatConfig::removeScheme(QFormatScheme *s) +{ + if ( m_currentScheme == s ) + { + m_currentScheme = 0; + } + + for ( int i = 0; i < m_schemes.count(); ++i ) + { + if ( m_schemes.at(i) == s ) + { + m_selector->removeItem(i); + m_schemes.removeAt(i); + } + } + + if ( m_schemes.count() <= 1 ) + { + // hide the scheme selector + m_frame->hide(); + } +} + +/*! + \brief Enforce the currently edited scheme + + \note It is possible to pass a scheme not previously added to the widget, + but it really isn't recommended. +*/ +void QFormatConfig::setCurrentScheme(QFormatScheme *s) +{ + int idx = m_schemes.indexOf(s); + + if ( idx != -1 ) + { + m_currentScheme = s; + + // update table widget + cancel(); + } else { + m_selector->setCurrentIndex(idx); + } +} + +/*! + \brief Apply changes made to the currently edited scheme, if any +*/ +void QFormatConfig::apply() +{ + if ( m_currentScheme ) + { + const int n = m_currentScheme->formatCount(); + + m_table->setRowCount(n); + + for ( int i = 0 ; i < n; ++i ) + { + QString fid = m_currentScheme->id(i); + QFormat& fmt = m_currentScheme->formatRef(i); + + QTableWidgetItem *item; + + item = m_table->item(i, 1); + fmt.weight = item->checkState() == Qt::Checked ? QFont::Bold : QFont::Normal; + + item = m_table->item(i, 2); + fmt.italic = item->checkState() == Qt::Checked; + + item = m_table->item(i, 3); + fmt.underline = item->checkState() == Qt::Checked; + + item = m_table->item(i, 4); + fmt.overline = item->checkState() == Qt::Checked; + + item = m_table->item(i, 5); + fmt.strikeout = item->checkState() == Qt::Checked; + + item = m_table->item(i, 6); + fmt.waveUnderline = item->checkState() == Qt::Checked; + + QSimpleColorPicker *cp; + + cp = qobject_cast(m_table->cellWidget(i, 7)); + if ( cp ) + fmt.foreground = cp->color(); + + cp = qobject_cast(m_table->cellWidget(i, 8)); + if ( cp ) + fmt.background = cp->color(); + + cp = qobject_cast(m_table->cellWidget(i, 9)); + if ( cp ) + fmt.linescolor = cp->color(); + + } + + // TODO : save scheme and update editors + + // this will only save schemes loaded from an existing file + m_currentScheme->save(); + + if ( m_autonomous ) + { + QList editors = QEditor::editors(); + + foreach ( QEditor *e, editors ) + { + if ( e->document()->formatScheme() == m_currentScheme ) + e->viewport()->update(); + } + } + } +} + +/*! + \brief Reset the subcontrols to reflect the data of the format scheme being edited + + The name can be a bit misleading at first, it has been chosen + because it directly maps to the effect a "cancel" button would + have on the widget +*/ +void QFormatConfig::cancel() +{ + m_table->clearContents(); + + if ( m_currentScheme ) + { + const int n = m_currentScheme->formatCount(); + + m_table->setRowCount(n); + + for ( int i = 0 ; i < n; ++i ) + { + QString fid = m_currentScheme->id(i); + const QFormat& fmt = m_currentScheme->formatRef(i); + + QTableWidgetItem *item; + + item = new QTableWidgetItem(fid); + item->setFlags(Qt::ItemIsEnabled); + m_table->setItem(i, 0, item); + + item = new QTableWidgetItem; + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + item->setCheckState(fmt.weight == QFont::Bold ? Qt::Checked : Qt::Unchecked); + item->setToolTip(tr("Bold")); + m_table->setItem(i, 1, item); + + item = new QTableWidgetItem; + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + item->setCheckState(fmt.italic ? Qt::Checked : Qt::Unchecked); + item->setToolTip(tr("Italic")); + m_table->setItem(i, 2, item); + + item = new QTableWidgetItem; + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + item->setCheckState(fmt.underline ? Qt::Checked : Qt::Unchecked); + item->setToolTip(tr("Underline")); + m_table->setItem(i, 3, item); + + item = new QTableWidgetItem; + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + item->setCheckState(fmt.overline ? Qt::Checked : Qt::Unchecked); + item->setToolTip(tr("Overline")); + m_table->setItem(i, 4, item); + + item = new QTableWidgetItem; + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + item->setCheckState(fmt.strikeout ? Qt::Checked : Qt::Unchecked); + item->setToolTip(tr("Strikeout")); + m_table->setItem(i, 5, item); + + item = new QTableWidgetItem; + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + item->setCheckState(fmt.waveUnderline ? Qt::Checked : Qt::Unchecked); + item->setToolTip(tr("Wave underline")); + m_table->setItem(i, 6, item); + + m_table->setCellWidget(i, 7, new QSimpleColorPicker(fmt.foreground)); + m_table->cellWidget(i, 7)->setToolTip(tr("Text color (aka foreground)")); + //m_table->cellWidget(i, 7)->setMaximumSize(22, 22); + + m_table->setCellWidget(i, 8, new QSimpleColorPicker(fmt.background)); + m_table->cellWidget(i, 8)->setToolTip(tr("Background color")); + //m_table->cellWidget(i, 8)->setMaximumSize(22, 22); + + m_table->setCellWidget(i, 9, new QSimpleColorPicker(fmt.linescolor)); + m_table->cellWidget(i, 9)->setToolTip(tr("Lines color (used by all lines formatting : underline, overline, ...)")); + //m_table->cellWidget(i, 9)->setMaximumSize(22, 22); + } + } + + m_table->resizeColumnsToContents(); +} + +/*! + \brief Restore default values for all subcontrols + + \note The widgets are changed but these changes are NOT applied. +*/ +void QFormatConfig::restore() +{ + // restoring what? this is only provided for API consistency and in case + // (very unlikely to ever happen) design changes make restore() a sensible + // thing to do on format schemes +} + +QList QFormatConfig::modifiedFormats() const +{ + QList hasModif; + + if ( m_currentScheme ) + { + const int n = m_currentScheme->formatCount(); + + m_table->setRowCount(n); + + for ( int i = 0 ; i < n; ++i ) + { + QString fid = m_currentScheme->id(i); + QFormat& fmt = m_currentScheme->formatRef(i); + + QTableWidgetItem *item; + + item = m_table->item(i, 1); + if ( fmt.weight != (item->checkState() == Qt::Checked ? QFont::Bold : QFont::Normal) ) + { + hasModif << i; + continue; + } + + item = m_table->item(i, 2); + if ( fmt.italic != (item->checkState() == Qt::Checked) ) + { + hasModif << i; + continue; + } + + item = m_table->item(i, 3); + if ( fmt.underline != (item->checkState() == Qt::Checked) ) + { + hasModif << i; + continue; + } + + item = m_table->item(i, 4); + if ( fmt.overline != (item->checkState() == Qt::Checked) ) + { + hasModif << i; + continue; + } + + item = m_table->item(i, 5); + if ( fmt.strikeout != (item->checkState() == Qt::Checked) ) + { + hasModif << i; + continue; + } + + item = m_table->item(i, 6); + if ( fmt.waveUnderline != (item->checkState() == Qt::Checked) ) + { + hasModif << i; + continue; + } + + QSimpleColorPicker *cp; + + cp = qobject_cast(m_table->cellWidget(i, 7)); + if ( cp && fmt.foreground != cp->color() ) + { + hasModif << i; + continue; + } + + cp = qobject_cast(m_table->cellWidget(i, 8)); + if ( cp && fmt.background != cp->color() ) + { + hasModif << i; + continue; + } + + cp = qobject_cast(m_table->cellWidget(i, 9)); + if ( cp && fmt.linescolor != cp->color() ) + { + hasModif << i; + continue; + } + } + } + + return hasModif; +} + +void QFormatConfig::hideEvent(QHideEvent *e) +{ + Q_UNUSED(e) + + if ( !m_autonomous ) + return; + + QList hasModif = modifiedFormats(); + + if ( hasModif.count() ) + { + // TODO : provide custom widget to allow user to select which items should be saved? + int ret = QMessageBox::warning( + 0, + tr("Unsaved changes"), + tr("There are unsaved changes in this format scheme.\nDo you want them to be saved?"), + QMessageBox::Save | QMessageBox::Discard + ); + + if ( ret == QMessageBox::Save ) + { + apply(); + } else { + cancel(); + } + } +} + +void QFormatConfig::on_m_selector_currentIndexChanged(int idx) +{ + QList hasModif = modifiedFormats(); + + if ( hasModif.count() ) + { + // TODO : provide custom widget to allow user to select which items should be saved? + int ret = QMessageBox::warning( + 0, + tr("Unsaved changes"), + tr("There are unsaved changes in this format scheme.\nDo you want them to be saved?"), + QMessageBox::Save | QMessageBox::Discard + ); + + if ( ret == QMessageBox::Save ) + { + apply(); + } else { + cancel(); + } + } + + m_currentScheme = idx >= 0 && idx < m_schemes.count() ? m_schemes.at(idx) : 0; + + cancel(); +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qformatconfig.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qformatconfig.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QFORMAT_CONFIG_H_ +#define _QFORMAT_CONFIG_H_ + +#include "qce-config.h" + +/*! + \file qformatconfig.h + \brief Definition of the QFormatConfig widget + + \see QFormatConfig +*/ + +#include + +#include "ui_formatconfig.h" + +class QFormatScheme; + +class QCE_EXPORT QFormatConfig : public QWidget, private Ui::FormatConfig +{ + Q_OBJECT + + public: + QFormatConfig(QWidget *w = 0); + + bool isAutonomous() const; + + bool hasUnsavedChanges() const; + + QList schemes() const; + + public slots: + void retranslate(); + + void apply(); + void cancel(); + void restore(); + + void setAutonomous(bool y); + + void addScheme(const QString& name, QFormatScheme *scheme); + void removeScheme(QFormatScheme *scheme); + + void setCurrentScheme(QFormatScheme *scheme); + + protected: + virtual void hideEvent(QHideEvent *e); + + private slots: + void on_m_selector_currentIndexChanged(int idx); + + private: + QList modifiedFormats() const; + + bool m_autonomous; + QFormatScheme *m_currentScheme; + QList m_schemes; +}; + +#endif // _QFORMAT_CONFIG_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qgotolinedialog.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qgotolinedialog.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qgotolinedialog.h" + +/*! + \file qgotolinedialog.cpp + \brief Implementation of the QGotoDialog class. + + \see QGotoDialog +*/ + +#include "qeditor.h" + +#include "qdocument.h" +#include "qdocumentline.h" +#include "qdocumentcursor.h" + +/*! + \ingroup dialogs + @{ + + \class QGotoLineDialog + \brief A Kate-like generic goto line dialog. + +*/ + +QGotoLineDialog::QGotoLineDialog(QWidget *w) + : QDialog(w) +{ + setupUi(this); +} + +void QGotoLineDialog::exec(QEditor *e) +{ + if ( !e ) + return; + + int ln = e->cursor().lineNumber() + 1, + max = e->document()->lines(); + + spinLine->setValue(ln); + spinLine->setMaximum(max); + + slideLine->setValue(ln); + slideLine->setMaximum(max); + + spinLine->selectAll(); + + if ( QDialog::exec() != QDialog::Accepted ) + return; + + QDocumentCursor c(e->document(), spinLine->value() - 1); + + if ( c.isNull() ) + return; + + //qDebug("going to line : %i [%i]", c.lineNumber(), spinLine->value()); + + e->setCursor(c); +} + +/*! @} */ + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qgotolinedialog.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qgotolinedialog.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QGOTO_LINE__DIALOG_H_ +#define _QGOTO_LINE__DIALOG_H_ + +#include "qce-config.h" + +/*! + \file qgotodialog.h + \brief Definition of the QGotoLineDialog class + + \see QGotoLineDialog +*/ + +#include "ui_gotolinedialog.h" + +#include + +class QEditor; + +class QCE_EXPORT QGotoLineDialog : public QDialog, private Ui::GotoDialog +{ + Q_OBJECT + + public: + QGotoLineDialog(QWidget *w = 0); + + public slots: + void exec(QEditor *e); +}; + +#endif // _QGOTO_LINE__DIALOG_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qgotolinepanel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qgotolinepanel.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qgotolinepanel.h" + +/*! + \file qgotolinepanel.cpp + \brief Implementation of the QGotoLinePanel class. + + \see QGotoLinePanel +*/ + +#include "qeditor.h" + +#include "qdocument.h" +#include "qdocumentline.h" +#include "qdocumentcursor.h" + +#include + +/*! + \ingroup widgets + @{ +*/ + +/*! + \class QGotoLinePanel + \brief A panel that provide inline goto line functionalities +*/ + +QCE_AUTO_REGISTER(QGotoLinePanel) + +/*! + \brief Constructor +*/ +QGotoLinePanel::QGotoLinePanel(QWidget *p) + : QPanel(p) +{ + setupUi(this); + setDefaultVisibility(false); + + bClose->setIcon(QPixmap(":/closeall.png")); +} + +/*! + \brief Empty destructor +*/ +QGotoLinePanel::~QGotoLinePanel() +{ + +} + +/*! + +*/ +QString QGotoLinePanel::type() const +{ + return "Goto"; +} + +/*! + \brief +*/ +void QGotoLinePanel::editorChange(QEditor *e) +{ + if ( editor() ) + { + disconnect( editor(), SIGNAL( cursorPositionChanged() ), + this , SLOT ( cursorPositionChanged() ) ); + + disconnect( editor()->document(), SIGNAL( lineCountChanged(int) ), + this , SLOT ( lineCountChanged(int) ) ); + } + + if ( e ) + { + connect(e , SIGNAL( cursorPositionChanged() ), + this, SLOT ( cursorPositionChanged() ) ); + + connect(e->document() , SIGNAL( lineCountChanged(int) ), + this , SLOT ( lineCountChanged(int) ) ); + + lineCountChanged(e->document()->lineCount()); + spLine->setValue(e->cursor().lineNumber() + 1); + slLine->setValue(e->cursor().lineNumber() + 1); + } +} + +bool QGotoLinePanel::forward(QMouseEvent *e) +{ + Q_UNUSED(e) + + /* + This panel does not need mouse events to be forwarded to the editor. + Even more, it requires them not to be forwarded... + */ + return false; +} + +void QGotoLinePanel::showEvent(QShowEvent *e) +{ + Q_UNUSED(e) + + spLine->setFocus(); + spLine->selectAll(); +} + +void QGotoLinePanel::keyPressEvent(QKeyEvent *e) +{ + if ( e->key() == Qt::Key_Escape ) + { + on_bClose_clicked(); + } else if ( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter ) { + on_bGo_clicked(); + } else { + QPanel::keyPressEvent(e); + } +} + +void QGotoLinePanel::on_bClose_clicked() +{ + hide(); + editor()->setFocus(); +} + +void QGotoLinePanel::on_bGo_clicked() +{ + editor()->setCursor(QDocumentCursor(editor()->document(), spLine->value() - 1)); +} + +void QGotoLinePanel::on_spLine_valueChanged(int v) +{ + if ( slLine->value() != v ) + slLine->setValue(v); +} + +void QGotoLinePanel::on_slLine_valueChanged(int v) +{ + if ( spLine->value() != v ) + spLine->setValue(v); +} + +void QGotoLinePanel::lineCountChanged(int n) +{ + spLine->setMaximum(n); + slLine->setMaximum(n); +} + +void QGotoLinePanel::cursorPositionChanged() +{ + spLine->setValue(editor()->cursor().lineNumber() + 1); + slLine->setValue(editor()->cursor().lineNumber() + 1); +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qgotolinepanel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qgotolinepanel.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QGOTO_LINE_PANEL_H_ +#define _QGOTO_LINE_PANEL_H_ + +#include "qpanel.h" + +/*! + \file qgotolinepanel.h + \brief Definition of the QGotoLinePanel class. + + \see QGotoLinePanel +*/ + +#include "ui_gotoline.h" + +class QCE_EXPORT QGotoLinePanel : public QPanel, private Ui::GotoLine +{ + Q_OBJECT + + public: + Q_PANEL(QGotoLinePanel, "Goto Line Panel") + + QGotoLinePanel(QWidget *p = 0); + virtual ~QGotoLinePanel(); + + virtual QString type() const; + + public slots: + + + protected: + virtual bool forward(QMouseEvent *e); + virtual void editorChange(QEditor *e); + virtual void showEvent(QShowEvent *e); + virtual void keyPressEvent(QKeyEvent *e); + + private slots: + void on_bClose_clicked(); + + void on_bGo_clicked(); + + void on_spLine_valueChanged(int v); + void on_slLine_valueChanged(int v); + + void lineCountChanged(int n); + void cursorPositionChanged(); +}; + +#endif // _QGOTO_LINE_PANEL_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qlinechangepanel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qlinechangepanel.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qlinechangepanel.h" + +/*! + \file qlinechangepanel.cpp + \brief Implementation of the QLineChangePanel class. +*/ + +#include "qeditor.h" + +#include "qdocument.h" +#include "qdocumentline.h" + +#include +#include +#include +#include +#include + +/*! + \ingroup widgets + @{ +*/ + +/*! + \class QLineMarkPanel + \brief A specific panel in charge of drawing line numbers of an editor + + \see QEditorInterface +*/ + +QCE_AUTO_REGISTER(QLineChangePanel) + +/*! + \brief Constructor +*/ +QLineChangePanel::QLineChangePanel(QWidget *p) + : QPanel(p) +{ + setFixedWidth(4); +} + +/*! + \brief Empty destructor +*/ +QLineChangePanel::~QLineChangePanel() +{ + +} + +/*! + +*/ +QString QLineChangePanel::type() const +{ + return "Line changes"; +} + +/*! + \internal +*/ +bool QLineChangePanel::paint(QPainter *p, QEditor *e) +{ + if ( !e || !e->document() ) + return true; + + const QFontMetrics fm( e->document()->font() ); + + int n, posY, + as = fm.ascent(), + ls = fm.lineSpacing(), + pageBottom = e->viewport()->height(), + contentsY = e->verticalOffset(); + + QString txt; + + QDocument *d = e->document(); + n = d->lineNumber(contentsY); + posY = 2 + d->y(n) - contentsY; + + for ( ; ; ++n ) + { + //qDebug("n = %i; pos = %i", n, posY); + QDocumentLine line = d->line(n); + + if ( line.isNull() || ((posY - as) > pageBottom) ) + break; + + if ( line.isHidden() ) + continue; + + int span = line.lineSpan(); + + if ( d->isLineModified(line) ) + { + p->fillRect(1, posY, 2, ls * span, Qt::red); + } else if ( d->hasLineEverBeenModified(line) ) { + p->fillRect(1, posY, 2, ls * span, Qt::green); + } + + posY += ls * span; + } + + return true; +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qlinechangepanel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qlinechangepanel.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QLINE_CHANGE_PANEL_H_ +#define _QLINE_CHANGE_PANEL_H_ + +/*! + \file qlinechangepanel.h + \brief Definition of the QLineChangePanel class. + + \see QLineChangePanel +*/ + +#include "qpanel.h" + +#include +#include + +class QDocumentLine; + +class QCE_EXPORT QLineChangePanel : public QPanel +{ + Q_OBJECT + + public: + Q_PANEL(QLineChangePanel, "Line Change Panel") + + QLineChangePanel(QWidget *p = 0); + virtual ~QLineChangePanel(); + + virtual QString type() const; + + protected: + virtual bool paint(QPainter *p, QEditor *e); + + private: + +}; + +#endif // _QLINE_CHANGE_PANEL_H_ + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qlinemarkpanel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qlinemarkpanel.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qlinemarkpanel.h" + +/*! + \file qlinemarkpanel.cpp + \brief Implementation of the QLineMarkPanel class. +*/ + +#include "qeditor.h" + +#include "qdocument.h" +#include "qdocumentline.h" + +#include "qlanguagedefinition.h" +#include "qlinemarksinfocenter.h" + +#include +#include +#include +#include +#include +#include + +/*! + \ingroup widgets + @{ +*/ + +/*! + \class QLineMarkPanel + \brief A specific panel in charge of drawing line marks of an editor +*/ + +QCE_AUTO_REGISTER(QLineMarkPanel) + +/*! + \brief Constructor +*/ +QLineMarkPanel::QLineMarkPanel(QWidget *p) + : QPanel(p) +{ + setFixedWidth(18); +} + +/*! + \brief Empty destructor +*/ +QLineMarkPanel::~QLineMarkPanel() +{ + +} + +/*! + +*/ +QString QLineMarkPanel::type() const +{ + return "Line marks"; +} + +/*! + \internal +*/ +bool QLineMarkPanel::paint(QPainter *p, QEditor *e) +{ + if ( !e || !e->document() ) + return true; + + m_rects.clear(); + m_lines.clear(); + QDocument *d = e->document(); + + int maxMarksPerLine = d->maxMarksPerLine(); + + setFixedWidth(maxMarksPerLine ? maxMarksPerLine * 16 + 2 : 18); + + const QFontMetrics fm( d->font() ); + + int n, posY, + as = fm.ascent(), + ls = fm.lineSpacing(), + pageBottom = e->viewport()->height(), + contentsY = e->verticalOffset(); + + QString txt; + const QFontMetrics sfm(fontMetrics()); + QLineMarksInfoCenter *mic = QLineMarksInfoCenter::instance(); + + n = d->lineNumber(contentsY); + posY = 2 + d->y(n) - contentsY; + + //qDebug("first = %i; last = %i", first, last); + //qDebug("beg pos : %i", posY); + //qDebug(""); + for ( ; ; ++n ) + { + //qDebug("n = %i; pos = %i", n, posY); + QDocumentLine line = d->line(n); + + if ( line.isNull() || ((posY - as) > pageBottom) ) + break; + + if ( line.isHidden() ) + continue; + + m_lines << n; + m_rects << QRect(0, posY, width(), ls); + + if ( maxMarksPerLine ) + { + int count = 1; + QList lm = line.marks(); + + foreach ( int id, lm ) + { + QPixmap pix = mic->markType(id).icon; + + if ( pix.isNull() ) + continue; + + int h = qMin(pix.height(), ls), + w = qMin(pix.width(), 16), + x = count, + y = posY + ( (ls - h) >> 1 ); + + p->drawPixmap(x, y, w, h, pix); + + count += 16; + } + } + + posY += ls * line.lineSpan(); + } + //qDebug(""); + + //setFixedWidth(sfm.width(txt) + 5); + return true; +} + +/*! + \internal +*/ +void QLineMarkPanel::mousePressEvent(QMouseEvent *e) +{ +// if ( !editor() || !editor()->document() || !editor()->marker() ) +// { +// return QPanel::mousePressEvent(e); +// } +// + QPanel::mousePressEvent(e); + e->accept(); +} + +/*! + \internal +*/ +void QLineMarkPanel::mouseReleaseEvent(QMouseEvent *e) +{ + if ( !editor() || !editor()->document() || !editor()->languageDefinition() ) + { + QPanel::mouseReleaseEvent(e); + return; + } + + //QMessageBox::warning(0, 0, "clik."); + + QDocumentLine l; + QLanguageDefinition *d = editor()->languageDefinition(); + const int id = QLineMarksInfoCenter::instance()->markTypeId(d->defaultLineMark()); + + if ( id < 0 ) + return; + + e->accept(); + + for ( int i = 0; i < m_rects.count(); ++i ) + { + if ( m_rects.at(i).contains(e->pos()) ) + { + l = editor()->document()->line(m_lines.at(i)); + l.toggleMark(id); + //m->toggleDefaultMark(l, -1); + + break; + } + } + + QPanel::mouseReleaseEvent(e); +} + +/*! + \internal +*/ +void QLineMarkPanel::contextMenuEvent(QContextMenuEvent *e) +{ + if ( !editor() || !editor()->document() ) + { + e->ignore(); + return; + } + + /* + QTextBlock b; + QMarker *m = editor()->marker(); + QTextDocument *d = editor()->document(); + + e->accept(); + + QHash::iterator i; + + for ( i = rects.begin(); i != rects.end(); i++ ) + { + b = d->findBlock(i.key()); + + if ( i->contains( e->pos() ) ) + return m->menu(b, e->globalPos()); + } + */ +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qlinemarkpanel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qlinemarkpanel.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QLINE_MARK_PANEL_H_ +#define _QLINE_MARK_PANEL_H_ + +/*! + \file qlinemarkpanel.h + \brief Definition of the QLineMarkPanel class. + + \see QLineMarkPanel +*/ + +#include "qpanel.h" + +#include +#include + +class QDocumentLine; + +class QCE_EXPORT QLineMarkPanel : public QPanel +{ + Q_OBJECT + + public: + Q_PANEL(QLineMarkPanel, "Line Mark Panel") + + QLineMarkPanel(QWidget *p = 0); + virtual ~QLineMarkPanel(); + + virtual QString type() const; + + protected: + virtual bool paint(QPainter *p, QEditor *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void contextMenuEvent(QContextMenuEvent *e); + + private: + QList m_rects; + QList m_lines; +}; + +#endif // _QLINE_MARK_PANEL_H_ + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qlinenumberpanel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qlinenumberpanel.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qlinenumberpanel.h" + +/*! + \file qlinenumberpanel.cpp + \brief Implementation of the QLineNumberPanel class + + \see QLineNumberPanel +*/ + +#include +#include + +#include "qeditor.h" +#include "qdocument.h" +#include "qdocument_p.h" +#include "qdocumentline.h" + + +/*! + \ingroup widgets + @{ +*/ + +/*! + \class QLineNumberPanel + \brief A specific panel in charge of drawing line numbers of an editor +*/ + +QCE_AUTO_REGISTER(QLineNumberPanel) + +/*! + \brief Constructor +*/ +QLineNumberPanel::QLineNumberPanel(QWidget *p) + : QPanel(p), m_verbose(false) +{ + setFixedWidth(20); +} + +/*! + \brief Empty destructor +*/ +QLineNumberPanel::~QLineNumberPanel() +{ + +} + +/*! + +*/ +QString QLineNumberPanel::type() const +{ + return "Line numbers"; +} + +/*! + +*/ +bool QLineNumberPanel::isVerboseMode() const +{ + return m_verbose; +} + +/*! + +*/ +void QLineNumberPanel::setVerboseMode(bool y) +{ + m_verbose = y; + update(); +} + +/*! + +*/ +void QLineNumberPanel::editorChange(QEditor *e) +{ + if ( editor() ) + { + disconnect( editor(), SIGNAL( cursorPositionChanged() ), + this , SLOT ( update() ) ); + + } + + if ( e ) + { + setFixedWidth(fontMetrics().width(QString::number(e->document()->lines())) + 5); + + connect(e , SIGNAL( cursorPositionChanged() ), + this, SLOT ( update() ) ); + + } +} + +/*! + +*/ +bool QLineNumberPanel::paint(QPainter *p, QEditor *e) +{ + /* + possible Unicode caracter for wrapping arrow : + 0x21B3 + 0x2937 + */ + + QFont f(font()); + f.setWeight(QFont::Bold); + const QFontMetrics sfm(f); + + #ifndef WIN32 + static const QChar wrappingArrow(0x2937); + const QFontMetrics specialSfm(sfm); + #else + // 0xC4 gives a decent wrapping arrow in Wingdings fonts, availables on all windows systems + // this is a hackish fallback to workaround Windows issues with Unicode... + static const QChar wrappingArrow(0xC4); + QFont specialFont(font()); + specialFont.setRawName("Wingdings"); + const QFontMetrics specialSfm(specialFont); + #endif + + const int max = e->document()->lines(); + const int panelWidth = sfm.width(QString::number(max)) + 5; + setFixedWidth(panelWidth); + + const QFontMetrics fm( e->document()->font() ); + + int n, posY, + as = fm.ascent(), + ls = fm.lineSpacing(), + pageBottom = e->viewport()->height(), + contentsY = e->verticalOffset(); + + QString txt; + QDocument *d = e->document(); + const int cursorLine = e->cursor().lineNumber(); + + n = d->lineNumber(contentsY); + posY = as + 2 + d->y(n) - contentsY; + + //qDebug("first = %i; last = %i", first, last); + //qDebug("beg pos : %i", posY); + + for ( ; ; ++n ) + { + //qDebug("n = %i; pos = %i", n, posY); + QDocumentLine line = d->line(n); + + if ( line.isNull() || ((posY - as) > pageBottom) ) + break; + + if ( line.isHidden() ) + continue; + + bool draw = true; + + if ( !m_verbose ) + { + draw = !((n + 1) % 10) || !n || line.marks().count(); + } + + txt = QString::number(n + 1); + + if ( n == cursorLine ) + { + draw = true; + + p->save(); + QFont f = p->font(); + f.setWeight(QFont::Bold); + + p->setFont(f); + } + + if ( draw ) + { + p->drawText(width() - 2 - sfm.width(txt), + posY, + txt); + + } else { + int yOff = posY - (as + 1) + ls / 2; + + if ( (n + 1) % 5 ) + p->drawPoint(width() - 5, yOff); + else + p->drawLine(width() - 7, yOff, width() - 2, yOff); + } + + if ( line.lineSpan() > 1 ) + { + #ifdef Q_OS_WIN32 + p->save(); + specialFont.setBold(n == cursorLine); //todo: only get bold on the current wrapped line + p->setFont(specialFont); + #endif + + for ( int i = 1; i < line.lineSpan(); ++i ) + { + // draw line wrapping indicators + //p->drawText(width() - 2 - sfm.width(wrappingArrow), posY + i * ls, wrappingArrow); + p->drawText(width() - 1 - specialSfm.width(wrappingArrow), posY + i * ls, wrappingArrow); + } + + #ifdef Q_OS_WIN32 + p->restore(); + #endif + } + + if ( n == cursorLine ) + { + p->restore(); + } + + posY += ls * line.lineSpan(); + } + + //p->setPen(Qt::DotLine); + //p->drawLine(width()-1, 0, width()-1, pageBottom); + + //setFixedWidth(sfm.width(txt) + 5); + return true; +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qlinenumberpanel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qlinenumberpanel.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QLINE_NUMBER_PANEL_H_ +#define _QLINE_NUMBER_PANEL_H_ + +/*! + \file qlinenumberpanel.h + \brief Definition of the QLineNumberPanel class + + \see QLineNumberPanel +*/ + +#include "qpanel.h" + +class QCE_EXPORT QLineNumberPanel : public QPanel +{ + Q_OBJECT + + public: + Q_PANEL(QLineNumberPanel, "Line Number Panel") + + QLineNumberPanel(QWidget *p = 0); + virtual ~QLineNumberPanel(); + + bool isVerboseMode() const; + + virtual QString type() const; + + public slots: + void setVerboseMode(bool y); + + protected: + virtual void editorChange(QEditor *e); + virtual bool paint(QPainter *p, QEditor *e); + + bool m_verbose; +}; + +#endif // _QLINE_NUMBER_PANEL_H_ + diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qpanel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qpanel.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,317 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qpanel.h" + +/*! + \file qpanel.cpp + \brief Implementation of the QPanel class + + \see QPanel +*/ + +#include "qeditor.h" +#include "qcodeedit.h" +#include "qpanellayout.h" + +#include +#include + +#include +#include + +#include + +#include +#include + +/*! + \ingroup widgets + @{ + + \class QPanel + \brief Helper class for panels displayed by QCodeEdit + +*/ + +QHash& QPanel::creators() +{ + static QHash _c; + return _c; +} + +QPanel* QPanel::panel(const QString& id, QWidget *p) +{ + if ( !creators().contains(id) ) + return 0; + + return creators().value(id)->panel(p); +} + +void QPanel::registerCreator(QPanelCreator *c) +{ + creators()[c->id()] = c; +} + +static int _panels = 0; + +/*! + \brief Constructor + + If the parent is a text editor, it is automatically connected to the panel +*/ +QPanel::QPanel(QWidget *p) + : QWidget(p), m_defaultVisibility(true), m_shownOnce(false) +{ + QEditor *e = qobject_cast(p); + + if ( e ) + attach(e); + + ++_panels; +} + +/*! + \brief Destructor +*/ +QPanel::~QPanel() +{ + --_panels; + + //if ( !_panels ) + // qDebug("Panels cleared."); + +} + +/*! +*/ +QEditor* QPanel::editor() +{ + return m_editor; +} + +/*! + \brief Connect the panel to a text editor +*/ +void QPanel::attach(QEditor *e) +{ + if ( m_editor ) + { + disconnect( m_editor->document(), + SIGNAL( formatsChanged() ), + this, + SLOT ( update() ) ); + + disconnect( m_editor->document(), + SIGNAL( contentsChanged() ), + this, + SLOT ( update() ) ); + + disconnect( m_editor->verticalScrollBar(), + SIGNAL( valueChanged(int) ), + this, + SLOT ( update() ) ); + + } + + editorChange(e); + + m_editor = e; + setParent(e); + + if ( m_editor ) + { + connect(m_editor->document(), + SIGNAL( formatsChanged() ), + this, + SLOT ( update() ) ); + + connect(m_editor->document(), + SIGNAL( contentsChanged() ), + this, + SLOT ( update() ) ); + + connect(m_editor->verticalScrollBar(), + SIGNAL( valueChanged(int) ), + this, + SLOT ( update() ) ); + + } +} + +/*! + \brief +*/ +bool QPanel::shallShow() const +{ + return m_shownOnce ? isHidden() : m_defaultVisibility; +} + +/*! + \brief +*/ +bool QPanel::defaultVisibility() const +{ + return m_defaultVisibility; +} + +/*! + \brief +*/ +void QPanel::setDefaultVisibility(bool on) +{ + m_defaultVisibility = on; +} + +/*! + \brief Callback + + Each time attach() is called, this function is called as well so that + the panel can fine tune its behaviour according to the editor monitored. + + \note the Default implementation does nothing... +*/ +void QPanel::editorChange(QEditor *) +{ + +} + +bool QPanel::forward(QMouseEvent *e) +{ + QPoint pos, globalPos = e->globalPos(), ref = editor()->viewport()->pos(); + + if ( editor()->viewport()->parentWidget() ) + ref = editor()->viewport()->parentWidget()->mapToGlobal(ref); + + globalPos.setX(qBound(ref.x(), globalPos.x(), ref.x() + editor()->width())); + globalPos.setY(qBound(ref.y(), globalPos.y(), ref.y() + editor()->height())); + + pos = editor()->viewport()->mapFromGlobal(globalPos); + + QMouseEvent fw( + e->type(), + pos, + globalPos, + e->button(), + e->buttons(), + e->modifiers() + ); + + bool ok = qApp->sendEvent(editor()->viewport(), &fw) && fw.isAccepted(); + + //qDebug("forwarding mouse event : (%i, %i) => %i", pos.x(), pos.y(), ok); + + return ok; +} + +/*! + \internal +*/ +void QPanel::mouseMoveEvent(QMouseEvent *e) +{ + if ( !editor() ) + return; + + if ( forward(e) ) + e->accept(); + else + QWidget::mouseMoveEvent(e); +} + +/*! + \internal +*/ +void QPanel::mousePressEvent(QMouseEvent *e) +{ + if ( !editor() ) + return; + + if ( forward(e) ) + e->accept(); + else + QWidget::mousePressEvent(e); +} + +/*! + \internal +*/ +void QPanel::mouseReleaseEvent(QMouseEvent *e) +{ + if ( !editor() ) + return; + + if ( forward(e) ) + e->accept(); + else + QWidget::mouseReleaseEvent(e); +} + +/*! + \internal +*/ +void QPanel::showEvent(QShowEvent *e) +{ + m_shownOnce = true; + + QWidget::showEvent(e); +} + +/*! + \internal +*/ +void QPanel::hideEvent(QHideEvent *e) +{ + QCodeEdit *ce = QCodeEdit::manager(editor()); + + if ( ce ) + ce->panelLayout()->update(); + + QWidget::hideEvent(e); +} + +/*! + \internal +*/ +void QPanel::paintEvent(QPaintEvent *e) +{ + if ( !m_editor || !m_editor->document() ) + { + e->ignore(); + return; + } + + e->accept(); + + QPainter p(this); + + if ( !paint(&p, m_editor) ) + QWidget::paintEvent(e); +} + +/*! + \internal +*/ +bool QPanel::paint(QPainter *, QEditor *) +{ + /* + qWarning("Bad panel implementation : " + "QPanel::paint(QPainter*, QEditor*)" + " is a stub that should not get called." + "\nCheck out the code of %s", qPrintable(type())); + */ + return false; +} + +/* @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qpanel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qpanel.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QPANEL_H_ +#define _QPANEL_H_ + +/*! + \file qpanel.h + \brief Definition of the QPanel class + + \see QPanel + + \defgroup widgets +*/ + +#include "qce-config.h" + +#include +#include +#include + +class QPainter; +class QPaintEvent; + +class QEditor; +class QPanelCreator; + +class QCE_EXPORT QPanel : public QWidget +{ + Q_OBJECT + + public: + QPanel(QWidget *p = 0); + virtual ~QPanel(); + + virtual QString id() const = 0; + virtual QString type() const = 0; + + QEditor* editor(); + void attach(QEditor *e); + + virtual bool shallShow() const; + + bool defaultVisibility() const; + void setDefaultVisibility(bool on); + + static QPanel* panel(const QString& id, QWidget *p = 0); + static void registerCreator(QPanelCreator *c); + + protected: + virtual bool forward(QMouseEvent *e); + + virtual void editorChange(QEditor *e); + + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + + virtual void showEvent(QShowEvent *e); + virtual void hideEvent(QHideEvent *e); + virtual void paintEvent(QPaintEvent *e); + virtual bool paint(QPainter *p, QEditor *e); + + private: + QPointer m_editor; + bool m_defaultVisibility, m_shownOnce; + static QHash& creators(); +}; + +class QPanelCreator +{ + public: + virtual ~QPanelCreator() {} + virtual QString id() const = 0; + virtual QPanel* panel(QWidget *p) = 0; +}; + +#define Q_PANEL(T, SID) \ + public: \ + class Creator : public QPanelCreator \ + { \ + public: \ + virtual QString id() const \ + { \ + return SID; \ + } \ + \ + virtual QPanel* panel(QWidget *p) \ + { \ + return new T(p); \ + } \ + \ + static QPanelCreator* instance() \ + { \ + static Creator global; \ + return &global; \ + } \ + \ + Creator() {} \ + virtual ~Creator() {} \ + }; \ + \ + QString id() const { return SID; } \ + \ + static void _register() \ + { \ + QPanel::registerCreator(Creator::instance()); \ + } \ + + +#define Q_PANEL_ID(T) \ + T::Creator::instance()->id() \ + + +#define Q_CREATE_PANEL(T) \ + QPanel::panel(Q_PANEL_ID(T)) \ + + +#endif // _QPANEL_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qsearchreplacepanel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qsearchreplacepanel.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,535 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qsearchreplacepanel.h" + +/*! + \file qsearchreplacepanel.cpp + \brief Implementation of the QSearchReplacePanel class. + + \see QSearchReplacePanel +*/ + +#include "qeditor.h" + +#include "qdocument.h" +#include "qdocumentline.h" +#include "qdocumentcursor.h" +#include "qdocumentsearch.h" + +#include +#include + +static QString escapeCpp(const QString& s, bool rep) +{ + if ( !rep ) + return s; + + QString es; + + for ( int i = 0; i < s.count(); ++i ) + { + if ( (s.at(i) == '\\') && ((i + 1) < s.count()) ) + { + QChar c = s.at(++i); + + if ( c == '\\' ) + es += '\\'; + else if ( c == 't' ) + es += '\t'; + else if ( c == 'n' ) + es += '\n'; + else if ( c == 'r' ) + es += '\r'; + else if ( c == '0' ) + es += '\0'; + + } else { + es += s.at(i); + } + } + + //qDebug("\"%s\" => \"%s\"", qPrintable(s), qPrintable(es)); + + return es; +} + +/*! + \ingroup widgets + @{ +*/ + +/*! + \class QSearchReplacePanel + \brief A panel that provide inline search/replace functionalities +*/ + +QCE_AUTO_REGISTER(QSearchReplacePanel) + +/*! + \brief Constructor +*/ +QSearchReplacePanel::QSearchReplacePanel(QWidget *p) + : QPanel(p), lastDirection(0), m_search(0) +{ + setupUi(this); + setDefaultVisibility(false); + + leFind->installEventFilter(this); + leReplace->installEventFilter(this); +} + +/*! + \brief Empty destructor +*/ +QSearchReplacePanel::~QSearchReplacePanel() +{ + if ( m_search ) + delete m_search; +} + +/*! + +*/ +QString QSearchReplacePanel::type() const +{ + return "Search"; +} + +/*! + \brief +*/ +void QSearchReplacePanel::editorChange(QEditor *e) +{ + if ( editor() ) + { + disconnect( editor(), SIGNAL( cursorPositionChanged() ), + this , SLOT ( cursorPositionChanged() ) ); + } + + if ( e ) + { + connect(e , SIGNAL( cursorPositionChanged() ), + this, SLOT ( cursorPositionChanged() ) ); + } +} + +bool QSearchReplacePanel::forward(QMouseEvent *e) +{ + Q_UNUSED(e) + + /* + This panel does not need mouse events to be forwarded to the editor. + Even more, it requires them not to be forwarded... + */ + return false; +} + +/*! + +*/ +void QSearchReplacePanel::display(int mode, bool replace) +{ + //qDebug("display(%i)", replace); + bool visible = true; + + if ( mode < 0 ) + visible = (replace != cbReplace->isChecked()) || isHidden(); + else + visible = mode; + + if ( visible ) + { + cbReplace->setChecked(replace); + //frameReplace->setVisible(replace); + leFind->setFocus(); + leFind->selectAll(); + //show(); + } + + setVisible(visible); + + if ( !visible ) + editor()->setFocus(); +} + +/*! + +*/ +void QSearchReplacePanel::find(int backward) +{ + if ( !m_search ) + { + if ( !isVisible() ) + { + display(1, false); + return; + } else { + init(); + } + + if ( backward != -1 ) + lastDirection = backward; + } + + bool replaceAll = cbReplace->isChecked() && cbReplaceAll->isChecked(); + + if ( backward == -1 ) + { + backward = lastDirection; + } else { + if ( lastDirection != backward && editor()->cursor().hasSelection() && !replaceAll ) + m_search->next(backward, false); //the first hit is already selected + + lastDirection = backward; + } + + m_search->next(backward, replaceAll); + + if ( !leFind->hasFocus() && !leReplace->hasFocus() ) + leFind->setFocus(); +} + +/*! + +*/ +void QSearchReplacePanel::hideEvent(QHideEvent *) +{ + /* + if ( m_search ) + delete m_search; + + m_search = 0; + */ +} + +bool QSearchReplacePanel::eventFilter(QObject *o, QEvent *e) +{ + int kc; + + if ( o == leFind || o == leReplace ) + { + switch ( e->type() ) + { + /* + case QEvent::FocusIn : + leFind->grabKeyboard(); + break; + + case QEvent::FocusOut : + leFind->releaseKeyboard(); + break; + */ + + case QEvent::KeyPress : + + kc = static_cast(e)->key(); + + if ( (kc == Qt::Key_Enter) || (kc == Qt::Key_Return) ) + { + //on_leFind_returnPressed(); + on_leFind_returnPressed(Qt::ShiftModifier & static_cast(e)->modifiers()); + return true; + } else if ( kc == Qt::Key_Escape) { + if ( cbReplace->isChecked() ) + display(1,false); + else + display(0,false); + return true; + } else if ( kc == Qt::Key_Tab || kc == Qt::Key_Backtab ) { + if ( cbReplace->isChecked() ) + { + if ( leFind->hasFocus() ) + leReplace->setFocus(); + else + leFind->setFocus(); + } + return true; + } + break; + + default: + break; + } + } + + return QWidget::eventFilter(o, e); +} + +void QSearchReplacePanel::on_leFind_textEdited(const QString& text) +{ + if ( cbReplace->isChecked() ) + { + // do not perfrom incremental search when replacing + + if ( m_search ) + m_search->setSearchText(text); + + leFind->setStyleSheet(QString()); + return; + } + + bool hadSearch = m_search; + QDocumentCursor cur = editor()->cursor(); + + if ( m_search ) + { + cur = m_search->cursor(); + + m_search->setSearchText(text); + + if ( cbCursor->isChecked() ) + { + QDocumentCursor c = cur; + c.setColumnNumber(qMin(c.anchorColumnNumber(), c.columnNumber())); + + m_search->setCursor(c); + } + } else { + // TODO : make incremental search optional + init(); + } + + if ( text.isEmpty() ) + { + leFind->setStyleSheet(QString()); + return; + } + + m_search->setOption(QDocumentSearch::Silent, true); + + find(0); + + m_search->setOption(QDocumentSearch::Silent, false); + + if ( m_search->cursor().isNull() ) + { + leFind->setStyleSheet("QLineEdit { background: red; color : white; }"); + + if ( hadSearch ) + { + m_search->setCursor(cur); + + // figure out whether other matches are availables + QDocumentSearch::Options opts = m_search->options(); + opts &= ~QDocumentSearch::HighlightAll; + opts |= QDocumentSearch::Silent; + + QDocumentSearch temp(editor(), text, opts); + temp.setOrigin(QDocumentCursor()); + temp.setScope(m_search->scope()); + temp.next(true); + + if ( temp.cursor().isValid() ) + { + // other match found from doc start + leFind->setStyleSheet("QLineEdit { background: yellow; color : black; }"); + m_search->setCursor(cur.document()->cursor(0,0)); + find(0); + } + } + } else { + leFind->setStyleSheet(QString()); + editor()->setCursor(m_search->cursor()); + } +} + +void QSearchReplacePanel::on_leFind_returnPressed(bool backward) +{ + leFind->setStyleSheet(QString()); + + if ( backward ) + find(1); + else + find(0); + +} + +void QSearchReplacePanel::on_leReplace_textEdited(const QString& text) +{ + if ( m_search ) + m_search->setReplaceText(text); + +} + +void QSearchReplacePanel::on_cbReplace_toggled(bool on) +{ + if ( m_search ) + m_search->setOption(QDocumentSearch::Replace, on); + + if ( leFind->isVisible() ) + leFind->setFocus(); +} + +void QSearchReplacePanel::on_cbWords_toggled(bool on) +{ + if ( m_search ) + m_search->setOption(QDocumentSearch::WholeWords, on); + + leFind->setFocus(); +} + +void QSearchReplacePanel::on_cbRegExp_toggled(bool on) +{ + if ( m_search ) + m_search->setOption(QDocumentSearch::RegExp, on); + + leFind->setFocus(); +} + +void QSearchReplacePanel::on_cbCase_toggled(bool on) +{ + if ( m_search ) + m_search->setOption(QDocumentSearch::CaseSensitive, on); + + leFind->setFocus(); +} + +void QSearchReplacePanel::on_cbCursor_toggled(bool on) +{ + if ( m_search ) + { + m_search->setOrigin(on ? editor()->cursor() : QDocumentCursor()); + + if ( cbHighlight->isChecked() ) + m_search->next(false); + } + + leFind->setFocus(); +} + +void QSearchReplacePanel::on_cbHighlight_toggled(bool on) +{ + if ( !m_search ) + init(); + + if ( m_search ) + { + m_search->setOption(QDocumentSearch::HighlightAll, on); + + if ( on && !m_search->indexedMatchCount() ) + m_search->next(false); + } + + leFind->setFocus(); +} + +void QSearchReplacePanel::on_cbSelection_toggled(bool on) +{ + if ( m_search ) + m_search->setScope(on ? editor()->cursor() : QDocumentCursor()); + + leFind->setFocus(); +} + +void QSearchReplacePanel::on_cbPrompt_toggled(bool on) +{ + if ( m_search ) + m_search->setOption(QDocumentSearch::Prompt, on); + + leFind->setFocus(); +} + +void QSearchReplacePanel::on_cbEscapeSeq_toggled(bool on) +{ + if ( m_search ) + m_search->setReplaceText(escapeCpp(leReplace->text(), on)); +} + +void QSearchReplacePanel::on_bNext_clicked() +{ + if ( !m_search ) + init(); + + leFind->setStyleSheet(QString()); + find(0); +} + +void QSearchReplacePanel::on_bPrevious_clicked() +{ + if ( !m_search ) + init(); + + leFind->setStyleSheet(QString()); + find(1); +} + +void QSearchReplacePanel::on_bRefresh_clicked() +{ + init(); +} + +void QSearchReplacePanel::init() +{ + if ( m_search ) + { + delete m_search; + m_search = 0; + } + + QDocumentSearch::Options opt; + + if ( cbRegExp->isChecked() ) + opt |= QDocumentSearch::RegExp; + + if ( cbCase->isChecked() ) + opt |= QDocumentSearch::CaseSensitive; + + if ( cbWords->isChecked() ) + opt |= QDocumentSearch::WholeWords; + + if ( cbHighlight->isChecked() && !cbReplace->isVisible() ) + opt |= QDocumentSearch::HighlightAll; + + if ( cbReplace->isChecked() && cbReplace->isVisible() ) + opt |= QDocumentSearch::Replace; + + if ( cbPrompt->isChecked() ) + opt |= QDocumentSearch::Prompt; + + m_search = new QDocumentSearch( editor(), + leFind->text(), + opt, + cbReplace->isChecked() + ? + escapeCpp(leReplace->text(), cbEscapeSeq->isChecked()) + : + QString() + ); + + + if ( cbCursor->isChecked() ) + m_search->setCursor(editor()->cursor()); + + if ( cbSelection->isChecked() ) + m_search->setScope(editor()->cursor()); + +} + +void QSearchReplacePanel::cursorPositionChanged() +{ + if ( m_search ) + { + if ( editor()->cursor() == m_search->cursor() || cbHighlight->isChecked() ) + return; + + if ( cbCursor->isChecked() ) + m_search->setOrigin(editor()->cursor()); + + m_search->setCursor(editor()->cursor()); + } +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qsearchreplacepanel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qsearchreplacepanel.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QSEARCH_REPLACE_PANEL_H_ +#define _QSEARCH_REPLACE_PANEL_H_ + +#include "qpanel.h" + +/*! + \file qsearchreplacepanel.h + \brief Definition of the QSearchReplacePanel class. + + \see QSearchReplacePanel +*/ + +#include "ui_searchreplace.h" + +class QDocumentLine; +class QDocumentSearch; + +class QCE_EXPORT QSearchReplacePanel : public QPanel, private Ui::SearchReplace +{ + Q_OBJECT + + public: + Q_PANEL(QSearchReplacePanel, "Search Replace Panel") + + QSearchReplacePanel(QWidget *p = 0); + virtual ~QSearchReplacePanel(); + + virtual QString type() const; + + public slots: + void display(int mode, bool replace); + + void find(int backward = -1); + + protected: + virtual bool forward(QMouseEvent *e); + virtual void editorChange(QEditor *e); + + virtual bool eventFilter(QObject *o, QEvent *e); + + virtual void hideEvent(QHideEvent *e); + + private slots: + void on_leFind_textEdited(const QString& text); + void on_leReplace_textEdited(const QString& text); + + void on_cbReplace_toggled(bool on); + + void on_cbCase_toggled(bool on); + void on_cbWords_toggled(bool on); + void on_cbRegExp_toggled(bool on); + void on_cbCursor_toggled(bool on); + void on_cbHighlight_toggled(bool on); + void on_cbSelection_toggled(bool on); + void on_cbPrompt_toggled(bool on); + void on_cbEscapeSeq_toggled(bool on); + + void on_bRefresh_clicked(); + + void on_bNext_clicked(); + void on_bPrevious_clicked(); + + void cursorPositionChanged(); + + private: + void init(); + void on_leFind_returnPressed(bool backward); + + int lastDirection; + QDocumentSearch *m_search; +}; + +#endif // _QSEARCH_REPLACE_PANEL_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qsimplecolorpicker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qsimplecolorpicker.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qsimplecolorpicker.h" + +/*! + \file qsimplecolorpicker.cpp + \brief Implementation of the QSimpleColorPicker class +*/ + +#include +#include +#include +#include +#include + +QSimpleColorPicker::QSimpleColorPicker(QWidget *w) + : QToolButton(w) +{ + setColor(QColor()); + + connect(this, SIGNAL( clicked() ), this, SLOT( clicked() ) ); +} + +QSimpleColorPicker::QSimpleColorPicker(const QColor& c, QWidget *w) + : QToolButton(w) +{ + setColor(c); + + connect(this, SIGNAL( clicked() ), this, SLOT( clicked() ) ); +} + +const QColor& QSimpleColorPicker::color() const +{ + return m_color; +} + +void QSimpleColorPicker::setColor(const QColor& c) +{ + m_color = c; + + updateIcon(size()); +} + +void QSimpleColorPicker::resizeEvent(QResizeEvent *e) +{ + updateIcon(e->size()); + + QToolButton::resizeEvent(e); +} + +void QSimpleColorPicker::contextMenuEvent(QContextMenuEvent *e) +{ + setColor(QColor()); + + e->accept(); + + QToolButton::contextMenuEvent(e); +} + +void QSimpleColorPicker::updateIcon(const QSize& sz) +{ + QPixmap px(sz.width() - 3, sz.height() - 3); + QPainter p(&px); + + if ( m_color.isValid() ) + { + p.fillRect(0, 0, px.width(), px.height(), m_color); + setIcon(QIcon(px)); + } else { + //p.fillRect(0, 0, px.width(), px.height(), palette().window()); + setIcon(QIcon()); + } +} + +void QSimpleColorPicker::clicked() +{ + QColor c = QColorDialog::getColor(m_color); + + if ( c.isValid() ) + { + setColor(c); + } +} diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qsimplecolorpicker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qsimplecolorpicker.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QSIMPLE_COLOR_PICKER_H_ +#define _QSIMPLE_COLOR_PICKER_H_ + +#include "qce-config.h" + +/*! + \file qsimplecolorpicker.h + \brief Definition of the QSimpleColorPicker class +*/ + +#include + +class QCE_EXPORT QSimpleColorPicker : public QToolButton +{ + Q_OBJECT + + Q_PROPERTY(QColor color READ color WRITE setColor) + + public: + QSimpleColorPicker(QWidget *w = 0); + QSimpleColorPicker(const QColor& c, QWidget *w = 0); + + const QColor& color() const; + + protected: + void resizeEvent(QResizeEvent *e); + void contextMenuEvent(QContextMenuEvent *e); + + public slots: + void setColor(const QColor& c); + + void updateIcon(const QSize& sz); + + private slots: + void clicked(); + + private: + QColor m_color; +}; + +#endif diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qstatuspanel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qstatuspanel.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qstatuspanel.h" + +/*! + \file qstatuspanel.cpp + \brief Implementation of the QStatusPanel class. + + \see QStatusPanel +*/ + +#include "qeditor.h" + +#include "qdocument.h" +#include "qdocumentline.h" +#include "qdocumentcursor.h" + +#include +#include +#include +#include +#include +#include + +/*! + \ingroup widgets + @{ +*/ + +/*! + \class QStatusPanel + \brief A panel that display some status informations +*/ + +QCE_AUTO_REGISTER(QStatusPanel) + +/*! + \brief Constructor +*/ +QStatusPanel::QStatusPanel(QWidget *p) + : QPanel(p) +{ + setFixedHeight(fontMetrics().lineSpacing() + 4); +} + +/*! + \brief Empty destructor +*/ +QStatusPanel::~QStatusPanel() +{ + +} + +/*! + +*/ +QString QStatusPanel::type() const +{ + return "Status"; +} + +/*! + +*/ +void QStatusPanel::editorChange(QEditor *e) +{ + if ( editor() ) + { + disconnect( editor(), SIGNAL( cursorPositionChanged() ), + this , SLOT ( update() ) ); + + } + + if ( e ) + { + connect(e , SIGNAL( cursorPositionChanged() ), + this, SLOT ( update() ) ); + + } +} + +/*! + +*/ +bool QStatusPanel::paint(QPainter *p, QEditor *e) +{ + //qDebug("drawing status panel... [%i, %i, %i, %i]", + // geometry().x(), + // geometry().y(), + // geometry().width(), + // geometry().height()); + static QPixmap _warn(":/warning.png"), _mod(":/save.png"); + + QString s; + int xpos = 10; + QDocumentCursor c = e->cursor(); + const QFontMetrics fm(fontMetrics()); + + const int ls = fm.lineSpacing(); + const int ascent = fm.ascent() + 3; + + s = tr("Line : %1 Visual column : %2 Text column : %3") + .arg(c.lineNumber() + 1) + .arg(c.visualColumnNumber()) + .arg(c.columnNumber()); + + p->drawText(xpos, ascent, s); + xpos += fm.width(s) + 10; + + int sz = qMin(height(), _mod.height()); + //int lastMod = d->lastModified().secsTo(QDateTime::currentDateTime()); + //QString timeDiff = tr("(%1 min %2 s ago)").arg(lastMod / 60).arg(lastMod % 60); + + //xpos += 10; + if ( e->isContentModified() ) + { + p->drawPixmap(xpos, (height() - sz) / 2, sz, sz, _mod); + //xpos += sz; + //xpos += 10; + //p->drawText(xpos, ascent, timeDiff); + } + xpos += sz + 10; + //xpos += fm.width(timeDiff); + //xpos += 20; + + s = editor()->flag(QEditor::Overwrite) ? tr("OVERWRITE") : tr("INSERT"); + p->drawText(xpos, ascent, s); + xpos += fm.width(s) + 10; + + m_conflictSpot = 0; + + if ( editor()->isInConflict() ) + { + s = tr("Conflict"); + int w = fm.width(s) + 30; + + if ( xpos + w + _warn.width() < width() ) + { + m_conflictSpot = width() - (w + _warn.width()); + p->drawText(width() - w + 15, ascent, s); + p->drawPixmap(m_conflictSpot, (ls - _warn.height()) / 2 + 2, _warn); + } else if ( xpos + _warn.width() < width() ) { + m_conflictSpot = width() - _warn.width(); + p->drawPixmap(m_conflictSpot, (ls - _warn.height()) / 2 + 2, _warn); + } + } + + setFixedHeight(ls + 4); + + QTimer::singleShot(1000, this, SLOT( update() ) ); + + return true; +} + +/*! + +*/ +void QStatusPanel::mousePressEvent(QMouseEvent *e) +{ + if ( !editor() || (e->button() != Qt::LeftButton) || !m_conflictSpot || e->x() < m_conflictSpot ) + { + editor()->setFocus(); + return; + } + + editor()->save(); +} + +/*! + +*/ +void QStatusPanel::mouseReleaseEvent(QMouseEvent *e) +{ + Q_UNUSED(e) + + editor()->setFocus(); +} + +/*! @} */ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/qstatuspanel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/qstatuspanel.h Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2006-2009 fullmetalcoder +** +** This file is part of the Edyuk project +** +** This file may be used under the terms of the GNU General Public License +** version 3 as published by the Free Software Foundation and appearing in the +** file GPL.txt included in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef _QSTATUS_PANEL_H_ +#define _QSTATUS_PANEL_H_ + +#include "qpanel.h" + +/*! + \file qstatuspanel.h + \brief Definition of the QStatusPanel class. + + \see QStatusPanel +*/ + +class QLabel; + +class QDocumentLine; + +class QCE_EXPORT QStatusPanel : public QPanel +{ + Q_OBJECT + + public: + Q_PANEL(QStatusPanel, "Status Panel") + + QStatusPanel(QWidget *p = 0); + virtual ~QStatusPanel(); + + virtual QString type() const; + + protected: + virtual void editorChange(QEditor *e); + virtual bool paint(QPainter *p, QEditor *e); + + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + + private: + int m_conflictSpot; +}; + +#endif // _QSTATUS_PANEL_H_ diff -r d5566981b3ac -r 4a718f51baa3 gui//qcodeedit-2.2.3/widgets/searchreplace.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui//qcodeedit-2.2.3/widgets/searchreplace.ui Mon Apr 11 18:44:09 2011 +0200 @@ -0,0 +1,514 @@ + + + SearchReplace + + + + 0 + 0 + 801 + 82 + + + + Form + + + + 0 + + + 2 + + + 2 + + + 2 + + + 1 + + + + + + 0 + 0 + + + + + 16 + 24 + + + + + 16777215 + 16777215 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 2 + + + 0 + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 20 + + + + Close search/replace panel + + + + + + + :/closeall.png:/closeall.png + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 20 + + + + Refresh search underlying context (as an attempt to correct search behavior) + + + + + + + :/reload.png:/reload.png + + + + + + + + 0 + 0 + + + + Find : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 120 + 22 + + + + + 120 + 16777215 + + + + Text or pattern to search for + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 20 + + + + Find next occurence + + + + + + + :/down.png:/down.png + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 20 + + + + Find previous occurence + + + + + + + :/up.png:/up.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Words + + + + + + + + 0 + 0 + + + + Cursor + + + true + + + + + + + + 0 + 0 + + + + Selection + + + + + + + + 0 + 0 + + + + Highlight all + + + + + + + + 0 + 0 + + + + Regexp + + + + + + + + 0 + 0 + + + + Case + + + true + + + + + + + + + + + 0 + 0 + + + + + 16 + 24 + + + + + 16777215 + 16777215 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 2 + + + 0 + + + + + + 0 + 0 + + + + Replace : + + + true + + + + + + + true + + + + 0 + 0 + + + + + 168 + 22 + + + + + 1200 + 16777215 + + + + Replacement text + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 139 + 24 + + + + + + + + + 0 + 0 + + + + Prompt on replace + + + true + + + + + + + + 0 + 0 + + + + Replace all + + + + + + + + 0 + 0 + + + + Escape sequences + + + + + + + + + + + + + + cbReplace + toggled(bool) + frameReplace + setVisible(bool) + + + 46 + 47 + + + 249 + 49 + + + + + bClose + clicked() + SearchReplace + hide() + + + 11 + 15 + + + 202 + -27 + + + + + diff -r d5566981b3ac -r 4a718f51baa3 gui//src/QTerminalWidget.cpp --- a/gui//src/QTerminalWidget.cpp Mon Apr 11 17:45:06 2011 +0200 +++ b/gui//src/QTerminalWidget.cpp Mon Apr 11 18:44:09 2011 +0200 @@ -168,7 +168,7 @@ m_impl->m_terminalDisplay->setScrollBarPosition((TerminalDisplay::ScrollBarPosition)pos); } -void QTerminalWidget::sendText(QString &text) +void QTerminalWidget::sendText(const QString &text) { m_impl->m_session->sendText(text); } diff -r d5566981b3ac -r 4a718f51baa3 gui//src/QTerminalWidget.h --- a/gui//src/QTerminalWidget.h Mon Apr 11 17:45:06 2011 +0200 +++ b/gui//src/QTerminalWidget.h Mon Apr 11 18:44:09 2011 +0200 @@ -69,7 +69,7 @@ void setScrollBarPosition(ScrollBarPosition); /** Send some text to the terminal. */ - void sendText(QString &text); + void sendText(const QString &text); signals: /** Emitted, when the current program has finished. */